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 * Copyright 2021 Joyent, Inc. 6 */ 7 8 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 9 /* All Rights Reserved */ 10 11 12 /* 13 * Copyright (c) 1983 Regents of the University of California. 14 * All rights reserved. The Berkeley software License Agreement 15 * specifies the terms and conditions for redistribution. 16 */ 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 (cron_admin(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 (!cron_admin(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 that user. 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 of their files 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 their 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 their 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[19]; /* 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 snprintf(date, sizeof (date), "%3s %2d, %4d %02d:%02d", 390 mthnames[unpackeddate->tm_mon], 391 unpackeddate->tm_mday, unpackeddate->tm_year, 392 unpackeddate->tm_hour, unpackeddate->tm_min); 393 394 /* 395 * Print the date the job will be executed. 396 */ 397 printf("%-21.18s", date); 398 } 399 400 /* 401 * Print a job name. If the old "at" has been used to create the spoolfile, 402 * the three line header that the new version of "at" puts in the spoolfile. 403 * Thus, we just print "???". 404 */ 405 static void 406 printjobname(char *file) 407 { 408 char *ptr; /* scratch pointer */ 409 char jobname[28]; /* the job name */ 410 FILE *filename; /* job file in spooling area */ 411 412 /* 413 * Open the job file and grab the third line. 414 */ 415 printf(" "); 416 417 if ((filename = fopen(file, "r")) == NULL) { 418 printf("%.27s\n", "???"); 419 (void) fprintf(stderr, "atq: Can't open job file %s: %s\n", 420 file, errmsg(errno)); 421 return; 422 } 423 /* 424 * Skip over the first and second lines. 425 */ 426 fscanf(filename, "%*[^\n]\n"); 427 428 /* 429 * Now get the job name. 430 */ 431 if (fscanf(filename, ": jobname: %27s%*[^\n]\n", jobname) != 1) { 432 printf("%.27s\n", "???"); 433 fclose(filename); 434 return; 435 } 436 fclose(filename); 437 438 /* 439 * Put a pointer at the begining of the line and remove the basename 440 * from the job file. 441 */ 442 ptr = jobname; 443 if ((ptr = (char *)strrchr(jobname, '/')) != 0) 444 ++ptr; 445 else 446 ptr = jobname; 447 448 if (strlen(ptr) > 23) 449 printf("%.23s ...\n", ptr); 450 else 451 printf("%.27s\n", ptr); 452 } 453 454 455 456 /* 457 * Sort files by queue, time of creation, and sequence. (used by "scandir") 458 */ 459 int 460 creation(struct dirent **d1, struct dirent **d2) 461 { 462 char *p1, *p2; 463 int i; 464 struct stat stbuf1, stbuf2; 465 int seq1, seq2; 466 467 if ((p1 = strchr((*d1)->d_name, '.')) == NULL) 468 return (0); 469 if ((p2 = strchr((*d2)->d_name, '.')) == NULL) 470 return (0); 471 p1++; 472 p2++; 473 if ((i = *p1++ - *p2++) != 0) 474 return (i); 475 476 if (stat((*d1)->d_name, &stbuf1) < 0) 477 return (0); 478 479 if (stat((*d2)->d_name, &stbuf2) < 0) 480 return (0); 481 482 if (stbuf1.st_ctime < stbuf2.st_ctime) 483 return (-1); 484 else if (stbuf1.st_ctime > stbuf2.st_ctime) 485 return (1); 486 p1++; 487 p2++; 488 seq1 = atoi(p1); 489 seq2 = atoi(p2); 490 return (seq1 - seq2); 491 } 492 493 /* 494 * Sort files by queue, time of execution, and sequence. (used by "scandir") 495 */ 496 int 497 execution(struct dirent **d1, struct dirent **d2) 498 { 499 char *p1, *p2; 500 int i; 501 char *name1, *name2; 502 time_t time1, time2; 503 int seq1, seq2; 504 505 name1 = (*d1)->d_name; 506 name2 = (*d2)->d_name; 507 if ((p1 = strchr(name1, '.')) == NULL) 508 return (1); 509 if ((p2 = strchr(name2, '.')) == NULL) 510 return (1); 511 p1++; 512 p2++; 513 if ((i = *p1++ - *p2++) != 0) 514 return (i); 515 516 time1 = num(&name1); 517 time2 = num(&name2); 518 519 if (time1 < time2) 520 return (-1); 521 else if (time1 > time2) 522 return (1); 523 p1++; 524 p2++; 525 seq1 = atoi(p1); 526 seq2 = atoi(p2); 527 return (seq1 - seq2); 528 } 529 530 531 /* 532 * Print usage info and exit. 533 */ 534 static void 535 usage(void) 536 { 537 fprintf(stderr, "usage: atq [-c] [-n] [name ...]\n"); 538 exit(1); 539 } 540 541 static void 542 aterror(char *msg) 543 { 544 fprintf(stderr, "atq: %s\n", msg); 545 } 546 547 static void 548 atperror(char *msg) 549 { 550 fprintf(stderr, "atq: %s: %s\n", msg, errmsg(errno)); 551 } 552 553 static void 554 atabort(char *msg) 555 { 556 aterror(msg); 557 exit(1); 558 } 559 560 static void 561 atabortperror(char *msg) 562 { 563 atperror(msg); 564 exit(1); 565 } 566