xref: /original-bsd/usr.bin/at/atq/atq.c (revision c3e32dec)
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 
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  */
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  */
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  */
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  */
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  */
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
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  */
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  */
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  */
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  */
530 usage()
531 {
532 	fprintf(stderr,"usage:	atq [-c] [-n] [name ...]\n");
533 	exit(1);
534 }
535