Commit | Line | Data |
---|---|---|
2791ff57 KB |
1 | /*- |
2 | * Copyright (c) 1983 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * %sccs.include.proprietary.c% | |
d0aeaf5a DF |
6 | */ |
7 | ||
ec349c9f | 8 | #ifndef lint |
d0aeaf5a | 9 | char copyright[] = |
2791ff57 | 10 | "@(#) Copyright (c) 1983 The Regents of the University of California.\n\ |
d0aeaf5a | 11 | All rights reserved.\n"; |
2791ff57 | 12 | #endif /* not lint */ |
d0aeaf5a DF |
13 | |
14 | #ifndef lint | |
2791ff57 KB |
15 | static char sccsid[] = "@(#)atq.c 5.8 (Berkeley) %G%"; |
16 | #endif /* not lint */ | |
ec349c9f SW |
17 | |
18 | /* | |
19 | * | |
d6fff2c5 | 20 | * Synopsis: atq [ -c ] [ -n ] [ name ... ] |
ec349c9f SW |
21 | * |
22 | * | |
d6fff2c5 KM |
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". | |
ec349c9f SW |
26 | * |
27 | * | |
d6fff2c5 KM |
28 | * Author: Steve Wall |
29 | * Computer Systems Research Group | |
30 | * University of California @ Berkeley | |
ec349c9f SW |
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> | |
7abf8d65 | 42 | # include "pathnames.h" |
ec349c9f SW |
43 | |
44 | /* | |
45 | * Months of the year | |
46 | */ | |
47 | static char *mthnames[12] = { | |
d6fff2c5 KM |
48 | "Jan","Feb","Mar","Apr","May","Jun","Jul", |
49 | "Aug","Sep","Oct","Nov","Dec", | |
ec349c9f SW |
50 | }; |
51 | ||
e0799b82 | 52 | char *nullentry = NULL; /* avoid 'namelist' NULL ptr problems */ |
d6fff2c5 KM |
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 */ | |
ec349c9f SW |
57 | |
58 | ||
59 | main(argc,argv) | |
60 | int argc; | |
61 | char **argv; | |
62 | { | |
63 | ||
d6fff2c5 KM |
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 */ | |
e0799b82 | 74 | char **namelist = &nullentry; /* array of specific name(s) requested*/ |
d6fff2c5 KM |
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 | */ | |
e0799b82 | 102 | if (argc > 0) { |
d6fff2c5 KM |
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 | */ | |
7abf8d65 KB |
112 | if (chdir(_PATH_ATDIR) == -1) { |
113 | perror(_PATH_ATDIR); | |
d6fff2c5 KM |
114 | exit(1); |
115 | } | |
116 | if ((numentries = scandir(".",&queue,filewanted, (cflag) ? creation : | |
117 | alphasort)) < 0) { | |
7abf8d65 | 118 | perror(_PATH_ATDIR); |
d6fff2c5 KM |
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); | |
ec349c9f SW |
153 | } |
154 | ||
155 | /* | |
156 | * Count the number of jobs in the spooling area owned by a certain person(s). | |
157 | */ | |
158 | countfiles(namelist) | |
159 | char **namelist; | |
160 | { | |
d6fff2c5 KM |
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 */ | |
d6fff2c5 KM |
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++) { | |
d6fff2c5 KM |
175 | ptr = namelist; |
176 | entryfound = 0; | |
177 | ||
178 | while (*ptr) { | |
8cc905ee | 179 | if (isowner(*ptr,queue[i]->d_name)) |
d6fff2c5 KM |
180 | ++entryfound; |
181 | ++ptr; | |
182 | } | |
183 | if (entryfound) | |
184 | ++numfiles; | |
185 | } | |
186 | return(numfiles); | |
ec349c9f SW |
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 | */ | |
193 | printqueue(namelist) | |
194 | char **namelist; | |
195 | { | |
d6fff2c5 KM |
196 | int i; /* for loop index */ |
197 | int rank = 1; /* rank of a job */ | |
198 | int entryfound; /* found file owned by user(s)*/ | |
9e709397 | 199 | static int printrank(); /* print the rank of a job */ |
d6fff2c5 KM |
200 | int plastrun(); /* print the last time the |
201 | spooling area was updated */ | |
8cc905ee SW |
202 | int powner(); /* print the name of the owner |
203 | of the job */ | |
d6fff2c5 | 204 | char **ptr; /* scratch pointer */ |
d6fff2c5 KM |
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) { | |
8cc905ee | 236 | if (isowner(*ptr,queue[i]->d_name)) |
d6fff2c5 KM |
237 | ++entryfound; |
238 | ++ptr; | |
239 | } | |
240 | if (!entryfound) | |
241 | continue; | |
242 | } | |
243 | printrank(rank++); | |
244 | printdate(queue[i]->d_name); | |
8cc905ee | 245 | powner(queue[i]->d_name); |
d6fff2c5 KM |
246 | printf("%5d",stbuf.st_ino); |
247 | printjobname(queue[i]->d_name); | |
248 | } | |
249 | ++ptr; | |
ec349c9f SW |
250 | } |
251 | ||
252 | /* | |
8cc905ee | 253 | * See if "name" owns "job". |
ec349c9f | 254 | */ |
8cc905ee SW |
255 | isowner(name,job) |
256 | char *name; | |
257 | char *job; | |
ec349c9f | 258 | { |
e0799b82 | 259 | char buf[128]; /* buffer for 1st line of spoolfile |
8cc905ee SW |
260 | header */ |
261 | FILE *infile; /* I/O stream to spoolfile */ | |
262 | ||
263 | if ((infile = fopen(job,"r")) == NULL) { | |
e0799b82 | 264 | fprintf(stderr,"Couldn't open spoolfile "); |
8cc905ee SW |
265 | perror(job); |
266 | return(0); | |
267 | } | |
d6fff2c5 | 268 | |
e0799b82 | 269 | if (fscanf(infile,"# owner: %127s%*[^\n]\n",buf) != 1) { |
8cc905ee SW |
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 | */ | |
282 | powner(file) | |
283 | char *file; | |
284 | { | |
e0799b82 | 285 | char owner[10]; /* the owner */ |
8cc905ee SW |
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","???"); | |
e0799b82 | 294 | perror(file); |
8cc905ee | 295 | return; |
d6fff2c5 | 296 | } |
8cc905ee | 297 | |
e0799b82 | 298 | if (fscanf(infile,"# owner: %9s%*[^\n]\n",owner) != 1) { |
8cc905ee SW |
299 | printf("%-10.9s","???"); |
300 | fclose(infile); | |
301 | return; | |
302 | } | |
303 | ||
304 | fclose(infile); | |
305 | printf("%-10.9s",owner); | |
306 | ||
ec349c9f | 307 | } |
d6fff2c5 | 308 | |
ec349c9f SW |
309 | /* |
310 | * Print the time the spooling area was updated. | |
311 | */ | |
312 | plastrun() | |
313 | { | |
d6fff2c5 KM |
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 */ | |
9e709397 | 317 | time_t lasttime; /* last update time in seconds |
d6fff2c5 KM |
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 | */ | |
7abf8d65 KB |
328 | if ((last = fopen(_PATH_LASTFILE,"r")) == NULL) { |
329 | perror(_PATH_LASTFILE); | |
d6fff2c5 KM |
330 | exit(1); |
331 | } | |
339ca52e | 332 | fscanf(last,"%lu",&lasttime); |
d6fff2c5 KM |
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); | |
ec349c9f SW |
346 | } |
347 | ||
348 | /* | |
349 | * Print the rank of a job. (I've got to admit it, I stole it from "lpq") | |
350 | */ | |
351 | static | |
352 | printrank(n) | |
353 | { | |
d6fff2c5 KM |
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]); | |
ec349c9f SW |
362 | } |
363 | ||
364 | /* | |
365 | * Print the date that a job is to be executed. This takes some manipulation | |
366 | * of the file name. | |
367 | */ | |
368 | printdate(filename) | |
369 | char *filename; | |
370 | { | |
d6fff2c5 KM |
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 */ | |
cce9af83 | 380 | char date[19]; /* reformatted execution date */ |
d6fff2c5 KM |
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); | |
ec349c9f SW |
399 | } |
400 | ||
401 | /* | |
402 | * Given a day of the year, calculate the month and day of month. | |
403 | */ | |
404 | get_mth_day(year,dayofyear,month,day) | |
405 | int year, dayofyear, *month, *day; | |
406 | ||
407 | { | |
408 | ||
d6fff2c5 KM |
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); | |
ec349c9f | 437 | } |
d6fff2c5 | 438 | |
ec349c9f SW |
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 | */ | |
444 | printjobname(file) | |
445 | char *file; | |
446 | { | |
8cc905ee | 447 | char *ptr; /* scratch pointer */ |
e0799b82 | 448 | char jobname[28]; /* the job name */ |
d6fff2c5 KM |
449 | FILE *filename; /* job file in spooling area */ |
450 | ||
451 | /* | |
8cc905ee | 452 | * Open the job file and grab the second line. |
d6fff2c5 KM |
453 | */ |
454 | printf(" "); | |
455 | ||
456 | if ((filename = fopen(file,"r")) == NULL) { | |
457 | printf("%.27s\n", "???"); | |
e0799b82 | 458 | perror(file); |
d6fff2c5 KM |
459 | return; |
460 | } | |
8cc905ee | 461 | /* |
e0799b82 | 462 | * Skip over the first line. |
8cc905ee | 463 | */ |
e0799b82 | 464 | fscanf(filename,"%*[^\n]\n"); |
8cc905ee SW |
465 | |
466 | /* | |
467 | * Now get the job name. | |
468 | */ | |
e0799b82 | 469 | if (fscanf(filename,"# jobname: %27s%*[^\n]\n",jobname) != 1) { |
d6fff2c5 KM |
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 | */ | |
8cc905ee SW |
480 | ptr = jobname; |
481 | if ((ptr = (char *)rindex(jobname,'/')) != 0) | |
482 | ++ptr; | |
d6fff2c5 | 483 | else |
8cc905ee | 484 | ptr = jobname; |
d6fff2c5 | 485 | |
8cc905ee SW |
486 | if (strlen(ptr) > 23) |
487 | printf("%.23s ...\n",ptr); | |
488 | else | |
489 | printf("%.27s\n",ptr); | |
ec349c9f SW |
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 | { | |
d6fff2c5 KM |
501 | int numdot = 0; |
502 | char *filename; | |
ec349c9f | 503 | |
d6fff2c5 KM |
504 | filename = direntry->d_name; |
505 | while (*filename) | |
506 | numdot += (*(filename++) == '.'); | |
507 | return(numdot == 3); | |
ec349c9f SW |
508 | } |
509 | ||
510 | /* | |
511 | * Sort files by time of creation. (used by "scandir") | |
512 | */ | |
513 | creation(d1, d2) | |
514 | struct direct **d1, **d2; | |
515 | { | |
d6fff2c5 | 516 | struct stat stbuf1, stbuf2; |
ec349c9f | 517 | |
d6fff2c5 KM |
518 | if (stat((*d1)->d_name,&stbuf1) < 0) |
519 | return(1); | |
ec349c9f | 520 | |
d6fff2c5 KM |
521 | if (stat((*d2)->d_name,&stbuf2) < 0) |
522 | return(1); | |
ec349c9f | 523 | |
d6fff2c5 | 524 | return(stbuf1.st_ctime < stbuf2.st_ctime); |
ec349c9f | 525 | } |
d6fff2c5 | 526 | |
ec349c9f SW |
527 | /* |
528 | * Print usage info and exit. | |
529 | */ | |
530 | usage() | |
531 | { | |
d6fff2c5 KM |
532 | fprintf(stderr,"usage: atq [-c] [-n] [name ...]\n"); |
533 | exit(1); | |
ec349c9f | 534 | } |