1: /* 2: * Copyright (c) 1983 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #ifndef lint 8: char copyright[] = 9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 10: All rights reserved.\n"; 11: #endif not lint 12: 13: #ifndef lint 14: static char sccsid[] = "@(#)atq.c 5.2 (Berkeley) 5/28/86"; 15: #endif not lint 16: 17: /* 18: * 19: * Synopsis: atq [ -c ] [ -n ] [ name ... ] 20: * 21: * 22: * Print the queue of files waiting to be executed. These files 23: * were created by using the "at" command and are located in the 24: * directory "/usr/spool/at". 25: * 26: * 27: * Author: Steve Wall 28: * Computer Systems Research Group 29: * University of California @ Berkeley 30: * 31: */ 32: 33: # include <stdio.h> 34: # include <sys/types.h> 35: # include <sys/file.h> 36: # include <sys/dir.h> 37: # include <sys/stat.h> 38: # include <sys/time.h> 39: # include <pwd.h> 40: # include <ctype.h> 41: 42: # define ATDIR "/usr/spool/at" /* spooling area */ 43: # define LASTFILE "/usr/spool/at/lasttimedone" /* update time record 44: file */ 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: char *nullentry = NULL; /* avoid 'namelist' NULL ptr problems */ 55: int numentries; /* number of entries in spooling area */ 56: int namewanted = 0; /* only print jobs belonging to a 57: certain person */ 58: struct direct **queue; /* the queue itself */ 59: 60: 61: main(argc,argv) 62: int argc; 63: char **argv; 64: { 65: 66: int cflag = 0; /* print in order of creation time */ 67: int nflag = 0; /* just print the number of jobs in 68: queue */ 69: int usage(); /* print usage info and exit */ 70: int creation(); /* sort jobs by date of creation */ 71: int alphasort(); /* sort jobs by date of execution */ 72: int filewanted(); /* should a file be included in queue?*/ 73: int printqueue(); /* print the queue */ 74: int countfiles(); /* count the number of files in queue 75: for a given person */ 76: char **namelist = &nullentry; /* array of specific name(s) requested*/ 77: 78: 79: --argc, ++argv; 80: 81: /* 82: * Interpret command line flags if they exist. 83: */ 84: while (argc > 0 && **argv == '-') { 85: (*argv)++; 86: while (**argv) switch (*(*argv)++) { 87: 88: case 'c' : cflag++; 89: break; 90: 91: case 'n' : nflag++; 92: break; 93: 94: default : usage(); 95: 96: } 97: --argc, ++argv; 98: } 99: 100: /* 101: * If a certain name (or names) is requested, set a pointer to the 102: * beginning of the list. 103: */ 104: if (argc > 0) { 105: ++namewanted; 106: namelist = argv; 107: } 108: 109: /* 110: * Move to the spooling area and scan the directory, placing the 111: * files in the queue structure. The queue comes back sorted by 112: * execution time or creation time. 113: */ 114: if (chdir(ATDIR) == -1) { 115: perror(ATDIR); 116: exit(1); 117: } 118: if ((numentries = scandir(".",&queue,filewanted, (cflag) ? creation : 119: alphasort)) < 0) { 120: perror(ATDIR); 121: exit(1); 122: } 123: 124: /* 125: * Either print a message stating: 126: * 127: * 1) that the spooling area is empty. 128: * 2) the number of jobs in the spooling area. 129: * 3) the number of jobs in the spooling area belonging to 130: * a certain person. 131: * 4) that the person requested doesn't have any files in the 132: * spooling area. 133: * 134: * or send the queue off to "printqueue" for printing. 135: * 136: * This whole process might seem a bit elaborate, but it's worthwhile 137: * to print some informative messages for the user. 138: * 139: */ 140: if ((numentries == 0) && (!nflag)) { 141: printf("no files in queue.\n"); 142: exit(0); 143: } 144: if (nflag) { 145: printf("%d\n",(namewanted) ? countfiles(namelist) : numentries); 146: exit(0); 147: } 148: if ((namewanted) && (countfiles(namelist) == 0)) { 149: printf("no files for %s.\n", (argc == 1) ? 150: *argv : "specified users"); 151: exit(0); 152: } 153: printqueue(namelist); 154: exit(0); 155: } 156: 157: /* 158: * Count the number of jobs in the spooling area owned by a certain person(s). 159: */ 160: countfiles(namelist) 161: char **namelist; 162: { 163: int i; /* for loop index */ 164: int entryfound; /* found file owned by user(s)*/ 165: int numfiles = 0; /* number of files owned by a 166: certain person(s) */ 167: char **ptr; /* scratch pointer */ 168: 169: 170: /* 171: * For each file in the queue, see if the user(s) own the file. We 172: * have to use "entryfound" (rather than simply incrementing "numfiles") 173: * so that if a person's name appears twice on the command line we 174: * don't double the number of files owned by him/her. 175: */ 176: for (i = 0; i < numentries ; i++) { 177: ptr = namelist; 178: entryfound = 0; 179: 180: while (*ptr) { 181: if (isowner(*ptr,queue[i]->d_name)) 182: ++entryfound; 183: ++ptr; 184: } 185: if (entryfound) 186: ++numfiles; 187: } 188: return(numfiles); 189: } 190: 191: /* 192: * Print the queue. If only jobs belonging to a certain person(s) are requested, 193: * only print jobs that belong to that person(s). 194: */ 195: printqueue(namelist) 196: char **namelist; 197: { 198: int i; /* for loop index */ 199: int rank = 1; /* rank of a job */ 200: int entryfound; /* found file owned by user(s)*/ 201: int printrank(); /* print the rank of a job */ 202: int plastrun(); /* print the last time the 203: spooling area was updated */ 204: int powner(); /* print the name of the owner 205: of the job */ 206: int getid(); /* get uid of a person */ 207: char **ptr; /* scratch pointer */ 208: struct stat stbuf; /* buffer for file stats */ 209: 210: 211: /* 212: * Print the time the spooling area was last modified and the header 213: * for the queue. 214: */ 215: plastrun(); 216: printf(" Rank Execution Date Owner Job # Job Name\n"); 217: 218: /* 219: * Print the queue. If a certain name(s) was requested, print only jobs 220: * belonging to that person(s), otherwise print the entire queue. 221: * Once again, we have to use "entryfound" (rather than simply 222: * comparing each command line argument) so that if a person's name 223: * appears twice we don't print each file owned by him/her twice. 224: * 225: * 226: * "printrank", "printdate", and "printjobname" all take existing 227: * data and display it in a friendly manner. 228: * 229: */ 230: for (i = 0; i < numentries; i++) { 231: if ((stat(queue[i]->d_name, &stbuf)) < 0) { 232: continue; 233: } 234: if (namewanted) { 235: ptr = namelist; 236: entryfound = 0; 237: 238: while (*ptr) { 239: if (isowner(*ptr,queue[i]->d_name)) 240: ++entryfound; 241: ++ptr; 242: } 243: if (!entryfound) 244: continue; 245: } 246: printrank(rank++); 247: printdate(queue[i]->d_name); 248: powner(queue[i]->d_name); 249: printf("%5d",stbuf.st_ino); 250: printjobname(queue[i]->d_name); 251: } 252: ++ptr; 253: } 254: 255: /* 256: * See if "name" owns "job". 257: */ 258: isowner(name,job) 259: char *name; 260: char *job; 261: { 262: char buf[128]; /* buffer for 1st line of spoolfile 263: header */ 264: FILE *infile; /* I/O stream to spoolfile */ 265: 266: if ((infile = fopen(job,"r")) == NULL) { 267: fprintf(stderr,"Couldn't open spoolfile "); 268: perror(job); 269: return(0); 270: } 271: 272: if (fscanf(infile,"# owner: %127s%*[^\n]\n",buf) != 1) { 273: fclose(infile); 274: return(0); 275: } 276: 277: fclose(infile); 278: return((strcmp(name,buf) == 0) ? 1 : 0); 279: } 280: 281: /* 282: * Print the owner of the job. This is stored on the first line of the 283: * spoolfile. If we run into trouble getting the name, we'll just print "???". 284: */ 285: powner(file) 286: char *file; 287: { 288: char owner[10]; /* the owner */ 289: FILE *infile; /* I/O stream to spoolfile */ 290: 291: /* 292: * Open the job file and grab the first line. 293: */ 294: 295: if ((infile = fopen(file,"r")) == NULL) { 296: printf("%-10.9s","???"); 297: perror(file); 298: return; 299: } 300: 301: if (fscanf(infile,"# owner: %9s%*[^\n]\n",owner) != 1) { 302: printf("%-10.9s","???"); 303: fclose(infile); 304: return; 305: } 306: 307: fclose(infile); 308: printf("%-10.9s",owner); 309: 310: } 311: 312: 313: /* 314: * Get the uid of a person using his/her login name. Return -1 if no 315: * such account name exists. 316: */ 317: getid(name) 318: char *name; 319: { 320: 321: struct passwd *pwdinfo; /* password info structure */ 322: 323: 324: if ((pwdinfo = getpwnam(name)) == 0) 325: return(-1); 326: 327: return(pwdinfo->pw_uid); 328: } 329: 330: /* 331: * Print the time the spooling area was updated. 332: */ 333: plastrun() 334: { 335: struct timeval now; /* time it is right now */ 336: struct timezone zone; /* NOT USED */ 337: struct tm *loc; /* detail of time it is right */ 338: u_long lasttime; /* last update time in seconds 339: since 1/1/70 */ 340: FILE *last; /* file where last update hour 341: is stored */ 342: 343: 344: /* 345: * Open the file where the last update time is stored, and grab the 346: * last update hour. The update time is measured in seconds since 347: * 1/1/70. 348: */ 349: if ((last = fopen(LASTFILE,"r")) == NULL) { 350: perror(LASTFILE); 351: exit(1); 352: } 353: fscanf(last,"%ld", &lasttime); 354: fclose(last); 355: 356: /* 357: * Get a broken down representation of the last update time. 358: */ 359: loc = localtime(&lasttime); 360: 361: /* 362: * Print the time that the spooling area was last updated. 363: */ 364: printf("\n LAST EXECUTION TIME: %s ",mthnames[loc->tm_mon]); 365: printf("%d, 19%d ",loc->tm_mday,loc->tm_year); 366: printf("at %d:%02d\n\n",loc->tm_hour,loc->tm_min); 367: } 368: 369: /* 370: * Print the rank of a job. (I've got to admit it, I stole it from "lpq") 371: */ 372: static 373: printrank(n) 374: { 375: static char *r[] = { 376: "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 377: }; 378: 379: if ((n/10) == 1) 380: printf("%3d%-5s", n,"th"); 381: else 382: printf("%3d%-5s", n, r[n%10]); 383: } 384: 385: /* 386: * Print the date that a job is to be executed. This takes some manipulation 387: * of the file name. 388: */ 389: printdate(filename) 390: char *filename; 391: { 392: int yday = 0; /* day of year file will be 393: executed */ 394: int min = 0; /* min. file will be executed */ 395: int hour = 0; /* hour file will be executed */ 396: int day = 0; /* day file will be executed */ 397: int month = 0; /* month file will be executed*/ 398: int year = 0; /* year file will be executed */ 399: int get_mth_day(); /* convert a day of year to a 400: month and day of month */ 401: char date[18]; /* reformatted execution date */ 402: 403: /* 404: * Pick off the necessary info from the file name and convert the day 405: * of year to a month and day of month. 406: */ 407: sscanf(filename,"%2d.%3d.%2d%2d",&year,&yday,&hour,&min); 408: get_mth_day(year,yday,&month,&day); 409: 410: /* 411: * Format the execution date of a job. 412: */ 413: sprintf(date,"%3s %2d, 19%2d %02d:%02d",mthnames[month], 414: day, year,hour,min); 415: 416: /* 417: * Print the date the job will be executed. 418: */ 419: printf("%-21.18s",date); 420: } 421: 422: /* 423: * Given a day of the year, calculate the month and day of month. 424: */ 425: get_mth_day(year,dayofyear,month,day) 426: int year, dayofyear, *month, *day; 427: 428: { 429: 430: int i = 1; /* for loop index */ 431: int leap; /* are we dealing with a leap 432: year? */ 433: /* Table of the number of days 434: in each month of the year. 435: 436: dofy_tab[1] -- regular year 437: dofy_tab[2] -- leap year 438: */ 439: 440: static int dofy_tab[2][13] = { 441: { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 442: { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 443: }; 444: 445: /* 446: * Are we dealing with a leap year? 447: */ 448: leap = ((year%4 == 0 && year%100 != 0) || year%100 == 0); 449: 450: /* 451: * Calculate the month of the year and day of the month. 452: */ 453: while (dayofyear >= dofy_tab[leap][i]) { 454: dayofyear -= dofy_tab[leap][i++]; 455: ++(*month); 456: } 457: *day = (dayofyear + 1); 458: } 459: 460: /* 461: * Print a job name. If the old "at" has been used to create the spoolfile, 462: * the three line header that the new version of "at" puts in the spoolfile. 463: * Thus, we just print "???". 464: */ 465: printjobname(file) 466: char *file; 467: { 468: char *ptr; /* scratch pointer */ 469: char jobname[28]; /* the job name */ 470: FILE *filename; /* job file in spooling area */ 471: 472: /* 473: * Open the job file and grab the second line. 474: */ 475: printf(" "); 476: 477: if ((filename = fopen(file,"r")) == NULL) { 478: printf("%.27s\n", "???"); 479: perror(file); 480: return; 481: } 482: /* 483: * Skip over the first line. 484: */ 485: fscanf(filename,"%*[^\n]\n"); 486: 487: /* 488: * Now get the job name. 489: */ 490: if (fscanf(filename,"# jobname: %27s%*[^\n]\n",jobname) != 1) { 491: printf("%.27s\n", "???"); 492: fclose(filename); 493: return; 494: } 495: fclose(filename); 496: 497: /* 498: * Put a pointer at the begining of the line and remove the basename 499: * from the job file. 500: */ 501: ptr = jobname; 502: if ((ptr = (char *)rindex(jobname,'/')) != 0) 503: ++ptr; 504: else 505: ptr = jobname; 506: 507: if (strlen(ptr) > 23) 508: printf("%.23s ...\n",ptr); 509: else 510: printf("%.27s\n",ptr); 511: } 512: 513: /* 514: * Do we want to include a file in the queue? (used by "scandir") We are looking 515: * for files with following syntax: yy.ddd.hhhh. so the test is made to see if 516: * the file name has three dots in it. This test will suffice since the only 517: * other files in /usr/spool/at don't have any dots in their name. 518: */ 519: filewanted(direntry) 520: struct direct *direntry; 521: { 522: int numdot = 0; 523: char *filename; 524: 525: filename = direntry->d_name; 526: while (*filename) 527: numdot += (*(filename++) == '.'); 528: return(numdot == 3); 529: } 530: 531: /* 532: * Sort files by time of creation. (used by "scandir") 533: */ 534: creation(d1, d2) 535: struct direct **d1, **d2; 536: { 537: struct stat stbuf1, stbuf2; 538: 539: if (stat((*d1)->d_name,&stbuf1) < 0) 540: return(1); 541: 542: if (stat((*d2)->d_name,&stbuf2) < 0) 543: return(1); 544: 545: return(stbuf1.st_ctime < stbuf2.st_ctime); 546: } 547: 548: /* 549: * Print usage info and exit. 550: */ 551: usage() 552: { 553: fprintf(stderr,"usage: atq [-c] [-n] [name ...]\n"); 554: exit(1); 555: }