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