116818Swall #ifndef lint 2*16865Swall static char sccsid[] = "@(#)atq.c 1.3 (Berkeley) 08/09/84"; 316828Smckusick #endif not lint 416818Swall 516818Swall /* 616818Swall * 716828Smckusick * Synopsis: atq [ -c ] [ -n ] [ name ... ] 816818Swall * 916818Swall * 1016828Smckusick * Print the queue of files waiting to be executed. These files 1116828Smckusick * were created by using the "at" command and are located in the 1216828Smckusick * directory "/usr/spool/at". 1316818Swall * 1416818Swall * 1516828Smckusick * Author: Steve Wall 1616828Smckusick * Computer Systems Research Group 1716828Smckusick * 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 3016828Smckusick # define ATDIR "/usr/spool/at" /* spooling area */ 3116828Smckusick # define LASTFILE "/usr/spool/at/lasttimedone" /* update time record 3216828Smckusick file */ 3316818Swall 3416818Swall /* 3516818Swall * Months of the year 3616818Swall */ 3716818Swall static char *mthnames[12] = { 3816828Smckusick "Jan","Feb","Mar","Apr","May","Jun","Jul", 3916828Smckusick "Aug","Sep","Oct","Nov","Dec", 4016818Swall }; 4116818Swall 4216818Swall 4316828Smckusick int numentries; /* number of entries in spooling area */ 4416828Smckusick int namewanted = 0; /* only print jobs belonging to a 4516828Smckusick certain person */ 4616828Smckusick struct direct **queue; /* the queue itself */ 4716818Swall 4816818Swall 4916818Swall main(argc,argv) 5016818Swall int argc; 5116818Swall char **argv; 5216818Swall { 5316818Swall 5416828Smckusick int cflag = 0; /* print in order of creation time */ 5516828Smckusick int nflag = 0; /* just print the number of jobs in 5616828Smckusick queue */ 5716828Smckusick int usage(); /* print usage info and exit */ 5816828Smckusick int creation(); /* sort jobs by date of creation */ 5916828Smckusick int alphasort(); /* sort jobs by date of execution */ 6016828Smckusick int filewanted(); /* should a file be included in queue?*/ 6116828Smckusick int printqueue(); /* print the queue */ 6216828Smckusick int countfiles(); /* count the number of files in queue 6316828Smckusick for a given person */ 6416828Smckusick char **namelist; /* array of specific name(s) requested*/ 6516818Swall 6616818Swall 6716828Smckusick --argc, ++argv; 6816818Swall 6916828Smckusick /* 7016828Smckusick * Interpret command line flags if they exist. 7116828Smckusick */ 7216828Smckusick while (argc > 0 && **argv == '-') { 7316828Smckusick (*argv)++; 7416828Smckusick while (**argv) switch (*(*argv)++) { 7516818Swall 7616828Smckusick case 'c' : cflag++; 7716828Smckusick break; 7816818Swall 7916828Smckusick case 'n' : nflag++; 8016828Smckusick break; 8116818Swall 8216828Smckusick default : usage(); 8316818Swall 8416828Smckusick } 8516828Smckusick --argc, ++argv; 8616828Smckusick } 8716818Swall 8816828Smckusick /* 8916828Smckusick * If a certain name (or names) is requested, set a pointer to the 9016828Smckusick * beginning of the list. 9116828Smckusick */ 9216828Smckusick if (**argv) { 9316828Smckusick ++namewanted; 9416828Smckusick namelist = argv; 9516828Smckusick } 9616818Swall 9716828Smckusick /* 9816828Smckusick * Move to the spooling area and scan the directory, placing the 9916828Smckusick * files in the queue structure. The queue comes back sorted by 10016828Smckusick * execution time or creation time. 10116828Smckusick */ 10216828Smckusick if (chdir(ATDIR) == -1) { 10316828Smckusick perror(ATDIR); 10416828Smckusick exit(1); 10516828Smckusick } 10616828Smckusick if ((numentries = scandir(".",&queue,filewanted, (cflag) ? creation : 10716828Smckusick alphasort)) < 0) { 10816828Smckusick perror(ATDIR); 10916828Smckusick exit(1); 11016828Smckusick } 11116818Swall 11216828Smckusick /* 11316828Smckusick * Either print a message stating: 11416828Smckusick * 11516828Smckusick * 1) that the spooling area is empty. 11616828Smckusick * 2) the number of jobs in the spooling area. 11716828Smckusick * 3) the number of jobs in the spooling area belonging to 11816828Smckusick * a certain person. 11916828Smckusick * 4) that the person requested doesn't have any files in the 12016828Smckusick * spooling area. 12116828Smckusick * 12216828Smckusick * or send the queue off to "printqueue" for printing. 12316828Smckusick * 12416828Smckusick * This whole process might seem a bit elaborate, but it's worthwhile 12516828Smckusick * to print some informative messages for the user. 12616828Smckusick * 12716828Smckusick */ 12816828Smckusick if ((numentries == 0) && (!nflag)) { 12916828Smckusick printf("no files in queue.\n"); 13016828Smckusick exit(0); 13116828Smckusick } 13216828Smckusick if (nflag) { 13316828Smckusick printf("%d\n",(namewanted) ? countfiles(namelist) : numentries); 13416828Smckusick exit(0); 13516828Smckusick } 13616828Smckusick if ((namewanted) && (countfiles(namelist) == 0)) { 13716828Smckusick printf("no files for %s.\n", (argc == 1) ? 13816828Smckusick *argv : "specified users"); 13916828Smckusick exit(0); 14016828Smckusick } 14116828Smckusick printqueue(namelist); 14216828Smckusick 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 { 15116828Smckusick int i; /* for loop index */ 15216828Smckusick int entryfound; /* found file owned by user(s)*/ 15316828Smckusick int numfiles = 0; /* number of files owned by a 15416828Smckusick certain person(s) */ 15516828Smckusick char **ptr; /* scratch pointer */ 15616818Swall 15716818Swall 15816828Smckusick /* 15916828Smckusick * For each file in the queue, see if the user(s) own the file. We 16016828Smckusick * have to use "entryfound" (rather than simply incrementing "numfiles") 16116828Smckusick * so that if a person's name appears twice on the command line we 16216828Smckusick * don't double the number of files owned by him/her. 16316828Smckusick */ 16416828Smckusick for (i = 0; i < numentries ; i++) { 16516828Smckusick ptr = namelist; 16616828Smckusick entryfound = 0; 16716818Swall 16816828Smckusick while (*ptr) { 169*16865Swall if (isowner(*ptr,queue[i]->d_name)) 17016828Smckusick ++entryfound; 17116828Smckusick ++ptr; 17216828Smckusick } 17316828Smckusick if (entryfound) 17416828Smckusick ++numfiles; 17516828Smckusick } 17616828Smckusick return(numfiles); 17716818Swall } 17816818Swall 17916818Swall /* 18016818Swall * Print the queue. If only jobs belonging to a certain person(s) are requested, 18116818Swall * only print jobs that belong to that person(s). 18216818Swall */ 18316818Swall printqueue(namelist) 18416818Swall char **namelist; 18516818Swall { 18616828Smckusick int i; /* for loop index */ 18716828Smckusick int rank = 1; /* rank of a job */ 18816828Smckusick int entryfound; /* found file owned by user(s)*/ 18916828Smckusick int printrank(); /* print the rank of a job */ 19016828Smckusick int plastrun(); /* print the last time the 19116828Smckusick spooling area was updated */ 192*16865Swall int powner(); /* print the name of the owner 193*16865Swall of the job */ 19416828Smckusick int getid(); /* get uid of a person */ 19516828Smckusick char **ptr; /* scratch pointer */ 19616828Smckusick struct stat stbuf; /* buffer for file stats */ 19716818Swall 19816818Swall 19916828Smckusick /* 20016828Smckusick * Print the time the spooling area was last modified and the header 20116828Smckusick * for the queue. 20216828Smckusick */ 20316828Smckusick plastrun(); 20416828Smckusick printf(" Rank Execution Date Owner Job # Job Name\n"); 20516818Swall 20616828Smckusick /* 20716828Smckusick * Print the queue. If a certain name(s) was requested, print only jobs 20816828Smckusick * belonging to that person(s), otherwise print the entire queue. 20916828Smckusick * Once again, we have to use "entryfound" (rather than simply 21016828Smckusick * comparing each command line argument) so that if a person's name 21116828Smckusick * appears twice we don't print each file owned by him/her twice. 21216828Smckusick * 21316828Smckusick * 21416828Smckusick * "printrank", "printdate", and "printjobname" all take existing 21516828Smckusick * data and display it in a friendly manner. 21616828Smckusick * 21716828Smckusick */ 21816828Smckusick for (i = 0; i < numentries; i++) { 21916828Smckusick if ((stat(queue[i]->d_name, &stbuf)) < 0) { 22016828Smckusick continue; 22116828Smckusick } 22216828Smckusick if (namewanted) { 22316828Smckusick ptr = namelist; 22416828Smckusick entryfound = 0; 22516818Swall 22616828Smckusick while (*ptr) { 227*16865Swall if (isowner(*ptr,queue[i]->d_name)) 22816828Smckusick ++entryfound; 22916828Smckusick ++ptr; 23016828Smckusick } 23116828Smckusick if (!entryfound) 23216828Smckusick continue; 23316828Smckusick } 23416828Smckusick printrank(rank++); 23516828Smckusick printdate(queue[i]->d_name); 236*16865Swall powner(queue[i]->d_name); 23716828Smckusick printf("%5d",stbuf.st_ino); 23816828Smckusick printjobname(queue[i]->d_name); 23916828Smckusick } 24016828Smckusick ++ptr; 24116818Swall } 24216818Swall 24316818Swall /* 244*16865Swall * See if "name" owns "job". 24516818Swall */ 246*16865Swall isowner(name,job) 247*16865Swall char *name; 248*16865Swall char *job; 24916818Swall { 250*16865Swall char buf[30]; /* buffer for 1st line of spoolfile 251*16865Swall header */ 252*16865Swall FILE *infile; /* I/O stream to spoolfile */ 25316818Swall 254*16865Swall if ((infile = fopen(job,"r")) == NULL) { 255*16865Swall fprintf(stderr,"Couldn't open spoolfile"); 256*16865Swall perror(job); 257*16865Swall return(0); 25816828Smckusick } 259*16865Swall 260*16865Swall if (fscanf(infile,"# owner: %s\n",buf) != 1) { 261*16865Swall fclose(infile); 262*16865Swall return(0); 263*16865Swall } 264*16865Swall 265*16865Swall fclose(infile); 266*16865Swall return((strcmp(name,buf) == 0) ? 1 : 0); 26716818Swall } 268*16865Swall 269*16865Swall /* 270*16865Swall * Print the owner of the job. This is stored on the first line of the 271*16865Swall * spoolfile. If we run into trouble getting the name, we'll just print "???". 272*16865Swall */ 273*16865Swall powner(file) 274*16865Swall char *file; 275*16865Swall { 276*16865Swall char owner[80]; /* the owner */ 277*16865Swall FILE *infile; /* I/O stream to spoolfile */ 278*16865Swall 279*16865Swall /* 280*16865Swall * Open the job file and grab the first line. 281*16865Swall */ 282*16865Swall 283*16865Swall if ((infile = fopen(file,"r")) == NULL) { 284*16865Swall printf("%-10.9s","???"); 285*16865Swall return; 286*16865Swall } 287*16865Swall 288*16865Swall if (fscanf(infile,"# owner: %s",owner) != 1) { 289*16865Swall printf("%-10.9s","???"); 290*16865Swall fclose(infile); 291*16865Swall return; 292*16865Swall } 293*16865Swall 294*16865Swall fclose(infile); 295*16865Swall printf("%-10.9s",owner); 296*16865Swall 297*16865Swall } 29816828Smckusick 299*16865Swall 30016818Swall /* 30116818Swall * Get the uid of a person using his/her login name. Return -1 if no 30216818Swall * such account name exists. 30316818Swall */ 30416818Swall getid(name) 30516818Swall char *name; 30616818Swall { 30716818Swall 30816828Smckusick struct passwd *pwdinfo; /* password info structure */ 30916818Swall 31016818Swall 31116828Smckusick if ((pwdinfo = getpwnam(name)) == 0) 31216828Smckusick return(-1); 31316818Swall 31416828Smckusick return(pwdinfo->pw_uid); 31516818Swall } 31616818Swall 31716818Swall /* 31816818Swall * Print the time the spooling area was updated. 31916818Swall */ 32016818Swall plastrun() 32116818Swall { 32216828Smckusick struct timeval now; /* time it is right now */ 32316828Smckusick struct timezone zone; /* NOT USED */ 32416828Smckusick struct tm *loc; /* detail of time it is right */ 32516828Smckusick u_long lasttime; /* last update time in seconds 32616828Smckusick since 1/1/70 */ 32716828Smckusick FILE *last; /* file where last update hour 32816828Smckusick is stored */ 32916818Swall 33016818Swall 33116828Smckusick /* 33216828Smckusick * Open the file where the last update time is stored, and grab the 33316828Smckusick * last update hour. The update time is measured in seconds since 33416828Smckusick * 1/1/70. 33516828Smckusick */ 33616828Smckusick if ((last = fopen(LASTFILE,"r")) == NULL) { 33716828Smckusick perror(LASTFILE); 33816828Smckusick exit(1); 33916828Smckusick } 34016828Smckusick fscanf(last,"%d",(u_long) &lasttime); 34116828Smckusick fclose(last); 34216818Swall 34316828Smckusick /* 34416828Smckusick * Get a broken down representation of the last update time. 34516828Smckusick */ 34616828Smckusick loc = localtime(&lasttime); 34716818Swall 34816828Smckusick /* 34916828Smckusick * Print the time that the spooling area was last updated. 35016828Smckusick */ 35116828Smckusick printf("\n LAST EXECUTION TIME: %s ",mthnames[loc->tm_mon]); 35216828Smckusick printf("%d, 19%d ",loc->tm_mday,loc->tm_year); 35316828Smckusick printf("at %d:%02d\n\n",loc->tm_hour,loc->tm_min); 35416818Swall } 35516818Swall 35616818Swall /* 35716818Swall * Print the rank of a job. (I've got to admit it, I stole it from "lpq") 35816818Swall */ 35916818Swall static 36016818Swall printrank(n) 36116818Swall { 36216828Smckusick static char *r[] = { 36316828Smckusick "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 36416828Smckusick }; 36516818Swall 36616828Smckusick if ((n/10) == 1) 36716828Smckusick printf("%3d%-5s", n,"th"); 36816828Smckusick else 36916828Smckusick printf("%3d%-5s", n, r[n%10]); 37016818Swall } 37116818Swall 37216818Swall /* 37316818Swall * Print the date that a job is to be executed. This takes some manipulation 37416818Swall * of the file name. 37516818Swall */ 37616818Swall printdate(filename) 37716818Swall char *filename; 37816818Swall { 37916828Smckusick int yday = 0; /* day of year file will be 38016828Smckusick executed */ 38116828Smckusick int min = 0; /* min. file will be executed */ 38216828Smckusick int hour = 0; /* hour file will be executed */ 38316828Smckusick int day = 0; /* day file will be executed */ 38416828Smckusick int month = 0; /* month file will be executed*/ 38516828Smckusick int year = 0; /* year file will be executed */ 38616828Smckusick int get_mth_day(); /* convert a day of year to a 38716828Smckusick month and day of month */ 38816828Smckusick char date[18]; /* reformatted execution date */ 38916818Swall 39016828Smckusick /* 39116828Smckusick * Pick off the necessary info from the file name and convert the day 39216828Smckusick * of year to a month and day of month. 39316828Smckusick */ 39416828Smckusick sscanf(filename,"%2d.%3d.%2d%2d",&year,&yday,&hour,&min); 39516828Smckusick get_mth_day(year,yday,&month,&day); 39616818Swall 39716828Smckusick /* 39816828Smckusick * Format the execution date of a job. 39916828Smckusick */ 40016828Smckusick sprintf(date,"%3s %2d, 19%2d %02d:%02d",mthnames[month], 40116828Smckusick day, year,hour,min); 40216818Swall 40316828Smckusick /* 40416828Smckusick * Print the date the job will be executed. 40516828Smckusick */ 40616828Smckusick printf("%-21.18s",date); 40716818Swall } 40816818Swall 40916818Swall /* 41016818Swall * Given a day of the year, calculate the month and day of month. 41116818Swall */ 41216818Swall get_mth_day(year,dayofyear,month,day) 41316818Swall int year, dayofyear, *month, *day; 41416818Swall 41516818Swall { 41616818Swall 41716828Smckusick int i = 1; /* for loop index */ 41816828Smckusick int leap; /* are we dealing with a leap 41916828Smckusick year? */ 42016828Smckusick /* Table of the number of days 42116828Smckusick in each month of the year. 42216818Swall 42316828Smckusick dofy_tab[1] -- regular year 42416828Smckusick dofy_tab[2] -- leap year 42516828Smckusick */ 42616818Swall 42716828Smckusick static int dofy_tab[2][13] = { 42816828Smckusick { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 42916828Smckusick { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 43016828Smckusick }; 43116818Swall 43216828Smckusick /* 43316828Smckusick * Are we dealing with a leap year? 43416828Smckusick */ 43516828Smckusick leap = ((year%4 == 0 && year%100 != 0) || year%100 == 0); 43616818Swall 43716828Smckusick /* 43816828Smckusick * Calculate the month of the year and day of the month. 43916828Smckusick */ 44016828Smckusick while (dayofyear >= dofy_tab[leap][i]) { 44116828Smckusick dayofyear -= dofy_tab[leap][i++]; 44216828Smckusick ++(*month); 44316828Smckusick } 44416828Smckusick *day = (dayofyear + 1); 44516818Swall } 44616828Smckusick 44716818Swall /* 44816818Swall * Print a job name. If the old "at" has been used to create the spoolfile, 44916818Swall * the three line header that the new version of "at" puts in the spoolfile. 45016818Swall * Thus, we just print "???". 45116818Swall */ 45216818Swall printjobname(file) 45316818Swall char *file; 45416818Swall { 455*16865Swall char *ptr; /* scratch pointer */ 45616828Smckusick char jobname[80]; /* the job name */ 45716828Smckusick FILE *filename; /* job file in spooling area */ 45816818Swall 45916828Smckusick /* 460*16865Swall * Open the job file and grab the second line. 46116828Smckusick */ 46216828Smckusick printf(" "); 46316818Swall 46416828Smckusick if ((filename = fopen(file,"r")) == NULL) { 46516828Smckusick printf("%.27s\n", "???"); 46616828Smckusick return; 46716828Smckusick } 468*16865Swall /* 469*16865Swall * We'll yank the first line into the buffer temporarily. 470*16865Swall */ 471*16865Swall fgets(jobname,80,filename); 472*16865Swall 473*16865Swall /* 474*16865Swall * Now get the job name. 475*16865Swall */ 47616828Smckusick if (fscanf(filename,"# jobname: %s",jobname) != 1) { 47716828Smckusick printf("%.27s\n", "???"); 47816828Smckusick fclose(filename); 47916828Smckusick return; 48016828Smckusick } 48116828Smckusick fclose(filename); 48216818Swall 48316828Smckusick /* 48416828Smckusick * Put a pointer at the begining of the line and remove the basename 48516828Smckusick * from the job file. 48616828Smckusick */ 487*16865Swall ptr = jobname; 488*16865Swall if ((ptr = (char *)rindex(jobname,'/')) != 0) 489*16865Swall ++ptr; 49016828Smckusick else 491*16865Swall ptr = jobname; 49216818Swall 493*16865Swall if (strlen(ptr) > 23) 494*16865Swall printf("%.23s ...\n",ptr); 495*16865Swall else 496*16865Swall printf("%.27s\n",ptr); 49716818Swall } 49816818Swall 49916818Swall /* 50016818Swall * Do we want to include a file in the queue? (used by "scandir") We are looking 50116818Swall * for files with following syntax: yy.ddd.hhhh. so the test is made to see if 50216818Swall * the file name has three dots in it. This test will suffice since the only 50316818Swall * other files in /usr/spool/at don't have any dots in their name. 50416818Swall */ 50516818Swall filewanted(direntry) 50616818Swall struct direct *direntry; 50716818Swall { 50816828Smckusick int numdot = 0; 50916828Smckusick char *filename; 51016818Swall 51116828Smckusick filename = direntry->d_name; 51216828Smckusick while (*filename) 51316828Smckusick numdot += (*(filename++) == '.'); 51416828Smckusick return(numdot == 3); 51516818Swall } 51616818Swall 51716818Swall /* 51816818Swall * Sort files by time of creation. (used by "scandir") 51916818Swall */ 52016818Swall creation(d1, d2) 52116818Swall struct direct **d1, **d2; 52216818Swall { 52316828Smckusick struct stat stbuf1, stbuf2; 52416818Swall 52516828Smckusick if (stat((*d1)->d_name,&stbuf1) < 0) 52616828Smckusick return(1); 52716818Swall 52816828Smckusick if (stat((*d2)->d_name,&stbuf2) < 0) 52916828Smckusick return(1); 53016818Swall 53116828Smckusick return(stbuf1.st_ctime < stbuf2.st_ctime); 53216818Swall } 53316828Smckusick 53416818Swall /* 53516818Swall * Print usage info and exit. 53616818Swall */ 53716818Swall usage() 53816818Swall { 53916828Smckusick fprintf(stderr,"usage: atq [-c] [-n] [name ...]\n"); 54016828Smckusick exit(1); 54116818Swall } 542