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