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