xref: /csrg-svn/usr.bin/at/atq/atq.c (revision 16828)
116818Swall #ifndef lint
2*16828Smckusick static char sccsid[] = "@(#)atq.c	1.2	(Berkeley)	08/02/84";
3*16828Smckusick #endif not lint
416818Swall 
516818Swall /*
616818Swall  *
7*16828Smckusick  *	Synopsis:  atq [ -c ] [ -n ] [ name ... ]
816818Swall  *
916818Swall  *
10*16828Smckusick  *	Print the queue of files waiting to be executed. These files
11*16828Smckusick  *	were created by using the "at" command and are located in the
12*16828Smckusick  *	directory "/usr/spool/at".
1316818Swall  *
1416818Swall  *
15*16828Smckusick  *	Author: Steve Wall
16*16828Smckusick  *		Computer Systems Research Group
17*16828Smckusick  *		University of California @ Berkeley
1816818Swall  *
1916818Swall  */
2016818Swall 
2116818Swall # include <stdio.h>
2216818Swall # include <sys/types.h>
2316818Swall # include <sys/file.h>
2416818Swall # include <sys/dir.h>
2516818Swall # include <sys/stat.h>
2616818Swall # include <sys/time.h>
2716818Swall # include <pwd.h>
2816818Swall # include <ctype.h>
2916818Swall 
30*16828Smckusick # define ATDIR		"/usr/spool/at"			/* spooling area */
31*16828Smckusick # define LASTFILE	"/usr/spool/at/lasttimedone"	/* update time record
32*16828Smckusick 							   file */
3316818Swall 
3416818Swall /*
3516818Swall  * Months of the year
3616818Swall  */
3716818Swall static char *mthnames[12] = {
38*16828Smckusick 	"Jan","Feb","Mar","Apr","May","Jun","Jul",
39*16828Smckusick 	"Aug","Sep","Oct","Nov","Dec",
4016818Swall };
4116818Swall 
4216818Swall 
43*16828Smckusick int numentries;				/* number of entries in spooling area */
44*16828Smckusick int namewanted = 0;			/* only print jobs belonging to a
45*16828Smckusick 					   certain person */
46*16828Smckusick struct direct **queue;			/* the queue itself */
4716818Swall 
4816818Swall 
4916818Swall main(argc,argv)
5016818Swall int argc;
5116818Swall char **argv;
5216818Swall {
5316818Swall 
54*16828Smckusick 	int cflag = 0;			/* print in order of creation time */
55*16828Smckusick 	int nflag = 0;			/* just print the number of jobs in
56*16828Smckusick 					   queue */
57*16828Smckusick 	int usage();			/* print usage info and exit */
58*16828Smckusick 	int creation();			/* sort jobs by date of creation */
59*16828Smckusick 	int alphasort();		/* sort jobs by date of execution */
60*16828Smckusick 	int filewanted();		/* should a file be included in queue?*/
61*16828Smckusick 	int printqueue();		/* print the queue */
62*16828Smckusick 	int countfiles();		/* count the number of files in queue
63*16828Smckusick 					   for a given person */
64*16828Smckusick 	char **namelist;		/* array of specific name(s) requested*/
6516818Swall 
6616818Swall 
67*16828Smckusick 	--argc, ++argv;
6816818Swall 
69*16828Smckusick 	/*
70*16828Smckusick 	 * Interpret command line flags if they exist.
71*16828Smckusick 	 */
72*16828Smckusick 	while (argc > 0 && **argv == '-') {
73*16828Smckusick 		(*argv)++;
74*16828Smckusick 		while (**argv) switch (*(*argv)++) {
7516818Swall 
76*16828Smckusick 			case 'c' :	cflag++;
77*16828Smckusick 					break;
7816818Swall 
79*16828Smckusick 			case 'n' :	nflag++;
80*16828Smckusick 					break;
8116818Swall 
82*16828Smckusick 			default	 :	usage();
8316818Swall 
84*16828Smckusick 		}
85*16828Smckusick 		--argc, ++argv;
86*16828Smckusick 	}
8716818Swall 
88*16828Smckusick 	/*
89*16828Smckusick 	 * If a certain name (or names) is requested, set a pointer to the
90*16828Smckusick 	 * beginning of the list.
91*16828Smckusick 	 */
92*16828Smckusick 	if (**argv) {
93*16828Smckusick 		++namewanted;
94*16828Smckusick 		namelist = argv;
95*16828Smckusick 	}
9616818Swall 
97*16828Smckusick 	/*
98*16828Smckusick 	 * Move to the spooling area and scan the directory, placing the
99*16828Smckusick 	 * files in the queue structure. The queue comes back sorted by
100*16828Smckusick 	 * execution time or creation time.
101*16828Smckusick 	 */
102*16828Smckusick 	if (chdir(ATDIR) == -1) {
103*16828Smckusick 		perror(ATDIR);
104*16828Smckusick 		exit(1);
105*16828Smckusick 	}
106*16828Smckusick 	if ((numentries = scandir(".",&queue,filewanted, (cflag) ? creation :
107*16828Smckusick 				alphasort)) < 0) {
108*16828Smckusick 		perror(ATDIR);
109*16828Smckusick 		exit(1);
110*16828Smckusick 	}
11116818Swall 
112*16828Smckusick 	/*
113*16828Smckusick 	 * Either print a message stating:
114*16828Smckusick 	 *
115*16828Smckusick 	 *	1) that the spooling area is empty.
116*16828Smckusick 	 *	2) the number of jobs in the spooling area.
117*16828Smckusick 	 *	3) the number of jobs in the spooling area belonging to
118*16828Smckusick 	 *	   a certain person.
119*16828Smckusick 	 *	4) that the person requested doesn't have any files in the
120*16828Smckusick 	 *	   spooling area.
121*16828Smckusick 	 *
122*16828Smckusick 	 * or send the queue off to "printqueue" for printing.
123*16828Smckusick 	 *
124*16828Smckusick 	 * This whole process might seem a bit elaborate, but it's worthwhile
125*16828Smckusick 	 * to print some informative messages for the user.
126*16828Smckusick 	 *
127*16828Smckusick 	 */
128*16828Smckusick 	if ((numentries == 0) && (!nflag)) {
129*16828Smckusick 		printf("no files in queue.\n");
130*16828Smckusick 		exit(0);
131*16828Smckusick 	}
132*16828Smckusick 	if (nflag) {
133*16828Smckusick 		printf("%d\n",(namewanted) ? countfiles(namelist) : numentries);
134*16828Smckusick 		exit(0);
135*16828Smckusick 	}
136*16828Smckusick 	if ((namewanted) && (countfiles(namelist) == 0)) {
137*16828Smckusick 		printf("no files for %s.\n", (argc == 1) ?
138*16828Smckusick 					*argv : "specified users");
139*16828Smckusick 		exit(0);
140*16828Smckusick 	}
141*16828Smckusick 	printqueue(namelist);
142*16828Smckusick 	exit(0);
14316818Swall }
14416818Swall 
14516818Swall /*
14616818Swall  * Count the number of jobs in the spooling area owned by a certain person(s).
14716818Swall  */
14816818Swall countfiles(namelist)
14916818Swall char **namelist;
15016818Swall {
151*16828Smckusick 	int i;					/* for loop index */
152*16828Smckusick 	int entryfound;				/* found file owned by user(s)*/
153*16828Smckusick 	int numfiles = 0;			/* number of files owned by a
154*16828Smckusick 						   certain person(s) */
155*16828Smckusick 	char **ptr;				/* scratch pointer */
156*16828Smckusick 	struct stat stbuf;			/* buffer for file stats */
157*16828Smckusick 
15816818Swall 
15916818Swall 
160*16828Smckusick 	/*
161*16828Smckusick 	 * For each file in the queue, see if the user(s) own the file. We
162*16828Smckusick 	 * have to use "entryfound" (rather than simply incrementing "numfiles")
163*16828Smckusick 	 * so that if a person's name appears twice on the command line we
164*16828Smckusick 	 * don't double the number of files owned by him/her.
165*16828Smckusick 	 */
166*16828Smckusick 	for (i = 0; i < numentries ; i++) {
167*16828Smckusick 		if ((stat(queue[i]->d_name, &stbuf)) < 0) {
168*16828Smckusick 			continue;
169*16828Smckusick 		}
170*16828Smckusick 		ptr = namelist;
171*16828Smckusick 		entryfound = 0;
17216818Swall 
173*16828Smckusick 		while (*ptr) {
174*16828Smckusick 			if (getid(*ptr) == stbuf.st_uid)
175*16828Smckusick 				++entryfound;
176*16828Smckusick 			++ptr;
177*16828Smckusick 		}
178*16828Smckusick 		if (entryfound)
179*16828Smckusick 			++numfiles;
180*16828Smckusick 	}
181*16828Smckusick 	return(numfiles);
18216818Swall }
18316818Swall 
18416818Swall /*
18516818Swall  * Print the queue. If only jobs belonging to a certain person(s) are requested,
18616818Swall  * only print jobs that belong to that person(s).
18716818Swall  */
18816818Swall printqueue(namelist)
18916818Swall char **namelist;
19016818Swall {
191*16828Smckusick 	int i;					/* for loop index */
192*16828Smckusick 	int rank = 1;				/* rank of a job */
193*16828Smckusick 	int entryfound;				/* found file owned by user(s)*/
194*16828Smckusick 	int printrank();			/* print the rank of a job */
195*16828Smckusick 	int plastrun();				/* print the last time the
196*16828Smckusick 						   spooling area was updated */
197*16828Smckusick 	int getid();				/* get uid of a person */
198*16828Smckusick 	char **ptr;				/* scratch pointer */
199*16828Smckusick 	char *getname();			/* get the login name of a
200*16828Smckusick 						   person using their uid */
201*16828Smckusick 	struct stat stbuf;			/* buffer for file stats */
20216818Swall 
20316818Swall 
204*16828Smckusick 	/*
205*16828Smckusick 	 * Print the time the spooling area was last modified and the header
206*16828Smckusick 	 * for the queue.
207*16828Smckusick 	 */
208*16828Smckusick 	plastrun();
209*16828Smckusick 	printf(" Rank	  Execution Date     Owner     Job #   Job Name\n");
21016818Swall 
211*16828Smckusick 	/*
212*16828Smckusick 	 * Print the queue. If a certain name(s) was requested, print only jobs
213*16828Smckusick 	 * belonging to that person(s), otherwise print the entire queue.
214*16828Smckusick 	 * Once again, we have to use "entryfound" (rather than simply
215*16828Smckusick 	 * comparing each command line argument) so that if a person's name
216*16828Smckusick 	 * appears twice we don't print each file owned by him/her twice.
217*16828Smckusick 	 *
218*16828Smckusick 	 *
219*16828Smckusick 	 * "printrank", "printdate", and "printjobname" all take existing
220*16828Smckusick 	 * data and display it in a friendly manner.
221*16828Smckusick 	 *
222*16828Smckusick 	 */
223*16828Smckusick 	for (i = 0; i < numentries; i++) {
224*16828Smckusick 		if ((stat(queue[i]->d_name, &stbuf)) < 0) {
225*16828Smckusick 			continue;
226*16828Smckusick 		}
227*16828Smckusick 		if (namewanted) {
228*16828Smckusick 			ptr = namelist;
229*16828Smckusick 			entryfound = 0;
23016818Swall 
231*16828Smckusick 			while (*ptr) {
232*16828Smckusick 				if (getid(*ptr) == stbuf.st_uid)
233*16828Smckusick 					++entryfound;
234*16828Smckusick 				++ptr;
235*16828Smckusick 			}
236*16828Smckusick 			if (!entryfound)
237*16828Smckusick 				continue;
238*16828Smckusick 		}
239*16828Smckusick 		printrank(rank++);
240*16828Smckusick 		printdate(queue[i]->d_name);
241*16828Smckusick 		printf("%-10.9s",getname(stbuf.st_uid));
242*16828Smckusick 		printf("%5d",stbuf.st_ino);
243*16828Smckusick 		printjobname(queue[i]->d_name);
244*16828Smckusick 	}
245*16828Smckusick 	++ptr;
24616818Swall }
24716818Swall 
24816818Swall /*
24916818Swall  * Get the full login name of a person using his/her user id.
25016818Swall  */
25116818Swall char *
25216818Swall getname(uid)
25316818Swall int uid;
25416818Swall {
255*16828Smckusick 	struct passwd *pwdinfo;			/* password info structure */
256*16828Smckusick 
25716818Swall 
258*16828Smckusick 	if ((pwdinfo = getpwuid(uid)) == 0) {
259*16828Smckusick 		perror(uid);
260*16828Smckusick 		exit(1);
261*16828Smckusick 	}
262*16828Smckusick 	return(pwdinfo->pw_name);
26316818Swall }
264*16828Smckusick 
26516818Swall /*
26616818Swall  * Get the uid of a person using his/her login name. Return -1 if no
26716818Swall  * such account name exists.
26816818Swall  */
26916818Swall getid(name)
27016818Swall char *name;
27116818Swall {
27216818Swall 
273*16828Smckusick 	struct passwd *pwdinfo;			/* password info structure */
27416818Swall 
27516818Swall 
276*16828Smckusick 	if ((pwdinfo = getpwnam(name)) == 0)
277*16828Smckusick 		return(-1);
27816818Swall 
279*16828Smckusick 	return(pwdinfo->pw_uid);
28016818Swall }
28116818Swall 
28216818Swall /*
28316818Swall  * Print the time the spooling area was updated.
28416818Swall  */
28516818Swall plastrun()
28616818Swall {
287*16828Smckusick 	struct timeval now;			/* time it is right now */
288*16828Smckusick 	struct timezone zone;			/* NOT USED */
289*16828Smckusick 	struct tm *loc;				/* detail of time it is right */
290*16828Smckusick 	u_long lasttime;			/* last update time in seconds
291*16828Smckusick 						   since 1/1/70 */
292*16828Smckusick 	FILE *last;				/* file where last update hour
293*16828Smckusick 						   is stored */
29416818Swall 
29516818Swall 
296*16828Smckusick 	/*
297*16828Smckusick 	 * Open the file where the last update time is stored, and grab the
298*16828Smckusick 	 * last update hour. The update time is measured in seconds since
299*16828Smckusick 	 * 1/1/70.
300*16828Smckusick 	 */
301*16828Smckusick 	if ((last = fopen(LASTFILE,"r")) == NULL) {
302*16828Smckusick 		perror(LASTFILE);
303*16828Smckusick 		exit(1);
304*16828Smckusick 	}
305*16828Smckusick 	fscanf(last,"%d",(u_long) &lasttime);
306*16828Smckusick 	fclose(last);
30716818Swall 
308*16828Smckusick 	/*
309*16828Smckusick 	 * Get a broken down representation of the last update time.
310*16828Smckusick 	 */
311*16828Smckusick 	loc = localtime(&lasttime);
31216818Swall 
313*16828Smckusick 	/*
314*16828Smckusick 	 * Print the time that the spooling area was last updated.
315*16828Smckusick 	 */
316*16828Smckusick 	printf("\n LAST EXECUTION TIME: %s ",mthnames[loc->tm_mon]);
317*16828Smckusick 	printf("%d, 19%d ",loc->tm_mday,loc->tm_year);
318*16828Smckusick 	printf("at %d:%02d\n\n",loc->tm_hour,loc->tm_min);
31916818Swall }
32016818Swall 
32116818Swall /*
32216818Swall  * Print the rank of a job. (I've got to admit it, I stole it from "lpq")
32316818Swall  */
32416818Swall static
32516818Swall printrank(n)
32616818Swall {
327*16828Smckusick 	static char *r[] = {
328*16828Smckusick 		"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
329*16828Smckusick 	};
33016818Swall 
331*16828Smckusick 	if ((n/10) == 1)
332*16828Smckusick 		 printf("%3d%-5s", n,"th");
333*16828Smckusick 	else
334*16828Smckusick 		 printf("%3d%-5s", n, r[n%10]);
33516818Swall }
33616818Swall 
33716818Swall /*
33816818Swall  * Print the date that a job is to be executed. This takes some manipulation
33916818Swall  * of the file name.
34016818Swall  */
34116818Swall printdate(filename)
34216818Swall char *filename;
34316818Swall {
344*16828Smckusick 	int yday  =  0;				/* day of year file will be
345*16828Smckusick 						   executed */
346*16828Smckusick 	int min	  =  0;				/* min. file will be executed */
347*16828Smckusick 	int hour  =  0;				/* hour file will be executed */
348*16828Smckusick 	int day	  =  0;				/* day file will be executed */
349*16828Smckusick 	int month =  0;				/* month file will be executed*/
350*16828Smckusick 	int year  =  0;				/* year file will be executed */
351*16828Smckusick 	int get_mth_day();			/* convert a day of year to a
352*16828Smckusick 						   month and day of month */
353*16828Smckusick 	char date[18];				/* reformatted execution date */
35416818Swall 
355*16828Smckusick 	/*
356*16828Smckusick 	 * Pick off the necessary info from the file name and convert the day
357*16828Smckusick 	 * of year to a month and day of month.
358*16828Smckusick 	 */
359*16828Smckusick 	sscanf(filename,"%2d.%3d.%2d%2d",&year,&yday,&hour,&min);
360*16828Smckusick 	get_mth_day(year,yday,&month,&day);
36116818Swall 
362*16828Smckusick 	/*
363*16828Smckusick 	 * Format the execution date of a job.
364*16828Smckusick 	 */
365*16828Smckusick 	sprintf(date,"%3s %2d, 19%2d %02d:%02d",mthnames[month],
366*16828Smckusick 						    day, year,hour,min);
36716818Swall 
368*16828Smckusick 	/*
369*16828Smckusick 	 * Print the date the job will be executed.
370*16828Smckusick 	 */
371*16828Smckusick 	printf("%-21.18s",date);
37216818Swall }
37316818Swall 
37416818Swall /*
37516818Swall  * Given a day of the year, calculate the month and day of month.
37616818Swall  */
37716818Swall get_mth_day(year,dayofyear,month,day)
37816818Swall int year, dayofyear, *month, *day;
37916818Swall 
38016818Swall {
38116818Swall 
382*16828Smckusick 	int i = 1;				/* for loop index */
383*16828Smckusick 	int leap;				/* are we dealing with a leap
384*16828Smckusick 						   year? */
385*16828Smckusick 						/* Table of the number of days
386*16828Smckusick 						   in each month of the year.
38716818Swall 
388*16828Smckusick 						     dofy_tab[1] -- regular year
389*16828Smckusick 						     dofy_tab[2] -- leap year
390*16828Smckusick 									      */
39116818Swall 
392*16828Smckusick 	static int dofy_tab[2][13] = {
393*16828Smckusick 		{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
394*16828Smckusick 		{ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
395*16828Smckusick 	};
39616818Swall 
397*16828Smckusick 	/*
398*16828Smckusick 	 * Are we dealing with a leap year?
399*16828Smckusick 	 */
400*16828Smckusick 	leap = ((year%4 == 0 && year%100 != 0) || year%100 == 0);
40116818Swall 
402*16828Smckusick 	/*
403*16828Smckusick 	 * Calculate the month of the year and day of the month.
404*16828Smckusick 	 */
405*16828Smckusick 	while (dayofyear >= dofy_tab[leap][i]) {
406*16828Smckusick 		dayofyear -= dofy_tab[leap][i++];
407*16828Smckusick 		++(*month);
408*16828Smckusick 	}
409*16828Smckusick 	*day = (dayofyear + 1);
41016818Swall }
411*16828Smckusick 
41216818Swall /*
41316818Swall  * Print a job name. If the old "at" has been used to create the spoolfile,
41416818Swall  * the three line header that the new version of "at" puts in the spoolfile.
41516818Swall  * Thus, we just print "???".
41616818Swall  */
41716818Swall printjobname(file)
41816818Swall char *file;
41916818Swall {
420*16828Smckusick 	char *ch;				/* char ptr */
421*16828Smckusick 	char jobname[80];			/* the job name */
422*16828Smckusick 	FILE *filename;				/* job file in spooling area */
42316818Swall 
424*16828Smckusick 	/*
425*16828Smckusick 	 * Open the job file and grab the first line.
426*16828Smckusick 	 */
427*16828Smckusick 	printf("   ");
42816818Swall 
429*16828Smckusick 	if ((filename = fopen(file,"r")) == NULL) {
430*16828Smckusick 		printf("%.27s\n", "???");
431*16828Smckusick 		fclose(filename);
432*16828Smckusick 		return;
433*16828Smckusick 	}
434*16828Smckusick 	if (fscanf(filename,"# jobname: %s",jobname) != 1) {
435*16828Smckusick 		printf("%.27s\n", "???");
436*16828Smckusick 		fclose(filename);
437*16828Smckusick 		return;
438*16828Smckusick 	}
439*16828Smckusick 	fclose(filename);
44016818Swall 
441*16828Smckusick 	/*
442*16828Smckusick 	 * Put a pointer at the begining of the line and remove the basename
443*16828Smckusick 	 * from the job file.
444*16828Smckusick 	 */
445*16828Smckusick 	ch = jobname;
446*16828Smckusick 	if ((ch = (char *)rindex(jobname,'/')) != 0)
447*16828Smckusick 		++ch;
448*16828Smckusick 	else
449*16828Smckusick 		ch = jobname;
45016818Swall 
451*16828Smckusick 	printf("%.27s\n",ch);
45216818Swall }
45316818Swall 
45416818Swall /*
45516818Swall  * Do we want to include a file in the queue? (used by "scandir") We are looking
45616818Swall  * for files with following syntax: yy.ddd.hhhh. so the test is made to see if
45716818Swall  * the file name has three dots in it. This test will suffice since the only
45816818Swall  * other files in /usr/spool/at don't have any dots in their name.
45916818Swall  */
46016818Swall filewanted(direntry)
46116818Swall struct direct *direntry;
46216818Swall {
463*16828Smckusick 	int numdot = 0;
464*16828Smckusick 	char *filename;
46516818Swall 
466*16828Smckusick 	filename = direntry->d_name;
467*16828Smckusick 	while (*filename)
468*16828Smckusick 		numdot += (*(filename++) == '.');
469*16828Smckusick 	return(numdot == 3);
47016818Swall }
47116818Swall 
47216818Swall /*
47316818Swall  * Sort files by time of creation. (used by "scandir")
47416818Swall  */
47516818Swall creation(d1, d2)
47616818Swall struct direct **d1, **d2;
47716818Swall {
478*16828Smckusick 	struct stat stbuf1, stbuf2;
47916818Swall 
480*16828Smckusick 	if (stat((*d1)->d_name,&stbuf1) < 0)
481*16828Smckusick 		return(1);
48216818Swall 
483*16828Smckusick 	if (stat((*d2)->d_name,&stbuf2) < 0)
484*16828Smckusick 		return(1);
48516818Swall 
486*16828Smckusick 	return(stbuf1.st_ctime < stbuf2.st_ctime);
48716818Swall }
488*16828Smckusick 
48916818Swall /*
49016818Swall  * Print usage info and exit.
49116818Swall  */
49216818Swall usage()
49316818Swall {
494*16828Smckusick 	fprintf(stderr,"usage:	atq [-c] [-n] [name ...]\n");
495*16828Smckusick 	exit(1);
49616818Swall }
497