116818Swall #ifndef lint 2*16828Smckusick static char sccsid[] = "@(#)atq.c 1.2 (Berkeley) 08/02/84"; 3*16828Smckusick #endif not lint 416818Swall 516818Swall /* 616818Swall * 7*16828Smckusick * Synopsis: atq [ -c ] [ -n ] [ name ... ] 816818Swall * 916818Swall * 10*16828Smckusick * Print the queue of files waiting to be executed. These files 11*16828Smckusick * were created by using the "at" command and are located in the 12*16828Smckusick * directory "/usr/spool/at". 1316818Swall * 1416818Swall * 15*16828Smckusick * Author: Steve Wall 16*16828Smckusick * Computer Systems Research Group 17*16828Smckusick * University of California @ Berkeley 1816818Swall * 1916818Swall */ 2016818Swall 2116818Swall # include <stdio.h> 2216818Swall # include <sys/types.h> 2316818Swall # include <sys/file.h> 2416818Swall # include <sys/dir.h> 2516818Swall # include <sys/stat.h> 2616818Swall # include <sys/time.h> 2716818Swall # include <pwd.h> 2816818Swall # include <ctype.h> 2916818Swall 30*16828Smckusick # define ATDIR "/usr/spool/at" /* spooling area */ 31*16828Smckusick # define LASTFILE "/usr/spool/at/lasttimedone" /* update time record 32*16828Smckusick file */ 3316818Swall 3416818Swall /* 3516818Swall * Months of the year 3616818Swall */ 3716818Swall static char *mthnames[12] = { 38*16828Smckusick "Jan","Feb","Mar","Apr","May","Jun","Jul", 39*16828Smckusick "Aug","Sep","Oct","Nov","Dec", 4016818Swall }; 4116818Swall 4216818Swall 43*16828Smckusick int numentries; /* number of entries in spooling area */ 44*16828Smckusick int namewanted = 0; /* only print jobs belonging to a 45*16828Smckusick certain person */ 46*16828Smckusick struct direct **queue; /* the queue itself */ 4716818Swall 4816818Swall 4916818Swall main(argc,argv) 5016818Swall int argc; 5116818Swall char **argv; 5216818Swall { 5316818Swall 54*16828Smckusick int cflag = 0; /* print in order of creation time */ 55*16828Smckusick int nflag = 0; /* just print the number of jobs in 56*16828Smckusick queue */ 57*16828Smckusick int usage(); /* print usage info and exit */ 58*16828Smckusick int creation(); /* sort jobs by date of creation */ 59*16828Smckusick int alphasort(); /* sort jobs by date of execution */ 60*16828Smckusick int filewanted(); /* should a file be included in queue?*/ 61*16828Smckusick int printqueue(); /* print the queue */ 62*16828Smckusick int countfiles(); /* count the number of files in queue 63*16828Smckusick for a given person */ 64*16828Smckusick char **namelist; /* array of specific name(s) requested*/ 6516818Swall 6616818Swall 67*16828Smckusick --argc, ++argv; 6816818Swall 69*16828Smckusick /* 70*16828Smckusick * Interpret command line flags if they exist. 71*16828Smckusick */ 72*16828Smckusick while (argc > 0 && **argv == '-') { 73*16828Smckusick (*argv)++; 74*16828Smckusick while (**argv) switch (*(*argv)++) { 7516818Swall 76*16828Smckusick case 'c' : cflag++; 77*16828Smckusick break; 7816818Swall 79*16828Smckusick case 'n' : nflag++; 80*16828Smckusick break; 8116818Swall 82*16828Smckusick default : usage(); 8316818Swall 84*16828Smckusick } 85*16828Smckusick --argc, ++argv; 86*16828Smckusick } 8716818Swall 88*16828Smckusick /* 89*16828Smckusick * If a certain name (or names) is requested, set a pointer to the 90*16828Smckusick * beginning of the list. 91*16828Smckusick */ 92*16828Smckusick if (**argv) { 93*16828Smckusick ++namewanted; 94*16828Smckusick namelist = argv; 95*16828Smckusick } 9616818Swall 97*16828Smckusick /* 98*16828Smckusick * Move to the spooling area and scan the directory, placing the 99*16828Smckusick * files in the queue structure. The queue comes back sorted by 100*16828Smckusick * execution time or creation time. 101*16828Smckusick */ 102*16828Smckusick if (chdir(ATDIR) == -1) { 103*16828Smckusick perror(ATDIR); 104*16828Smckusick exit(1); 105*16828Smckusick } 106*16828Smckusick if ((numentries = scandir(".",&queue,filewanted, (cflag) ? creation : 107*16828Smckusick alphasort)) < 0) { 108*16828Smckusick perror(ATDIR); 109*16828Smckusick exit(1); 110*16828Smckusick } 11116818Swall 112*16828Smckusick /* 113*16828Smckusick * Either print a message stating: 114*16828Smckusick * 115*16828Smckusick * 1) that the spooling area is empty. 116*16828Smckusick * 2) the number of jobs in the spooling area. 117*16828Smckusick * 3) the number of jobs in the spooling area belonging to 118*16828Smckusick * a certain person. 119*16828Smckusick * 4) that the person requested doesn't have any files in the 120*16828Smckusick * spooling area. 121*16828Smckusick * 122*16828Smckusick * or send the queue off to "printqueue" for printing. 123*16828Smckusick * 124*16828Smckusick * This whole process might seem a bit elaborate, but it's worthwhile 125*16828Smckusick * to print some informative messages for the user. 126*16828Smckusick * 127*16828Smckusick */ 128*16828Smckusick if ((numentries == 0) && (!nflag)) { 129*16828Smckusick printf("no files in queue.\n"); 130*16828Smckusick exit(0); 131*16828Smckusick } 132*16828Smckusick if (nflag) { 133*16828Smckusick printf("%d\n",(namewanted) ? countfiles(namelist) : numentries); 134*16828Smckusick exit(0); 135*16828Smckusick } 136*16828Smckusick if ((namewanted) && (countfiles(namelist) == 0)) { 137*16828Smckusick printf("no files for %s.\n", (argc == 1) ? 138*16828Smckusick *argv : "specified users"); 139*16828Smckusick exit(0); 140*16828Smckusick } 141*16828Smckusick printqueue(namelist); 142*16828Smckusick exit(0); 14316818Swall } 14416818Swall 14516818Swall /* 14616818Swall * Count the number of jobs in the spooling area owned by a certain person(s). 14716818Swall */ 14816818Swall countfiles(namelist) 14916818Swall char **namelist; 15016818Swall { 151*16828Smckusick int i; /* for loop index */ 152*16828Smckusick int entryfound; /* found file owned by user(s)*/ 153*16828Smckusick int numfiles = 0; /* number of files owned by a 154*16828Smckusick certain person(s) */ 155*16828Smckusick char **ptr; /* scratch pointer */ 156*16828Smckusick struct stat stbuf; /* buffer for file stats */ 157*16828Smckusick 15816818Swall 15916818Swall 160*16828Smckusick /* 161*16828Smckusick * For each file in the queue, see if the user(s) own the file. We 162*16828Smckusick * have to use "entryfound" (rather than simply incrementing "numfiles") 163*16828Smckusick * so that if a person's name appears twice on the command line we 164*16828Smckusick * don't double the number of files owned by him/her. 165*16828Smckusick */ 166*16828Smckusick for (i = 0; i < numentries ; i++) { 167*16828Smckusick if ((stat(queue[i]->d_name, &stbuf)) < 0) { 168*16828Smckusick continue; 169*16828Smckusick } 170*16828Smckusick ptr = namelist; 171*16828Smckusick entryfound = 0; 17216818Swall 173*16828Smckusick while (*ptr) { 174*16828Smckusick if (getid(*ptr) == stbuf.st_uid) 175*16828Smckusick ++entryfound; 176*16828Smckusick ++ptr; 177*16828Smckusick } 178*16828Smckusick if (entryfound) 179*16828Smckusick ++numfiles; 180*16828Smckusick } 181*16828Smckusick return(numfiles); 18216818Swall } 18316818Swall 18416818Swall /* 18516818Swall * Print the queue. If only jobs belonging to a certain person(s) are requested, 18616818Swall * only print jobs that belong to that person(s). 18716818Swall */ 18816818Swall printqueue(namelist) 18916818Swall char **namelist; 19016818Swall { 191*16828Smckusick int i; /* for loop index */ 192*16828Smckusick int rank = 1; /* rank of a job */ 193*16828Smckusick int entryfound; /* found file owned by user(s)*/ 194*16828Smckusick int printrank(); /* print the rank of a job */ 195*16828Smckusick int plastrun(); /* print the last time the 196*16828Smckusick spooling area was updated */ 197*16828Smckusick int getid(); /* get uid of a person */ 198*16828Smckusick char **ptr; /* scratch pointer */ 199*16828Smckusick char *getname(); /* get the login name of a 200*16828Smckusick person using their uid */ 201*16828Smckusick struct stat stbuf; /* buffer for file stats */ 20216818Swall 20316818Swall 204*16828Smckusick /* 205*16828Smckusick * Print the time the spooling area was last modified and the header 206*16828Smckusick * for the queue. 207*16828Smckusick */ 208*16828Smckusick plastrun(); 209*16828Smckusick printf(" Rank Execution Date Owner Job # Job Name\n"); 21016818Swall 211*16828Smckusick /* 212*16828Smckusick * Print the queue. If a certain name(s) was requested, print only jobs 213*16828Smckusick * belonging to that person(s), otherwise print the entire queue. 214*16828Smckusick * Once again, we have to use "entryfound" (rather than simply 215*16828Smckusick * comparing each command line argument) so that if a person's name 216*16828Smckusick * appears twice we don't print each file owned by him/her twice. 217*16828Smckusick * 218*16828Smckusick * 219*16828Smckusick * "printrank", "printdate", and "printjobname" all take existing 220*16828Smckusick * data and display it in a friendly manner. 221*16828Smckusick * 222*16828Smckusick */ 223*16828Smckusick for (i = 0; i < numentries; i++) { 224*16828Smckusick if ((stat(queue[i]->d_name, &stbuf)) < 0) { 225*16828Smckusick continue; 226*16828Smckusick } 227*16828Smckusick if (namewanted) { 228*16828Smckusick ptr = namelist; 229*16828Smckusick entryfound = 0; 23016818Swall 231*16828Smckusick while (*ptr) { 232*16828Smckusick if (getid(*ptr) == stbuf.st_uid) 233*16828Smckusick ++entryfound; 234*16828Smckusick ++ptr; 235*16828Smckusick } 236*16828Smckusick if (!entryfound) 237*16828Smckusick continue; 238*16828Smckusick } 239*16828Smckusick printrank(rank++); 240*16828Smckusick printdate(queue[i]->d_name); 241*16828Smckusick printf("%-10.9s",getname(stbuf.st_uid)); 242*16828Smckusick printf("%5d",stbuf.st_ino); 243*16828Smckusick printjobname(queue[i]->d_name); 244*16828Smckusick } 245*16828Smckusick ++ptr; 24616818Swall } 24716818Swall 24816818Swall /* 24916818Swall * Get the full login name of a person using his/her user id. 25016818Swall */ 25116818Swall char * 25216818Swall getname(uid) 25316818Swall int uid; 25416818Swall { 255*16828Smckusick struct passwd *pwdinfo; /* password info structure */ 256*16828Smckusick 25716818Swall 258*16828Smckusick if ((pwdinfo = getpwuid(uid)) == 0) { 259*16828Smckusick perror(uid); 260*16828Smckusick exit(1); 261*16828Smckusick } 262*16828Smckusick return(pwdinfo->pw_name); 26316818Swall } 264*16828Smckusick 26516818Swall /* 26616818Swall * Get the uid of a person using his/her login name. Return -1 if no 26716818Swall * such account name exists. 26816818Swall */ 26916818Swall getid(name) 27016818Swall char *name; 27116818Swall { 27216818Swall 273*16828Smckusick struct passwd *pwdinfo; /* password info structure */ 27416818Swall 27516818Swall 276*16828Smckusick if ((pwdinfo = getpwnam(name)) == 0) 277*16828Smckusick return(-1); 27816818Swall 279*16828Smckusick return(pwdinfo->pw_uid); 28016818Swall } 28116818Swall 28216818Swall /* 28316818Swall * Print the time the spooling area was updated. 28416818Swall */ 28516818Swall plastrun() 28616818Swall { 287*16828Smckusick struct timeval now; /* time it is right now */ 288*16828Smckusick struct timezone zone; /* NOT USED */ 289*16828Smckusick struct tm *loc; /* detail of time it is right */ 290*16828Smckusick u_long lasttime; /* last update time in seconds 291*16828Smckusick since 1/1/70 */ 292*16828Smckusick FILE *last; /* file where last update hour 293*16828Smckusick is stored */ 29416818Swall 29516818Swall 296*16828Smckusick /* 297*16828Smckusick * Open the file where the last update time is stored, and grab the 298*16828Smckusick * last update hour. The update time is measured in seconds since 299*16828Smckusick * 1/1/70. 300*16828Smckusick */ 301*16828Smckusick if ((last = fopen(LASTFILE,"r")) == NULL) { 302*16828Smckusick perror(LASTFILE); 303*16828Smckusick exit(1); 304*16828Smckusick } 305*16828Smckusick fscanf(last,"%d",(u_long) &lasttime); 306*16828Smckusick fclose(last); 30716818Swall 308*16828Smckusick /* 309*16828Smckusick * Get a broken down representation of the last update time. 310*16828Smckusick */ 311*16828Smckusick loc = localtime(&lasttime); 31216818Swall 313*16828Smckusick /* 314*16828Smckusick * Print the time that the spooling area was last updated. 315*16828Smckusick */ 316*16828Smckusick printf("\n LAST EXECUTION TIME: %s ",mthnames[loc->tm_mon]); 317*16828Smckusick printf("%d, 19%d ",loc->tm_mday,loc->tm_year); 318*16828Smckusick printf("at %d:%02d\n\n",loc->tm_hour,loc->tm_min); 31916818Swall } 32016818Swall 32116818Swall /* 32216818Swall * Print the rank of a job. (I've got to admit it, I stole it from "lpq") 32316818Swall */ 32416818Swall static 32516818Swall printrank(n) 32616818Swall { 327*16828Smckusick static char *r[] = { 328*16828Smckusick "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 329*16828Smckusick }; 33016818Swall 331*16828Smckusick if ((n/10) == 1) 332*16828Smckusick printf("%3d%-5s", n,"th"); 333*16828Smckusick else 334*16828Smckusick printf("%3d%-5s", n, r[n%10]); 33516818Swall } 33616818Swall 33716818Swall /* 33816818Swall * Print the date that a job is to be executed. This takes some manipulation 33916818Swall * of the file name. 34016818Swall */ 34116818Swall printdate(filename) 34216818Swall char *filename; 34316818Swall { 344*16828Smckusick int yday = 0; /* day of year file will be 345*16828Smckusick executed */ 346*16828Smckusick int min = 0; /* min. file will be executed */ 347*16828Smckusick int hour = 0; /* hour file will be executed */ 348*16828Smckusick int day = 0; /* day file will be executed */ 349*16828Smckusick int month = 0; /* month file will be executed*/ 350*16828Smckusick int year = 0; /* year file will be executed */ 351*16828Smckusick int get_mth_day(); /* convert a day of year to a 352*16828Smckusick month and day of month */ 353*16828Smckusick char date[18]; /* reformatted execution date */ 35416818Swall 355*16828Smckusick /* 356*16828Smckusick * Pick off the necessary info from the file name and convert the day 357*16828Smckusick * of year to a month and day of month. 358*16828Smckusick */ 359*16828Smckusick sscanf(filename,"%2d.%3d.%2d%2d",&year,&yday,&hour,&min); 360*16828Smckusick get_mth_day(year,yday,&month,&day); 36116818Swall 362*16828Smckusick /* 363*16828Smckusick * Format the execution date of a job. 364*16828Smckusick */ 365*16828Smckusick sprintf(date,"%3s %2d, 19%2d %02d:%02d",mthnames[month], 366*16828Smckusick day, year,hour,min); 36716818Swall 368*16828Smckusick /* 369*16828Smckusick * Print the date the job will be executed. 370*16828Smckusick */ 371*16828Smckusick printf("%-21.18s",date); 37216818Swall } 37316818Swall 37416818Swall /* 37516818Swall * Given a day of the year, calculate the month and day of month. 37616818Swall */ 37716818Swall get_mth_day(year,dayofyear,month,day) 37816818Swall int year, dayofyear, *month, *day; 37916818Swall 38016818Swall { 38116818Swall 382*16828Smckusick int i = 1; /* for loop index */ 383*16828Smckusick int leap; /* are we dealing with a leap 384*16828Smckusick year? */ 385*16828Smckusick /* Table of the number of days 386*16828Smckusick in each month of the year. 38716818Swall 388*16828Smckusick dofy_tab[1] -- regular year 389*16828Smckusick dofy_tab[2] -- leap year 390*16828Smckusick */ 39116818Swall 392*16828Smckusick static int dofy_tab[2][13] = { 393*16828Smckusick { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 394*16828Smckusick { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 395*16828Smckusick }; 39616818Swall 397*16828Smckusick /* 398*16828Smckusick * Are we dealing with a leap year? 399*16828Smckusick */ 400*16828Smckusick leap = ((year%4 == 0 && year%100 != 0) || year%100 == 0); 40116818Swall 402*16828Smckusick /* 403*16828Smckusick * Calculate the month of the year and day of the month. 404*16828Smckusick */ 405*16828Smckusick while (dayofyear >= dofy_tab[leap][i]) { 406*16828Smckusick dayofyear -= dofy_tab[leap][i++]; 407*16828Smckusick ++(*month); 408*16828Smckusick } 409*16828Smckusick *day = (dayofyear + 1); 41016818Swall } 411*16828Smckusick 41216818Swall /* 41316818Swall * Print a job name. If the old "at" has been used to create the spoolfile, 41416818Swall * the three line header that the new version of "at" puts in the spoolfile. 41516818Swall * Thus, we just print "???". 41616818Swall */ 41716818Swall printjobname(file) 41816818Swall char *file; 41916818Swall { 420*16828Smckusick char *ch; /* char ptr */ 421*16828Smckusick char jobname[80]; /* the job name */ 422*16828Smckusick FILE *filename; /* job file in spooling area */ 42316818Swall 424*16828Smckusick /* 425*16828Smckusick * Open the job file and grab the first line. 426*16828Smckusick */ 427*16828Smckusick printf(" "); 42816818Swall 429*16828Smckusick if ((filename = fopen(file,"r")) == NULL) { 430*16828Smckusick printf("%.27s\n", "???"); 431*16828Smckusick fclose(filename); 432*16828Smckusick return; 433*16828Smckusick } 434*16828Smckusick if (fscanf(filename,"# jobname: %s",jobname) != 1) { 435*16828Smckusick printf("%.27s\n", "???"); 436*16828Smckusick fclose(filename); 437*16828Smckusick return; 438*16828Smckusick } 439*16828Smckusick fclose(filename); 44016818Swall 441*16828Smckusick /* 442*16828Smckusick * Put a pointer at the begining of the line and remove the basename 443*16828Smckusick * from the job file. 444*16828Smckusick */ 445*16828Smckusick ch = jobname; 446*16828Smckusick if ((ch = (char *)rindex(jobname,'/')) != 0) 447*16828Smckusick ++ch; 448*16828Smckusick else 449*16828Smckusick ch = jobname; 45016818Swall 451*16828Smckusick printf("%.27s\n",ch); 45216818Swall } 45316818Swall 45416818Swall /* 45516818Swall * Do we want to include a file in the queue? (used by "scandir") We are looking 45616818Swall * for files with following syntax: yy.ddd.hhhh. so the test is made to see if 45716818Swall * the file name has three dots in it. This test will suffice since the only 45816818Swall * other files in /usr/spool/at don't have any dots in their name. 45916818Swall */ 46016818Swall filewanted(direntry) 46116818Swall struct direct *direntry; 46216818Swall { 463*16828Smckusick int numdot = 0; 464*16828Smckusick char *filename; 46516818Swall 466*16828Smckusick filename = direntry->d_name; 467*16828Smckusick while (*filename) 468*16828Smckusick numdot += (*(filename++) == '.'); 469*16828Smckusick return(numdot == 3); 47016818Swall } 47116818Swall 47216818Swall /* 47316818Swall * Sort files by time of creation. (used by "scandir") 47416818Swall */ 47516818Swall creation(d1, d2) 47616818Swall struct direct **d1, **d2; 47716818Swall { 478*16828Smckusick struct stat stbuf1, stbuf2; 47916818Swall 480*16828Smckusick if (stat((*d1)->d_name,&stbuf1) < 0) 481*16828Smckusick return(1); 48216818Swall 483*16828Smckusick if (stat((*d2)->d_name,&stbuf2) < 0) 484*16828Smckusick return(1); 48516818Swall 486*16828Smckusick return(stbuf1.st_ctime < stbuf2.st_ctime); 48716818Swall } 488*16828Smckusick 48916818Swall /* 49016818Swall * Print usage info and exit. 49116818Swall */ 49216818Swall usage() 49316818Swall { 494*16828Smckusick fprintf(stderr,"usage: atq [-c] [-n] [name ...]\n"); 495*16828Smckusick exit(1); 49616818Swall } 497