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