1*48233Sbostic /*- 2*48233Sbostic * Copyright (c) 1983 The Regents of the University of California. 3*48233Sbostic * All rights reserved. 4*48233Sbostic * 5*48233Sbostic * %sccs.include.proprietary.c% 622419Sdist */ 722419Sdist 816818Swall #ifndef lint 922419Sdist char copyright[] = 10*48233Sbostic "@(#) Copyright (c) 1983 The Regents of the University of California.\n\ 1122419Sdist All rights reserved.\n"; 12*48233Sbostic #endif /* not lint */ 1316818Swall 1422419Sdist #ifndef lint 15*48233Sbostic static char sccsid[] = "@(#)atq.c 5.8 (Berkeley) 04/17/91"; 16*48233Sbostic #endif /* not lint */ 1722419Sdist 1816818Swall /* 1916818Swall * 2016828Smckusick * Synopsis: atq [ -c ] [ -n ] [ name ... ] 2116818Swall * 2216818Swall * 2316828Smckusick * Print the queue of files waiting to be executed. These files 2416828Smckusick * were created by using the "at" command and are located in the 2516828Smckusick * directory "/usr/spool/at". 2616818Swall * 2716818Swall * 2816828Smckusick * Author: Steve Wall 2916828Smckusick * Computer Systems Research Group 3016828Smckusick * University of California @ Berkeley 3116818Swall * 3216818Swall */ 3316818Swall 3416818Swall # include <stdio.h> 3516818Swall # include <sys/types.h> 3616818Swall # include <sys/file.h> 3716818Swall # include <sys/dir.h> 3816818Swall # include <sys/stat.h> 3916818Swall # include <sys/time.h> 4016818Swall # include <pwd.h> 4116818Swall # include <ctype.h> 4237962Sbostic # include "pathnames.h" 4316818Swall 4416818Swall /* 4516818Swall * Months of the year 4616818Swall */ 4716818Swall static char *mthnames[12] = { 4816828Smckusick "Jan","Feb","Mar","Apr","May","Jun","Jul", 4916828Smckusick "Aug","Sep","Oct","Nov","Dec", 5016818Swall }; 5116818Swall 5228872Smckusick char *nullentry = NULL; /* avoid 'namelist' NULL ptr problems */ 5316828Smckusick int numentries; /* number of entries in spooling area */ 5416828Smckusick int namewanted = 0; /* only print jobs belonging to a 5516828Smckusick certain person */ 5616828Smckusick struct direct **queue; /* the queue itself */ 5716818Swall 5816818Swall 5916818Swall main(argc,argv) 6016818Swall int argc; 6116818Swall char **argv; 6216818Swall { 6316818Swall 6416828Smckusick int cflag = 0; /* print in order of creation time */ 6516828Smckusick int nflag = 0; /* just print the number of jobs in 6616828Smckusick queue */ 6716828Smckusick int usage(); /* print usage info and exit */ 6816828Smckusick int creation(); /* sort jobs by date of creation */ 6916828Smckusick int alphasort(); /* sort jobs by date of execution */ 7016828Smckusick int filewanted(); /* should a file be included in queue?*/ 7116828Smckusick int printqueue(); /* print the queue */ 7216828Smckusick int countfiles(); /* count the number of files in queue 7316828Smckusick for a given person */ 7428872Smckusick char **namelist = &nullentry; /* array of specific name(s) requested*/ 7516818Swall 7616818Swall 7716828Smckusick --argc, ++argv; 7816818Swall 7916828Smckusick /* 8016828Smckusick * Interpret command line flags if they exist. 8116828Smckusick */ 8216828Smckusick while (argc > 0 && **argv == '-') { 8316828Smckusick (*argv)++; 8416828Smckusick while (**argv) switch (*(*argv)++) { 8516818Swall 8616828Smckusick case 'c' : cflag++; 8716828Smckusick break; 8816818Swall 8916828Smckusick case 'n' : nflag++; 9016828Smckusick break; 9116818Swall 9216828Smckusick default : usage(); 9316818Swall 9416828Smckusick } 9516828Smckusick --argc, ++argv; 9616828Smckusick } 9716818Swall 9816828Smckusick /* 9916828Smckusick * If a certain name (or names) is requested, set a pointer to the 10016828Smckusick * beginning of the list. 10116828Smckusick */ 10228872Smckusick if (argc > 0) { 10316828Smckusick ++namewanted; 10416828Smckusick namelist = argv; 10516828Smckusick } 10616818Swall 10716828Smckusick /* 10816828Smckusick * Move to the spooling area and scan the directory, placing the 10916828Smckusick * files in the queue structure. The queue comes back sorted by 11016828Smckusick * execution time or creation time. 11116828Smckusick */ 11237962Sbostic if (chdir(_PATH_ATDIR) == -1) { 11337962Sbostic perror(_PATH_ATDIR); 11416828Smckusick exit(1); 11516828Smckusick } 11616828Smckusick if ((numentries = scandir(".",&queue,filewanted, (cflag) ? creation : 11716828Smckusick alphasort)) < 0) { 11837962Sbostic perror(_PATH_ATDIR); 11916828Smckusick exit(1); 12016828Smckusick } 12116818Swall 12216828Smckusick /* 12316828Smckusick * Either print a message stating: 12416828Smckusick * 12516828Smckusick * 1) that the spooling area is empty. 12616828Smckusick * 2) the number of jobs in the spooling area. 12716828Smckusick * 3) the number of jobs in the spooling area belonging to 12816828Smckusick * a certain person. 12916828Smckusick * 4) that the person requested doesn't have any files in the 13016828Smckusick * spooling area. 13116828Smckusick * 13216828Smckusick * or send the queue off to "printqueue" for printing. 13316828Smckusick * 13416828Smckusick * This whole process might seem a bit elaborate, but it's worthwhile 13516828Smckusick * to print some informative messages for the user. 13616828Smckusick * 13716828Smckusick */ 13816828Smckusick if ((numentries == 0) && (!nflag)) { 13916828Smckusick printf("no files in queue.\n"); 14016828Smckusick exit(0); 14116828Smckusick } 14216828Smckusick if (nflag) { 14316828Smckusick printf("%d\n",(namewanted) ? countfiles(namelist) : numentries); 14416828Smckusick exit(0); 14516828Smckusick } 14616828Smckusick if ((namewanted) && (countfiles(namelist) == 0)) { 14716828Smckusick printf("no files for %s.\n", (argc == 1) ? 14816828Smckusick *argv : "specified users"); 14916828Smckusick exit(0); 15016828Smckusick } 15116828Smckusick printqueue(namelist); 15216828Smckusick exit(0); 15316818Swall } 15416818Swall 15516818Swall /* 15616818Swall * Count the number of jobs in the spooling area owned by a certain person(s). 15716818Swall */ 15816818Swall countfiles(namelist) 15916818Swall char **namelist; 16016818Swall { 16116828Smckusick int i; /* for loop index */ 16216828Smckusick int entryfound; /* found file owned by user(s)*/ 16316828Smckusick int numfiles = 0; /* number of files owned by a 16416828Smckusick certain person(s) */ 16516828Smckusick char **ptr; /* scratch pointer */ 16616818Swall 16716818Swall 16816828Smckusick /* 16916828Smckusick * For each file in the queue, see if the user(s) own the file. We 17016828Smckusick * have to use "entryfound" (rather than simply incrementing "numfiles") 17116828Smckusick * so that if a person's name appears twice on the command line we 17216828Smckusick * don't double the number of files owned by him/her. 17316828Smckusick */ 17416828Smckusick for (i = 0; i < numentries ; i++) { 17516828Smckusick ptr = namelist; 17616828Smckusick entryfound = 0; 17716818Swall 17816828Smckusick while (*ptr) { 17916865Swall if (isowner(*ptr,queue[i]->d_name)) 18016828Smckusick ++entryfound; 18116828Smckusick ++ptr; 18216828Smckusick } 18316828Smckusick if (entryfound) 18416828Smckusick ++numfiles; 18516828Smckusick } 18616828Smckusick return(numfiles); 18716818Swall } 18816818Swall 18916818Swall /* 19016818Swall * Print the queue. If only jobs belonging to a certain person(s) are requested, 19116818Swall * only print jobs that belong to that person(s). 19216818Swall */ 19316818Swall printqueue(namelist) 19416818Swall char **namelist; 19516818Swall { 19616828Smckusick int i; /* for loop index */ 19716828Smckusick int rank = 1; /* rank of a job */ 19816828Smckusick int entryfound; /* found file owned by user(s)*/ 19946817Sbostic static int printrank(); /* print the rank of a job */ 20016828Smckusick int plastrun(); /* print the last time the 20116828Smckusick spooling area was updated */ 20216865Swall int powner(); /* print the name of the owner 20316865Swall of the job */ 20416828Smckusick char **ptr; /* scratch pointer */ 20516828Smckusick struct stat stbuf; /* buffer for file stats */ 20616818Swall 20716818Swall 20816828Smckusick /* 20916828Smckusick * Print the time the spooling area was last modified and the header 21016828Smckusick * for the queue. 21116828Smckusick */ 21216828Smckusick plastrun(); 21316828Smckusick printf(" Rank Execution Date Owner Job # Job Name\n"); 21416818Swall 21516828Smckusick /* 21616828Smckusick * Print the queue. If a certain name(s) was requested, print only jobs 21716828Smckusick * belonging to that person(s), otherwise print the entire queue. 21816828Smckusick * Once again, we have to use "entryfound" (rather than simply 21916828Smckusick * comparing each command line argument) so that if a person's name 22016828Smckusick * appears twice we don't print each file owned by him/her twice. 22116828Smckusick * 22216828Smckusick * 22316828Smckusick * "printrank", "printdate", and "printjobname" all take existing 22416828Smckusick * data and display it in a friendly manner. 22516828Smckusick * 22616828Smckusick */ 22716828Smckusick for (i = 0; i < numentries; i++) { 22816828Smckusick if ((stat(queue[i]->d_name, &stbuf)) < 0) { 22916828Smckusick continue; 23016828Smckusick } 23116828Smckusick if (namewanted) { 23216828Smckusick ptr = namelist; 23316828Smckusick entryfound = 0; 23416818Swall 23516828Smckusick while (*ptr) { 23616865Swall if (isowner(*ptr,queue[i]->d_name)) 23716828Smckusick ++entryfound; 23816828Smckusick ++ptr; 23916828Smckusick } 24016828Smckusick if (!entryfound) 24116828Smckusick continue; 24216828Smckusick } 24316828Smckusick printrank(rank++); 24416828Smckusick printdate(queue[i]->d_name); 24516865Swall powner(queue[i]->d_name); 24616828Smckusick printf("%5d",stbuf.st_ino); 24716828Smckusick printjobname(queue[i]->d_name); 24816828Smckusick } 24916828Smckusick ++ptr; 25016818Swall } 25116818Swall 25216818Swall /* 25316865Swall * See if "name" owns "job". 25416818Swall */ 25516865Swall isowner(name,job) 25616865Swall char *name; 25716865Swall char *job; 25816818Swall { 25928872Smckusick char buf[128]; /* buffer for 1st line of spoolfile 26016865Swall header */ 26116865Swall FILE *infile; /* I/O stream to spoolfile */ 26216818Swall 26316865Swall if ((infile = fopen(job,"r")) == NULL) { 26428872Smckusick fprintf(stderr,"Couldn't open spoolfile "); 26516865Swall perror(job); 26616865Swall return(0); 26716828Smckusick } 26816865Swall 26928872Smckusick if (fscanf(infile,"# owner: %127s%*[^\n]\n",buf) != 1) { 27016865Swall fclose(infile); 27116865Swall return(0); 27216865Swall } 27316865Swall 27416865Swall fclose(infile); 27516865Swall return((strcmp(name,buf) == 0) ? 1 : 0); 27616818Swall } 27716865Swall 27816865Swall /* 27916865Swall * Print the owner of the job. This is stored on the first line of the 28016865Swall * spoolfile. If we run into trouble getting the name, we'll just print "???". 28116865Swall */ 28216865Swall powner(file) 28316865Swall char *file; 28416865Swall { 28528872Smckusick char owner[10]; /* the owner */ 28616865Swall FILE *infile; /* I/O stream to spoolfile */ 28716865Swall 28816865Swall /* 28916865Swall * Open the job file and grab the first line. 29016865Swall */ 29116865Swall 29216865Swall if ((infile = fopen(file,"r")) == NULL) { 29316865Swall printf("%-10.9s","???"); 29428872Smckusick perror(file); 29516865Swall return; 29616865Swall } 29716865Swall 29828872Smckusick if (fscanf(infile,"# owner: %9s%*[^\n]\n",owner) != 1) { 29916865Swall printf("%-10.9s","???"); 30016865Swall fclose(infile); 30116865Swall return; 30216865Swall } 30316865Swall 30416865Swall fclose(infile); 30516865Swall printf("%-10.9s",owner); 30616865Swall 30716865Swall } 30816828Smckusick 30916818Swall /* 31016818Swall * Print the time the spooling area was updated. 31116818Swall */ 31216818Swall plastrun() 31316818Swall { 31416828Smckusick struct timeval now; /* time it is right now */ 31516828Smckusick struct timezone zone; /* NOT USED */ 31616828Smckusick struct tm *loc; /* detail of time it is right */ 31746817Sbostic time_t lasttime; /* last update time in seconds 31816828Smckusick since 1/1/70 */ 31916828Smckusick FILE *last; /* file where last update hour 32016828Smckusick is stored */ 32116818Swall 32216818Swall 32316828Smckusick /* 32416828Smckusick * Open the file where the last update time is stored, and grab the 32516828Smckusick * last update hour. The update time is measured in seconds since 32616828Smckusick * 1/1/70. 32716828Smckusick */ 32837962Sbostic if ((last = fopen(_PATH_LASTFILE,"r")) == NULL) { 32937962Sbostic perror(_PATH_LASTFILE); 33016828Smckusick exit(1); 33116828Smckusick } 33230804Sbostic fscanf(last,"%lu",&lasttime); 33316828Smckusick fclose(last); 33416818Swall 33516828Smckusick /* 33616828Smckusick * Get a broken down representation of the last update time. 33716828Smckusick */ 33816828Smckusick loc = localtime(&lasttime); 33916818Swall 34016828Smckusick /* 34116828Smckusick * Print the time that the spooling area was last updated. 34216828Smckusick */ 34316828Smckusick printf("\n LAST EXECUTION TIME: %s ",mthnames[loc->tm_mon]); 34416828Smckusick printf("%d, 19%d ",loc->tm_mday,loc->tm_year); 34516828Smckusick printf("at %d:%02d\n\n",loc->tm_hour,loc->tm_min); 34616818Swall } 34716818Swall 34816818Swall /* 34916818Swall * Print the rank of a job. (I've got to admit it, I stole it from "lpq") 35016818Swall */ 35116818Swall static 35216818Swall printrank(n) 35316818Swall { 35416828Smckusick static char *r[] = { 35516828Smckusick "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 35616828Smckusick }; 35716818Swall 35816828Smckusick if ((n/10) == 1) 35916828Smckusick printf("%3d%-5s", n,"th"); 36016828Smckusick else 36116828Smckusick printf("%3d%-5s", n, r[n%10]); 36216818Swall } 36316818Swall 36416818Swall /* 36516818Swall * Print the date that a job is to be executed. This takes some manipulation 36616818Swall * of the file name. 36716818Swall */ 36816818Swall printdate(filename) 36916818Swall char *filename; 37016818Swall { 37116828Smckusick int yday = 0; /* day of year file will be 37216828Smckusick executed */ 37316828Smckusick int min = 0; /* min. file will be executed */ 37416828Smckusick int hour = 0; /* hour file will be executed */ 37516828Smckusick int day = 0; /* day file will be executed */ 37616828Smckusick int month = 0; /* month file will be executed*/ 37716828Smckusick int year = 0; /* year file will be executed */ 37816828Smckusick int get_mth_day(); /* convert a day of year to a 37916828Smckusick month and day of month */ 38036357Sbostic char date[19]; /* reformatted execution date */ 38116818Swall 38216828Smckusick /* 38316828Smckusick * Pick off the necessary info from the file name and convert the day 38416828Smckusick * of year to a month and day of month. 38516828Smckusick */ 38616828Smckusick sscanf(filename,"%2d.%3d.%2d%2d",&year,&yday,&hour,&min); 38716828Smckusick get_mth_day(year,yday,&month,&day); 38816818Swall 38916828Smckusick /* 39016828Smckusick * Format the execution date of a job. 39116828Smckusick */ 39216828Smckusick sprintf(date,"%3s %2d, 19%2d %02d:%02d",mthnames[month], 39316828Smckusick day, year,hour,min); 39416818Swall 39516828Smckusick /* 39616828Smckusick * Print the date the job will be executed. 39716828Smckusick */ 39816828Smckusick printf("%-21.18s",date); 39916818Swall } 40016818Swall 40116818Swall /* 40216818Swall * Given a day of the year, calculate the month and day of month. 40316818Swall */ 40416818Swall get_mth_day(year,dayofyear,month,day) 40516818Swall int year, dayofyear, *month, *day; 40616818Swall 40716818Swall { 40816818Swall 40916828Smckusick int i = 1; /* for loop index */ 41016828Smckusick int leap; /* are we dealing with a leap 41116828Smckusick year? */ 41216828Smckusick /* Table of the number of days 41316828Smckusick in each month of the year. 41416818Swall 41516828Smckusick dofy_tab[1] -- regular year 41616828Smckusick dofy_tab[2] -- leap year 41716828Smckusick */ 41816818Swall 41916828Smckusick static int dofy_tab[2][13] = { 42016828Smckusick { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 42116828Smckusick { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 42216828Smckusick }; 42316818Swall 42416828Smckusick /* 42516828Smckusick * Are we dealing with a leap year? 42616828Smckusick */ 42716828Smckusick leap = ((year%4 == 0 && year%100 != 0) || year%100 == 0); 42816818Swall 42916828Smckusick /* 43016828Smckusick * Calculate the month of the year and day of the month. 43116828Smckusick */ 43216828Smckusick while (dayofyear >= dofy_tab[leap][i]) { 43316828Smckusick dayofyear -= dofy_tab[leap][i++]; 43416828Smckusick ++(*month); 43516828Smckusick } 43616828Smckusick *day = (dayofyear + 1); 43716818Swall } 43816828Smckusick 43916818Swall /* 44016818Swall * Print a job name. If the old "at" has been used to create the spoolfile, 44116818Swall * the three line header that the new version of "at" puts in the spoolfile. 44216818Swall * Thus, we just print "???". 44316818Swall */ 44416818Swall printjobname(file) 44516818Swall char *file; 44616818Swall { 44716865Swall char *ptr; /* scratch pointer */ 44828872Smckusick char jobname[28]; /* the job name */ 44916828Smckusick FILE *filename; /* job file in spooling area */ 45016818Swall 45116828Smckusick /* 45216865Swall * Open the job file and grab the second line. 45316828Smckusick */ 45416828Smckusick printf(" "); 45516818Swall 45616828Smckusick if ((filename = fopen(file,"r")) == NULL) { 45716828Smckusick printf("%.27s\n", "???"); 45828872Smckusick perror(file); 45916828Smckusick return; 46016828Smckusick } 46116865Swall /* 46228872Smckusick * Skip over the first line. 46316865Swall */ 46428872Smckusick fscanf(filename,"%*[^\n]\n"); 46516865Swall 46616865Swall /* 46716865Swall * Now get the job name. 46816865Swall */ 46928872Smckusick if (fscanf(filename,"# jobname: %27s%*[^\n]\n",jobname) != 1) { 47016828Smckusick printf("%.27s\n", "???"); 47116828Smckusick fclose(filename); 47216828Smckusick return; 47316828Smckusick } 47416828Smckusick fclose(filename); 47516818Swall 47616828Smckusick /* 47716828Smckusick * Put a pointer at the begining of the line and remove the basename 47816828Smckusick * from the job file. 47916828Smckusick */ 48016865Swall ptr = jobname; 48116865Swall if ((ptr = (char *)rindex(jobname,'/')) != 0) 48216865Swall ++ptr; 48316828Smckusick else 48416865Swall ptr = jobname; 48516818Swall 48616865Swall if (strlen(ptr) > 23) 48716865Swall printf("%.23s ...\n",ptr); 48816865Swall else 48916865Swall printf("%.27s\n",ptr); 49016818Swall } 49116818Swall 49216818Swall /* 49316818Swall * Do we want to include a file in the queue? (used by "scandir") We are looking 49416818Swall * for files with following syntax: yy.ddd.hhhh. so the test is made to see if 49516818Swall * the file name has three dots in it. This test will suffice since the only 49616818Swall * other files in /usr/spool/at don't have any dots in their name. 49716818Swall */ 49816818Swall filewanted(direntry) 49916818Swall struct direct *direntry; 50016818Swall { 50116828Smckusick int numdot = 0; 50216828Smckusick char *filename; 50316818Swall 50416828Smckusick filename = direntry->d_name; 50516828Smckusick while (*filename) 50616828Smckusick numdot += (*(filename++) == '.'); 50716828Smckusick return(numdot == 3); 50816818Swall } 50916818Swall 51016818Swall /* 51116818Swall * Sort files by time of creation. (used by "scandir") 51216818Swall */ 51316818Swall creation(d1, d2) 51416818Swall struct direct **d1, **d2; 51516818Swall { 51616828Smckusick struct stat stbuf1, stbuf2; 51716818Swall 51816828Smckusick if (stat((*d1)->d_name,&stbuf1) < 0) 51916828Smckusick return(1); 52016818Swall 52116828Smckusick if (stat((*d2)->d_name,&stbuf2) < 0) 52216828Smckusick return(1); 52316818Swall 52416828Smckusick return(stbuf1.st_ctime < stbuf2.st_ctime); 52516818Swall } 52616828Smckusick 52716818Swall /* 52816818Swall * Print usage info and exit. 52916818Swall */ 53016818Swall usage() 53116818Swall { 53216828Smckusick fprintf(stderr,"usage: atq [-c] [-n] [name ...]\n"); 53316828Smckusick exit(1); 53416818Swall } 535