1 /* 2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 * Copyright (c) 2016 by Delphix. All rights reserved. 5 */ 6 7 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 8 /* All Rights Reserved */ 9 10 11 /* 12 * Copyright (c) 1983 Regents of the University of California. 13 * All rights reserved. The Berkeley software License Agreement 14 * specifies the terms and conditions for redistribution. 15 */ 16 17 /* 18 * 19 * Synopsis: atq [ -c ] [ -n ] [ name ... ] 20 * 21 * 22 * Print the queue of files waiting to be executed. These files 23 * were created by using the "at" command and are located in the 24 * directory defined by ATDIR. 25 */ 26 27 #include <stdio.h> 28 #include <sys/types.h> 29 #include <sys/file.h> 30 #include <dirent.h> 31 #include <sys/stat.h> 32 #include <time.h> 33 #include <pwd.h> 34 #include <ctype.h> 35 #include <unistd.h> 36 #include <locale.h> 37 #include <errno.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include "cron.h" 41 42 extern char *errmsg(); 43 extern char *strchr(); 44 45 /* 46 * Months of the year 47 */ 48 static char *mthnames[12] = { 49 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", 50 "Aug", "Sep", "Oct", "Nov", "Dec", 51 }; 52 53 int numentries; /* number of entries in spooling area */ 54 int namewanted = 0; /* print jobs for a certain person */ 55 struct dirent **queue; /* the queue itself */ 56 57 #define INVALIDUSER "you are not a valid user (no entry in /etc/passwd)" 58 #define NOTALLOWED "you are not authorized to use at. Sorry." 59 60 static void atabortperror(char *msg); 61 static void atabort(char *msg); 62 static void aterror(char *msg); 63 static void atperror(char *msg); 64 static void usage(void); 65 static void printjobname(char *file); 66 static void printdate(char *filename); 67 static void printrank(int n); 68 static void printqueue(uid_t *uidlist, int nuids); 69 70 int 71 main(int argc, char **argv) 72 { 73 74 struct passwd *pp; /* password file entry pointer */ 75 struct passwd pr; 76 int i; 77 int cflag = 0; /* print in order of creation time */ 78 int nflag = 0; /* just print the number of jobs in */ 79 /* queue */ 80 extern int creation(); /* sort jobs by date of creation */ 81 extern int execution(); /* sort jobs by date of execution */ 82 int filewanted(); /* should file be included in queue? */ 83 int countfiles(); /* count the number of files in queue */ 84 /* for a given person */ 85 uid_t *uidlist = NULL; /* array of spec. owner ID(s) requ. */ 86 int argnum = 0; /* number of names passed as arg't */ 87 int badarg = 0; 88 char *c; 89 90 91 --argc, ++argv; 92 93 (void) setlocale(LC_ALL, ""); 94 pp = getpwuid(getuid()); 95 pr.pw_uid = pp->pw_uid; 96 pr.pw_name = pp->pw_name; 97 98 if (pp == NULL) 99 atabort(INVALIDUSER); 100 if (!allowed(pp->pw_name, ATALLOW, ATDENY)) 101 atabort(NOTALLOWED); 102 103 /* 104 * Interpret command line flags if they exist. 105 */ 106 while (argc > 0 && **argv == '-') { 107 (*argv)++; 108 while (**argv) { 109 switch (*(*argv)++) { 110 111 case 'c' : cflag++; 112 break; 113 114 case 'n' : nflag++; 115 break; 116 117 default : usage(); 118 119 } 120 } 121 --argc, ++argv; 122 } 123 124 /* 125 * If a certain name (or names) is requested, set a pointer to the 126 * beginning of the list. 127 */ 128 if (argc > 0) { 129 ++namewanted; 130 uidlist = (uid_t *)malloc(argc * sizeof (uid_t)); 131 if (uidlist == NULL) 132 atabortperror("can't allocate list of users"); 133 for (i = 0; i < argc; i++) { 134 if (cron_admin(pr.pw_name) || 135 strcmp(pr.pw_name, argv[i]) == 0) { 136 if ((pp = getpwnam(argv[i])) == NULL) { 137 (void) fprintf(stderr, 138 "atq: No such user %s\n", argv[i]); 139 exit(1); 140 } 141 uidlist[argnum] = pp->pw_uid; 142 argnum++; 143 } 144 else 145 badarg++; 146 } 147 if (badarg) 148 if (argnum) 149 printf("Printing queue information only " 150 "for %s:\n", pr.pw_name); 151 else { 152 printf("atq: Non-priviledged user cannot " 153 "request information regarding other " 154 "users\n"); 155 exit(1); 156 } 157 } else if (!cron_admin(pr.pw_name)) { 158 /* no argument specified and the invoker is not root */ 159 ++namewanted; 160 argnum = 1; 161 if ((uidlist = (uid_t *)malloc(sizeof (uid_t))) == NULL) 162 atabortperror("can't allocate list of users"); 163 *uidlist = pr.pw_uid; 164 } 165 166 /* 167 * Move to the spooling area and scan the directory, placing the 168 * files in the queue structure. The queue comes back sorted by 169 * execution time or creation time. 170 */ 171 if (chdir(ATDIR) == -1) 172 atabortperror(ATDIR); 173 if ((numentries = scandir(".", &queue, filewanted, 174 (cflag) ? creation : execution)) < 0) 175 atabortperror(ATDIR); 176 177 178 /* 179 * Either print a message stating: 180 * 181 * 1) that the spooling area is empty. 182 * 2) the number of jobs in the spooling area. 183 * 3) the number of jobs in the spooling area belonging to 184 * a certain person. 185 * 4) that the person requested doesn't have any files in the 186 * spooling area. 187 * 188 * or send the queue off to "printqueue" for printing. 189 * 190 * This whole process might seem a bit elaborate, but it's worthwhile 191 * to print some informative messages for the user. 192 * 193 */ 194 if ((numentries == 0) && (!nflag)) { 195 printf("no files in queue.\n"); 196 exit(0); 197 } 198 if (nflag) { 199 printf("%d\n", (namewanted) ? 200 countfiles(uidlist, argnum) : numentries); 201 exit(0); 202 } 203 if ((namewanted) && (countfiles(uidlist, argnum) == 0)) { 204 if (argnum == 1) 205 if (argnum != argc) c = pr.pw_name; 206 else c = *argv; 207 printf("no files for %s.\n", (argnum == 1) ? 208 c : "specified users"); 209 exit(0); 210 } 211 printqueue(uidlist, argnum); 212 return (0); 213 } 214 215 /* 216 * Count the number of jobs in the spooling area owned by a certain person(s). 217 */ 218 int 219 countfiles(uid_t *uidlist, int nuids) 220 { 221 int i, j; /* for loop indices */ 222 int entryfound; /* found file owned by users */ 223 int numfiles = 0; /* number of files owned by a */ 224 /* certain person(s) */ 225 uid_t *ptr; /* scratch pointer */ 226 struct stat stbuf; /* buffer for file stats */ 227 228 229 /* 230 * For each file in the queue, see if the user(s) own the file. We 231 * have to use "entryfound" (rather than simply incrementing "numfiles") 232 * so that if a person's name appears twice on the command line we 233 * don't double the number of files owned by that user. 234 */ 235 for (i = 0; i < numentries; i++) { 236 if ((stat(queue[i]->d_name, &stbuf)) < 0) { 237 continue; 238 } 239 ptr = uidlist; 240 entryfound = 0; 241 242 for (j = 0; j < nuids; j++) { 243 if (*ptr == stbuf.st_uid) 244 ++entryfound; 245 ++ptr; 246 } 247 if (entryfound) 248 ++numfiles; 249 } 250 return (numfiles); 251 } 252 253 /* 254 * Print the queue. If only jobs belonging to a certain person(s) are requested, 255 * only print jobs that belong to that person(s). 256 */ 257 static void 258 printqueue(uid_t *uidlist, int nuids) 259 { 260 int i, j; /* for loop indices */ 261 int rank; /* rank of a job */ 262 int entryfound; /* found file owned by users */ 263 char *getname(); 264 uid_t *ptr; /* scratch pointer */ 265 struct stat stbuf; /* buffer for file stats */ 266 char curqueue; /* queue of current job */ 267 char lastqueue; /* queue of previous job */ 268 269 /* 270 * Print the header for the queue. 271 */ 272 printf(" Rank Execution Date Owner Job " 273 "Queue Job Name\n"); 274 275 /* 276 * Print the queue. If a certain name(s) was requested, print only jobs 277 * belonging to that person(s), otherwise print the entire queue. 278 * Once again, we have to use "entryfound" (rather than simply 279 * comparing each command line argument) so that if a person's name 280 * appears twice we don't print each of their files twice. 281 * 282 * 283 * "printrank", "printdate", and "printjobname" all take existing 284 * data and display it in a friendly manner. 285 * 286 */ 287 lastqueue = '\0'; 288 for (i = 0; i < numentries; i++) { 289 if ((stat(queue[i]->d_name, &stbuf)) < 0) { 290 continue; 291 } 292 curqueue = *(strchr(queue[i]->d_name, '.') + 1); 293 if (curqueue != lastqueue) { 294 rank = 1; 295 lastqueue = curqueue; 296 } 297 if (namewanted) { 298 ptr = uidlist; 299 entryfound = 0; 300 301 for (j = 0; j < nuids; j++) { 302 if (*ptr == stbuf.st_uid) 303 ++entryfound; 304 ++ptr; 305 } 306 if (!entryfound) 307 continue; 308 } 309 printrank(rank++); 310 printdate(queue[i]->d_name); 311 printf("%-10s ", getname(stbuf.st_uid)); 312 printf("%-14s ", queue[i]->d_name); 313 printf(" %c", curqueue); 314 printjobname(queue[i]->d_name); 315 } 316 ++ptr; 317 } 318 319 /* 320 * Get the uid of a person using their login name. Return -1 if no 321 * such account name exists. 322 */ 323 uid_t 324 getid(char *name) 325 { 326 327 struct passwd *pwdinfo; /* password info structure */ 328 329 330 if ((pwdinfo = getpwnam(name)) == 0) 331 return ((uid_t)-1); 332 333 return (pwdinfo->pw_uid); 334 } 335 336 /* 337 * Get the full login name of a person using their user id. 338 */ 339 char * 340 getname(uid_t uid) 341 { 342 struct passwd *pwdinfo; /* password info structure */ 343 344 345 if ((pwdinfo = getpwuid(uid)) == 0) 346 return ("???"); 347 return (pwdinfo->pw_name); 348 } 349 350 /* 351 * Print the rank of a job. (I've got to admit it, I stole it from "lpq") 352 */ 353 static void 354 printrank(int n) 355 { 356 static char *r[] = { 357 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 358 }; 359 360 if ((n/10) == 1) 361 printf("%3d%-5s", n, "th"); 362 else 363 printf("%3d%-5s", n, r[n%10]); 364 } 365 366 /* 367 * Print the date that a job is to be executed. This takes some manipulation 368 * of the file name. 369 */ 370 static void 371 printdate(char *filename) 372 { 373 time_t jobdate; 374 struct tm *unpackeddate; 375 char date[18]; /* reformatted execution date */ 376 377 /* 378 * Convert the file name to a date. 379 */ 380 jobdate = num(&filename); 381 unpackeddate = localtime(&jobdate); 382 383 /* years since 1900 + base century 1900 */ 384 unpackeddate->tm_year += 1900; 385 /* 386 * Format the execution date of a job. 387 */ 388 sprintf(date, "%3s %2d, %4d %02d:%02d", mthnames[unpackeddate->tm_mon], 389 unpackeddate->tm_mday, unpackeddate->tm_year, 390 unpackeddate->tm_hour, unpackeddate->tm_min); 391 392 /* 393 * Print the date the job will be executed. 394 */ 395 printf("%-21.18s", date); 396 } 397 398 /* 399 * Print a job name. If the old "at" has been used to create the spoolfile, 400 * the three line header that the new version of "at" puts in the spoolfile. 401 * Thus, we just print "???". 402 */ 403 static void 404 printjobname(char *file) 405 { 406 char *ptr; /* scratch pointer */ 407 char jobname[28]; /* the job name */ 408 FILE *filename; /* job file in spooling area */ 409 410 /* 411 * Open the job file and grab the third line. 412 */ 413 printf(" "); 414 415 if ((filename = fopen(file, "r")) == NULL) { 416 printf("%.27s\n", "???"); 417 (void) fprintf(stderr, "atq: Can't open job file %s: %s\n", 418 file, errmsg(errno)); 419 return; 420 } 421 /* 422 * Skip over the first and second lines. 423 */ 424 fscanf(filename, "%*[^\n]\n"); 425 426 /* 427 * Now get the job name. 428 */ 429 if (fscanf(filename, ": jobname: %27s%*[^\n]\n", jobname) != 1) { 430 printf("%.27s\n", "???"); 431 fclose(filename); 432 return; 433 } 434 fclose(filename); 435 436 /* 437 * Put a pointer at the begining of the line and remove the basename 438 * from the job file. 439 */ 440 ptr = jobname; 441 if ((ptr = (char *)strrchr(jobname, '/')) != 0) 442 ++ptr; 443 else 444 ptr = jobname; 445 446 if (strlen(ptr) > 23) 447 printf("%.23s ...\n", ptr); 448 else 449 printf("%.27s\n", ptr); 450 } 451 452 453 454 /* 455 * Sort files by queue, time of creation, and sequence. (used by "scandir") 456 */ 457 int 458 creation(struct dirent **d1, struct dirent **d2) 459 { 460 char *p1, *p2; 461 int i; 462 struct stat stbuf1, stbuf2; 463 int seq1, seq2; 464 465 if ((p1 = strchr((*d1)->d_name, '.')) == NULL) 466 return (0); 467 if ((p2 = strchr((*d2)->d_name, '.')) == NULL) 468 return (0); 469 p1++; 470 p2++; 471 if ((i = *p1++ - *p2++) != 0) 472 return (i); 473 474 if (stat((*d1)->d_name, &stbuf1) < 0) 475 return (0); 476 477 if (stat((*d2)->d_name, &stbuf2) < 0) 478 return (0); 479 480 if (stbuf1.st_ctime < stbuf2.st_ctime) 481 return (-1); 482 else if (stbuf1.st_ctime > stbuf2.st_ctime) 483 return (1); 484 p1++; 485 p2++; 486 seq1 = atoi(p1); 487 seq2 = atoi(p2); 488 return (seq1 - seq2); 489 } 490 491 /* 492 * Sort files by queue, time of execution, and sequence. (used by "scandir") 493 */ 494 int 495 execution(struct dirent **d1, struct dirent **d2) 496 { 497 char *p1, *p2; 498 int i; 499 char *name1, *name2; 500 time_t time1, time2; 501 int seq1, seq2; 502 503 name1 = (*d1)->d_name; 504 name2 = (*d2)->d_name; 505 if ((p1 = strchr(name1, '.')) == NULL) 506 return (1); 507 if ((p2 = strchr(name2, '.')) == NULL) 508 return (1); 509 p1++; 510 p2++; 511 if ((i = *p1++ - *p2++) != 0) 512 return (i); 513 514 time1 = num(&name1); 515 time2 = num(&name2); 516 517 if (time1 < time2) 518 return (-1); 519 else if (time1 > time2) 520 return (1); 521 p1++; 522 p2++; 523 seq1 = atoi(p1); 524 seq2 = atoi(p2); 525 return (seq1 - seq2); 526 } 527 528 529 /* 530 * Print usage info and exit. 531 */ 532 static void 533 usage(void) 534 { 535 fprintf(stderr, "usage: atq [-c] [-n] [name ...]\n"); 536 exit(1); 537 } 538 539 static void 540 aterror(char *msg) 541 { 542 fprintf(stderr, "atq: %s\n", msg); 543 } 544 545 static void 546 atperror(char *msg) 547 { 548 fprintf(stderr, "atq: %s: %s\n", msg, errmsg(errno)); 549 } 550 551 static void 552 atabort(char *msg) 553 { 554 aterror(msg); 555 exit(1); 556 } 557 558 static void 559 atabortperror(char *msg) 560 { 561 atperror(msg); 562 exit(1); 563 } 564