1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 10 /* 11 * Copyright (c) 1983 Regents of the University of California. 12 * All rights reserved. The Berkeley software License Agreement 13 * specifies the terms and conditions for redistribution. 14 */ 15 16 #pragma ident "%Z%%M% %I% %E% SMI" 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 static void atabortperror(char *msg); 60 static void atabort(char *msg); 61 static void aterror(char *msg); 62 static void atperror(char *msg); 63 static void usage(void); 64 static void printjobname(char *file); 65 static void printdate(char *filename); 66 static void printrank(int n); 67 static void printqueue(uid_t *uidlist, int nuids); 68 69 int 70 main(int argc, char **argv) 71 { 72 73 struct passwd *pp; /* password file entry pointer */ 74 struct passwd pr; 75 int i; 76 int cflag = 0; /* print in order of creation time */ 77 int nflag = 0; /* just print the number of jobs in */ 78 /* queue */ 79 extern int creation(); /* sort jobs by date of creation */ 80 extern int execution(); /* sort jobs by date of execution */ 81 int filewanted(); /* should file be included in queue? */ 82 int countfiles(); /* count the number of files in queue */ 83 /* for a given person */ 84 uid_t *uidlist = NULL; /* array of spec. owner ID(s) requ. */ 85 int argnum = 0; /* number of names passed as arg't */ 86 int badarg = 0; 87 char *c; 88 89 90 --argc, ++argv; 91 92 (void) setlocale(LC_ALL, ""); 93 pp = getpwuid(getuid()); 94 pr.pw_uid = pp->pw_uid; 95 pr.pw_name = pp->pw_name; 96 97 if (pp == NULL) 98 atabort(INVALIDUSER); 99 if (!allowed(pp->pw_name, ATALLOW, ATDENY)) 100 atabort(NOTALLOWED); 101 102 /* 103 * Interpret command line flags if they exist. 104 */ 105 while (argc > 0 && **argv == '-') { 106 (*argv)++; 107 while (**argv) { 108 switch (*(*argv)++) { 109 110 case 'c' : cflag++; 111 break; 112 113 case 'n' : nflag++; 114 break; 115 116 default : usage(); 117 118 } 119 } 120 --argc, ++argv; 121 } 122 123 /* 124 * If a certain name (or names) is requested, set a pointer to the 125 * beginning of the list. 126 */ 127 if (argc > 0) { 128 ++namewanted; 129 uidlist = (uid_t *)malloc(argc * sizeof (uid_t)); 130 if (uidlist == NULL) 131 atabortperror("can't allocate list of users"); 132 for (i = 0; i < argc; i++) { 133 if ((chkauthattr(CRONADMIN_AUTH, pr.pw_name)) || 134 strcmp(pr.pw_name, argv[i]) == 0) { 135 if ((pp = getpwnam(argv[i])) == NULL) { 136 (void) fprintf(stderr, 137 "atq: No such user %s\n", argv[i]); 138 exit(1); 139 } 140 uidlist[argnum] = pp->pw_uid; 141 argnum++; 142 } 143 else 144 badarg++; 145 } 146 if (badarg) 147 if (argnum) 148 printf("Printing queue information only " 149 "for %s:\n", pr.pw_name); 150 else { 151 printf("atq: Non-priviledged user cannot " 152 "request information regarding other " 153 "users\n"); 154 exit(1); 155 } 156 } else if (!chkauthattr(CRONADMIN_AUTH, pr.pw_name)) { 157 /* no argument specified and the invoker is not root */ 158 ++namewanted; 159 argnum = 1; 160 if ((uidlist = (uid_t *)malloc(sizeof (uid_t))) == NULL) 161 atabortperror("can't allocate list of users"); 162 *uidlist = pr.pw_uid; 163 } 164 165 /* 166 * Move to the spooling area and scan the directory, placing the 167 * files in the queue structure. The queue comes back sorted by 168 * execution time or creation time. 169 */ 170 if (chdir(ATDIR) == -1) 171 atabortperror(ATDIR); 172 if ((numentries = ascandir(".", &queue, filewanted, 173 (cflag) ? creation : execution)) < 0) 174 atabortperror(ATDIR); 175 176 177 /* 178 * Either print a message stating: 179 * 180 * 1) that the spooling area is empty. 181 * 2) the number of jobs in the spooling area. 182 * 3) the number of jobs in the spooling area belonging to 183 * a certain person. 184 * 4) that the person requested doesn't have any files in the 185 * spooling area. 186 * 187 * or send the queue off to "printqueue" for printing. 188 * 189 * This whole process might seem a bit elaborate, but it's worthwhile 190 * to print some informative messages for the user. 191 * 192 */ 193 if ((numentries == 0) && (!nflag)) { 194 printf("no files in queue.\n"); 195 exit(0); 196 } 197 if (nflag) { 198 printf("%d\n", (namewanted) ? 199 countfiles(uidlist, argnum) : numentries); 200 exit(0); 201 } 202 if ((namewanted) && (countfiles(uidlist, argnum) == 0)) { 203 if (argnum == 1) 204 if (argnum != argc) c = pr.pw_name; 205 else c = *argv; 206 printf("no files for %s.\n", (argnum == 1) ? 207 c : "specified users"); 208 exit(0); 209 } 210 printqueue(uidlist, argnum); 211 return (0); 212 } 213 214 /* 215 * Count the number of jobs in the spooling area owned by a certain person(s). 216 */ 217 int 218 countfiles(uid_t *uidlist, int nuids) 219 { 220 int i, j; /* for loop indices */ 221 int entryfound; /* found file owned by users */ 222 int numfiles = 0; /* number of files owned by a */ 223 /* certain person(s) */ 224 uid_t *ptr; /* scratch pointer */ 225 struct stat stbuf; /* buffer for file stats */ 226 227 228 /* 229 * For each file in the queue, see if the user(s) own the file. We 230 * have to use "entryfound" (rather than simply incrementing "numfiles") 231 * so that if a person's name appears twice on the command line we 232 * don't double the number of files owned by him/her. 233 */ 234 for (i = 0; i < numentries; i++) { 235 if ((stat(queue[i]->d_name, &stbuf)) < 0) { 236 continue; 237 } 238 ptr = uidlist; 239 entryfound = 0; 240 241 for (j = 0; j < nuids; j++) { 242 if (*ptr == stbuf.st_uid) 243 ++entryfound; 244 ++ptr; 245 } 246 if (entryfound) 247 ++numfiles; 248 } 249 return (numfiles); 250 } 251 252 /* 253 * Print the queue. If only jobs belonging to a certain person(s) are requested, 254 * only print jobs that belong to that person(s). 255 */ 256 static void 257 printqueue(uid_t *uidlist, int nuids) 258 { 259 int i, j; /* for loop indices */ 260 int rank; /* rank of a job */ 261 int entryfound; /* found file owned by users */ 262 char *getname(); 263 uid_t *ptr; /* scratch pointer */ 264 struct stat stbuf; /* buffer for file stats */ 265 char curqueue; /* queue of current job */ 266 char lastqueue; /* queue of previous job */ 267 268 /* 269 * Print the header for the queue. 270 */ 271 printf(" Rank Execution Date Owner Job " 272 "Queue Job Name\n"); 273 274 /* 275 * Print the queue. If a certain name(s) was requested, print only jobs 276 * belonging to that person(s), otherwise print the entire queue. 277 * Once again, we have to use "entryfound" (rather than simply 278 * comparing each command line argument) so that if a person's name 279 * appears twice we don't print each file owned by him/her twice. 280 * 281 * 282 * "printrank", "printdate", and "printjobname" all take existing 283 * data and display it in a friendly manner. 284 * 285 */ 286 lastqueue = '\0'; 287 for (i = 0; i < numentries; i++) { 288 if ((stat(queue[i]->d_name, &stbuf)) < 0) { 289 continue; 290 } 291 curqueue = *(strchr(queue[i]->d_name, '.') + 1); 292 if (curqueue != lastqueue) { 293 rank = 1; 294 lastqueue = curqueue; 295 } 296 if (namewanted) { 297 ptr = uidlist; 298 entryfound = 0; 299 300 for (j = 0; j < nuids; j++) { 301 if (*ptr == stbuf.st_uid) 302 ++entryfound; 303 ++ptr; 304 } 305 if (!entryfound) 306 continue; 307 } 308 printrank(rank++); 309 printdate(queue[i]->d_name); 310 printf("%-10s ", getname(stbuf.st_uid)); 311 printf("%-14s ", queue[i]->d_name); 312 printf(" %c", curqueue); 313 printjobname(queue[i]->d_name); 314 } 315 ++ptr; 316 } 317 318 /* 319 * Get the uid of a person using his/her login name. Return -1 if no 320 * such account name exists. 321 */ 322 uid_t 323 getid(char *name) 324 { 325 326 struct passwd *pwdinfo; /* password info structure */ 327 328 329 if ((pwdinfo = getpwnam(name)) == 0) 330 return ((uid_t)-1); 331 332 return (pwdinfo->pw_uid); 333 } 334 335 /* 336 * Get the full login name of a person using his/her user id. 337 */ 338 char * 339 getname(uid_t uid) 340 { 341 struct passwd *pwdinfo; /* password info structure */ 342 343 344 if ((pwdinfo = getpwuid(uid)) == 0) 345 return ("???"); 346 return (pwdinfo->pw_name); 347 } 348 349 /* 350 * Print the rank of a job. (I've got to admit it, I stole it from "lpq") 351 */ 352 static void 353 printrank(int n) 354 { 355 static char *r[] = { 356 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 357 }; 358 359 if ((n/10) == 1) 360 printf("%3d%-5s", n, "th"); 361 else 362 printf("%3d%-5s", n, r[n%10]); 363 } 364 365 /* 366 * Print the date that a job is to be executed. This takes some manipulation 367 * of the file name. 368 */ 369 static void 370 printdate(char *filename) 371 { 372 time_t jobdate; 373 extern time_t num(); 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 "ascandir") 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 "ascandir") 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 extern time_t num(); 503 504 name1 = (*d1)->d_name; 505 name2 = (*d2)->d_name; 506 if ((p1 = strchr(name1, '.')) == NULL) 507 return (1); 508 if ((p2 = strchr(name2, '.')) == NULL) 509 return (1); 510 p1++; 511 p2++; 512 if ((i = *p1++ - *p2++) != 0) 513 return (i); 514 515 time1 = num(&name1); 516 time2 = num(&name2); 517 518 if (time1 < time2) 519 return (-1); 520 else if (time1 > time2) 521 return (1); 522 p1++; 523 p2++; 524 seq1 = atoi(p1); 525 seq2 = atoi(p2); 526 return (seq1 - seq2); 527 } 528 529 530 /* 531 * Print usage info and exit. 532 */ 533 static void 534 usage(void) 535 { 536 fprintf(stderr, "usage: atq [-c] [-n] [name ...]\n"); 537 exit(1); 538 } 539 540 static void 541 aterror(char *msg) 542 { 543 fprintf(stderr, "atq: %s\n", msg); 544 } 545 546 static void 547 atperror(char *msg) 548 { 549 fprintf(stderr, "atq: %s: %s\n", msg, errmsg(errno)); 550 } 551 552 static void 553 atabort(char *msg) 554 { 555 aterror(msg); 556 exit(1); 557 } 558 559 static void 560 atabortperror(char *msg) 561 { 562 atperror(msg); 563 exit(1); 564 } 565