1 /* 2 * Copyright 2009 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 /* 17 * 18 * Synopsis: atq [ -c ] [ -n ] [ name ... ] 19 * 20 * 21 * Print the queue of files waiting to be executed. These files 22 * were created by using the "at" command and are located in the 23 * directory defined by ATDIR. 24 */ 25 26 #include <stdio.h> 27 #include <sys/types.h> 28 #include <sys/file.h> 29 #include <dirent.h> 30 #include <sys/stat.h> 31 #include <time.h> 32 #include <pwd.h> 33 #include <ctype.h> 34 #include <unistd.h> 35 #include <locale.h> 36 #include <errno.h> 37 #include <stdlib.h> 38 #include <string.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 (cron_admin(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 (!cron_admin(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 = scandir(".", &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 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 static void 403 printjobname(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 "scandir") 455 */ 456 int 457 creation(struct dirent **d1, struct dirent **d2) 458 { 459 char *p1, *p2; 460 int i; 461 struct stat stbuf1, stbuf2; 462 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 "scandir") 492 */ 493 int 494 execution(struct dirent **d1, struct dirent **d2) 495 { 496 char *p1, *p2; 497 int i; 498 char *name1, *name2; 499 time_t time1, time2; 500 int seq1, seq2; 501 502 name1 = (*d1)->d_name; 503 name2 = (*d2)->d_name; 504 if ((p1 = strchr(name1, '.')) == NULL) 505 return (1); 506 if ((p2 = strchr(name2, '.')) == NULL) 507 return (1); 508 p1++; 509 p2++; 510 if ((i = *p1++ - *p2++) != 0) 511 return (i); 512 513 time1 = num(&name1); 514 time2 = num(&name2); 515 516 if (time1 < time2) 517 return (-1); 518 else if (time1 > time2) 519 return (1); 520 p1++; 521 p2++; 522 seq1 = atoi(p1); 523 seq2 = atoi(p2); 524 return (seq1 - seq2); 525 } 526 527 528 /* 529 * Print usage info and exit. 530 */ 531 static void 532 usage(void) 533 { 534 fprintf(stderr, "usage: atq [-c] [-n] [name ...]\n"); 535 exit(1); 536 } 537 538 static void 539 aterror(char *msg) 540 { 541 fprintf(stderr, "atq: %s\n", msg); 542 } 543 544 static void 545 atperror(char *msg) 546 { 547 fprintf(stderr, "atq: %s: %s\n", msg, errmsg(errno)); 548 } 549 550 static void 551 atabort(char *msg) 552 { 553 aterror(msg); 554 exit(1); 555 } 556 557 static void 558 atabortperror(char *msg) 559 { 560 atperror(msg); 561 exit(1); 562 } 563