xref: /csrg-svn/usr.bin/at/atq/atq.c (revision 16865)
116818Swall #ifndef lint
2*16865Swall static char sccsid[] = "@(#)atq.c	1.3	(Berkeley)	08/09/84";
316828Smckusick #endif not lint
416818Swall 
516818Swall /*
616818Swall  *
716828Smckusick  *	Synopsis:  atq [ -c ] [ -n ] [ name ... ]
816818Swall  *
916818Swall  *
1016828Smckusick  *	Print the queue of files waiting to be executed. These files
1116828Smckusick  *	were created by using the "at" command and are located in the
1216828Smckusick  *	directory "/usr/spool/at".
1316818Swall  *
1416818Swall  *
1516828Smckusick  *	Author: Steve Wall
1616828Smckusick  *		Computer Systems Research Group
1716828Smckusick  *		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 
3016828Smckusick # define ATDIR		"/usr/spool/at"			/* spooling area */
3116828Smckusick # define LASTFILE	"/usr/spool/at/lasttimedone"	/* update time record
3216828Smckusick 							   file */
3316818Swall 
3416818Swall /*
3516818Swall  * Months of the year
3616818Swall  */
3716818Swall static char *mthnames[12] = {
3816828Smckusick 	"Jan","Feb","Mar","Apr","May","Jun","Jul",
3916828Smckusick 	"Aug","Sep","Oct","Nov","Dec",
4016818Swall };
4116818Swall 
4216818Swall 
4316828Smckusick int numentries;				/* number of entries in spooling area */
4416828Smckusick int namewanted = 0;			/* only print jobs belonging to a
4516828Smckusick 					   certain person */
4616828Smckusick struct direct **queue;			/* the queue itself */
4716818Swall 
4816818Swall 
4916818Swall main(argc,argv)
5016818Swall int argc;
5116818Swall char **argv;
5216818Swall {
5316818Swall 
5416828Smckusick 	int cflag = 0;			/* print in order of creation time */
5516828Smckusick 	int nflag = 0;			/* just print the number of jobs in
5616828Smckusick 					   queue */
5716828Smckusick 	int usage();			/* print usage info and exit */
5816828Smckusick 	int creation();			/* sort jobs by date of creation */
5916828Smckusick 	int alphasort();		/* sort jobs by date of execution */
6016828Smckusick 	int filewanted();		/* should a file be included in queue?*/
6116828Smckusick 	int printqueue();		/* print the queue */
6216828Smckusick 	int countfiles();		/* count the number of files in queue
6316828Smckusick 					   for a given person */
6416828Smckusick 	char **namelist;		/* array of specific name(s) requested*/
6516818Swall 
6616818Swall 
6716828Smckusick 	--argc, ++argv;
6816818Swall 
6916828Smckusick 	/*
7016828Smckusick 	 * Interpret command line flags if they exist.
7116828Smckusick 	 */
7216828Smckusick 	while (argc > 0 && **argv == '-') {
7316828Smckusick 		(*argv)++;
7416828Smckusick 		while (**argv) switch (*(*argv)++) {
7516818Swall 
7616828Smckusick 			case 'c' :	cflag++;
7716828Smckusick 					break;
7816818Swall 
7916828Smckusick 			case 'n' :	nflag++;
8016828Smckusick 					break;
8116818Swall 
8216828Smckusick 			default	 :	usage();
8316818Swall 
8416828Smckusick 		}
8516828Smckusick 		--argc, ++argv;
8616828Smckusick 	}
8716818Swall 
8816828Smckusick 	/*
8916828Smckusick 	 * If a certain name (or names) is requested, set a pointer to the
9016828Smckusick 	 * beginning of the list.
9116828Smckusick 	 */
9216828Smckusick 	if (**argv) {
9316828Smckusick 		++namewanted;
9416828Smckusick 		namelist = argv;
9516828Smckusick 	}
9616818Swall 
9716828Smckusick 	/*
9816828Smckusick 	 * Move to the spooling area and scan the directory, placing the
9916828Smckusick 	 * files in the queue structure. The queue comes back sorted by
10016828Smckusick 	 * execution time or creation time.
10116828Smckusick 	 */
10216828Smckusick 	if (chdir(ATDIR) == -1) {
10316828Smckusick 		perror(ATDIR);
10416828Smckusick 		exit(1);
10516828Smckusick 	}
10616828Smckusick 	if ((numentries = scandir(".",&queue,filewanted, (cflag) ? creation :
10716828Smckusick 				alphasort)) < 0) {
10816828Smckusick 		perror(ATDIR);
10916828Smckusick 		exit(1);
11016828Smckusick 	}
11116818Swall 
11216828Smckusick 	/*
11316828Smckusick 	 * Either print a message stating:
11416828Smckusick 	 *
11516828Smckusick 	 *	1) that the spooling area is empty.
11616828Smckusick 	 *	2) the number of jobs in the spooling area.
11716828Smckusick 	 *	3) the number of jobs in the spooling area belonging to
11816828Smckusick 	 *	   a certain person.
11916828Smckusick 	 *	4) that the person requested doesn't have any files in the
12016828Smckusick 	 *	   spooling area.
12116828Smckusick 	 *
12216828Smckusick 	 * or send the queue off to "printqueue" for printing.
12316828Smckusick 	 *
12416828Smckusick 	 * This whole process might seem a bit elaborate, but it's worthwhile
12516828Smckusick 	 * to print some informative messages for the user.
12616828Smckusick 	 *
12716828Smckusick 	 */
12816828Smckusick 	if ((numentries == 0) && (!nflag)) {
12916828Smckusick 		printf("no files in queue.\n");
13016828Smckusick 		exit(0);
13116828Smckusick 	}
13216828Smckusick 	if (nflag) {
13316828Smckusick 		printf("%d\n",(namewanted) ? countfiles(namelist) : numentries);
13416828Smckusick 		exit(0);
13516828Smckusick 	}
13616828Smckusick 	if ((namewanted) && (countfiles(namelist) == 0)) {
13716828Smckusick 		printf("no files for %s.\n", (argc == 1) ?
13816828Smckusick 					*argv : "specified users");
13916828Smckusick 		exit(0);
14016828Smckusick 	}
14116828Smckusick 	printqueue(namelist);
14216828Smckusick 	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 {
15116828Smckusick 	int i;					/* for loop index */
15216828Smckusick 	int entryfound;				/* found file owned by user(s)*/
15316828Smckusick 	int numfiles = 0;			/* number of files owned by a
15416828Smckusick 						   certain person(s) */
15516828Smckusick 	char **ptr;				/* scratch pointer */
15616818Swall 
15716818Swall 
15816828Smckusick 	/*
15916828Smckusick 	 * For each file in the queue, see if the user(s) own the file. We
16016828Smckusick 	 * have to use "entryfound" (rather than simply incrementing "numfiles")
16116828Smckusick 	 * so that if a person's name appears twice on the command line we
16216828Smckusick 	 * don't double the number of files owned by him/her.
16316828Smckusick 	 */
16416828Smckusick 	for (i = 0; i < numentries ; i++) {
16516828Smckusick 		ptr = namelist;
16616828Smckusick 		entryfound = 0;
16716818Swall 
16816828Smckusick 		while (*ptr) {
169*16865Swall 			if (isowner(*ptr,queue[i]->d_name))
17016828Smckusick 				++entryfound;
17116828Smckusick 			++ptr;
17216828Smckusick 		}
17316828Smckusick 		if (entryfound)
17416828Smckusick 			++numfiles;
17516828Smckusick 	}
17616828Smckusick 	return(numfiles);
17716818Swall }
17816818Swall 
17916818Swall /*
18016818Swall  * Print the queue. If only jobs belonging to a certain person(s) are requested,
18116818Swall  * only print jobs that belong to that person(s).
18216818Swall  */
18316818Swall printqueue(namelist)
18416818Swall char **namelist;
18516818Swall {
18616828Smckusick 	int i;					/* for loop index */
18716828Smckusick 	int rank = 1;				/* rank of a job */
18816828Smckusick 	int entryfound;				/* found file owned by user(s)*/
18916828Smckusick 	int printrank();			/* print the rank of a job */
19016828Smckusick 	int plastrun();				/* print the last time the
19116828Smckusick 						   spooling area was updated */
192*16865Swall 	int powner();				/* print the name of the owner
193*16865Swall 						   of the job */
19416828Smckusick 	int getid();				/* get uid of a person */
19516828Smckusick 	char **ptr;				/* scratch pointer */
19616828Smckusick 	struct stat stbuf;			/* buffer for file stats */
19716818Swall 
19816818Swall 
19916828Smckusick 	/*
20016828Smckusick 	 * Print the time the spooling area was last modified and the header
20116828Smckusick 	 * for the queue.
20216828Smckusick 	 */
20316828Smckusick 	plastrun();
20416828Smckusick 	printf(" Rank	  Execution Date     Owner     Job #   Job Name\n");
20516818Swall 
20616828Smckusick 	/*
20716828Smckusick 	 * Print the queue. If a certain name(s) was requested, print only jobs
20816828Smckusick 	 * belonging to that person(s), otherwise print the entire queue.
20916828Smckusick 	 * Once again, we have to use "entryfound" (rather than simply
21016828Smckusick 	 * comparing each command line argument) so that if a person's name
21116828Smckusick 	 * appears twice we don't print each file owned by him/her twice.
21216828Smckusick 	 *
21316828Smckusick 	 *
21416828Smckusick 	 * "printrank", "printdate", and "printjobname" all take existing
21516828Smckusick 	 * data and display it in a friendly manner.
21616828Smckusick 	 *
21716828Smckusick 	 */
21816828Smckusick 	for (i = 0; i < numentries; i++) {
21916828Smckusick 		if ((stat(queue[i]->d_name, &stbuf)) < 0) {
22016828Smckusick 			continue;
22116828Smckusick 		}
22216828Smckusick 		if (namewanted) {
22316828Smckusick 			ptr = namelist;
22416828Smckusick 			entryfound = 0;
22516818Swall 
22616828Smckusick 			while (*ptr) {
227*16865Swall 				if (isowner(*ptr,queue[i]->d_name))
22816828Smckusick 					++entryfound;
22916828Smckusick 				++ptr;
23016828Smckusick 			}
23116828Smckusick 			if (!entryfound)
23216828Smckusick 				continue;
23316828Smckusick 		}
23416828Smckusick 		printrank(rank++);
23516828Smckusick 		printdate(queue[i]->d_name);
236*16865Swall 		powner(queue[i]->d_name);
23716828Smckusick 		printf("%5d",stbuf.st_ino);
23816828Smckusick 		printjobname(queue[i]->d_name);
23916828Smckusick 	}
24016828Smckusick 	++ptr;
24116818Swall }
24216818Swall 
24316818Swall /*
244*16865Swall  * See if "name" owns "job".
24516818Swall  */
246*16865Swall isowner(name,job)
247*16865Swall char *name;
248*16865Swall char *job;
24916818Swall {
250*16865Swall 	char buf[30];			/* buffer for 1st line of spoolfile
251*16865Swall 					   header */
252*16865Swall 	FILE *infile;			/* I/O stream to spoolfile */
25316818Swall 
254*16865Swall 	if ((infile = fopen(job,"r")) == NULL) {
255*16865Swall 		fprintf(stderr,"Couldn't open spoolfile");
256*16865Swall 		perror(job);
257*16865Swall 		return(0);
25816828Smckusick 	}
259*16865Swall 
260*16865Swall 	if (fscanf(infile,"# owner: %s\n",buf) != 1) {
261*16865Swall 		fclose(infile);
262*16865Swall 		return(0);
263*16865Swall 	}
264*16865Swall 
265*16865Swall 	fclose(infile);
266*16865Swall 	return((strcmp(name,buf) == 0) ? 1 : 0);
26716818Swall }
268*16865Swall 
269*16865Swall /*
270*16865Swall  * Print the owner of the job. This is stored on the first line of the
271*16865Swall  * spoolfile. If we run into trouble getting the name, we'll just print "???".
272*16865Swall  */
273*16865Swall powner(file)
274*16865Swall char *file;
275*16865Swall {
276*16865Swall 	char owner[80];				/* the owner */
277*16865Swall 	FILE *infile;				/* I/O stream to spoolfile */
278*16865Swall 
279*16865Swall 	/*
280*16865Swall 	 * Open the job file and grab the first line.
281*16865Swall 	 */
282*16865Swall 
283*16865Swall 	if ((infile = fopen(file,"r")) == NULL) {
284*16865Swall 		printf("%-10.9s","???");
285*16865Swall 		return;
286*16865Swall 	}
287*16865Swall 
288*16865Swall 	if (fscanf(infile,"# owner: %s",owner) != 1) {
289*16865Swall 		printf("%-10.9s","???");
290*16865Swall 		fclose(infile);
291*16865Swall 		return;
292*16865Swall 	}
293*16865Swall 
294*16865Swall 	fclose(infile);
295*16865Swall 	printf("%-10.9s",owner);
296*16865Swall 
297*16865Swall }
29816828Smckusick 
299*16865Swall 
30016818Swall /*
30116818Swall  * Get the uid of a person using his/her login name. Return -1 if no
30216818Swall  * such account name exists.
30316818Swall  */
30416818Swall getid(name)
30516818Swall char *name;
30616818Swall {
30716818Swall 
30816828Smckusick 	struct passwd *pwdinfo;			/* password info structure */
30916818Swall 
31016818Swall 
31116828Smckusick 	if ((pwdinfo = getpwnam(name)) == 0)
31216828Smckusick 		return(-1);
31316818Swall 
31416828Smckusick 	return(pwdinfo->pw_uid);
31516818Swall }
31616818Swall 
31716818Swall /*
31816818Swall  * Print the time the spooling area was updated.
31916818Swall  */
32016818Swall plastrun()
32116818Swall {
32216828Smckusick 	struct timeval now;			/* time it is right now */
32316828Smckusick 	struct timezone zone;			/* NOT USED */
32416828Smckusick 	struct tm *loc;				/* detail of time it is right */
32516828Smckusick 	u_long lasttime;			/* last update time in seconds
32616828Smckusick 						   since 1/1/70 */
32716828Smckusick 	FILE *last;				/* file where last update hour
32816828Smckusick 						   is stored */
32916818Swall 
33016818Swall 
33116828Smckusick 	/*
33216828Smckusick 	 * Open the file where the last update time is stored, and grab the
33316828Smckusick 	 * last update hour. The update time is measured in seconds since
33416828Smckusick 	 * 1/1/70.
33516828Smckusick 	 */
33616828Smckusick 	if ((last = fopen(LASTFILE,"r")) == NULL) {
33716828Smckusick 		perror(LASTFILE);
33816828Smckusick 		exit(1);
33916828Smckusick 	}
34016828Smckusick 	fscanf(last,"%d",(u_long) &lasttime);
34116828Smckusick 	fclose(last);
34216818Swall 
34316828Smckusick 	/*
34416828Smckusick 	 * Get a broken down representation of the last update time.
34516828Smckusick 	 */
34616828Smckusick 	loc = localtime(&lasttime);
34716818Swall 
34816828Smckusick 	/*
34916828Smckusick 	 * Print the time that the spooling area was last updated.
35016828Smckusick 	 */
35116828Smckusick 	printf("\n LAST EXECUTION TIME: %s ",mthnames[loc->tm_mon]);
35216828Smckusick 	printf("%d, 19%d ",loc->tm_mday,loc->tm_year);
35316828Smckusick 	printf("at %d:%02d\n\n",loc->tm_hour,loc->tm_min);
35416818Swall }
35516818Swall 
35616818Swall /*
35716818Swall  * Print the rank of a job. (I've got to admit it, I stole it from "lpq")
35816818Swall  */
35916818Swall static
36016818Swall printrank(n)
36116818Swall {
36216828Smckusick 	static char *r[] = {
36316828Smckusick 		"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
36416828Smckusick 	};
36516818Swall 
36616828Smckusick 	if ((n/10) == 1)
36716828Smckusick 		 printf("%3d%-5s", n,"th");
36816828Smckusick 	else
36916828Smckusick 		 printf("%3d%-5s", n, r[n%10]);
37016818Swall }
37116818Swall 
37216818Swall /*
37316818Swall  * Print the date that a job is to be executed. This takes some manipulation
37416818Swall  * of the file name.
37516818Swall  */
37616818Swall printdate(filename)
37716818Swall char *filename;
37816818Swall {
37916828Smckusick 	int yday  =  0;				/* day of year file will be
38016828Smckusick 						   executed */
38116828Smckusick 	int min	  =  0;				/* min. file will be executed */
38216828Smckusick 	int hour  =  0;				/* hour file will be executed */
38316828Smckusick 	int day	  =  0;				/* day file will be executed */
38416828Smckusick 	int month =  0;				/* month file will be executed*/
38516828Smckusick 	int year  =  0;				/* year file will be executed */
38616828Smckusick 	int get_mth_day();			/* convert a day of year to a
38716828Smckusick 						   month and day of month */
38816828Smckusick 	char date[18];				/* reformatted execution date */
38916818Swall 
39016828Smckusick 	/*
39116828Smckusick 	 * Pick off the necessary info from the file name and convert the day
39216828Smckusick 	 * of year to a month and day of month.
39316828Smckusick 	 */
39416828Smckusick 	sscanf(filename,"%2d.%3d.%2d%2d",&year,&yday,&hour,&min);
39516828Smckusick 	get_mth_day(year,yday,&month,&day);
39616818Swall 
39716828Smckusick 	/*
39816828Smckusick 	 * Format the execution date of a job.
39916828Smckusick 	 */
40016828Smckusick 	sprintf(date,"%3s %2d, 19%2d %02d:%02d",mthnames[month],
40116828Smckusick 						    day, year,hour,min);
40216818Swall 
40316828Smckusick 	/*
40416828Smckusick 	 * Print the date the job will be executed.
40516828Smckusick 	 */
40616828Smckusick 	printf("%-21.18s",date);
40716818Swall }
40816818Swall 
40916818Swall /*
41016818Swall  * Given a day of the year, calculate the month and day of month.
41116818Swall  */
41216818Swall get_mth_day(year,dayofyear,month,day)
41316818Swall int year, dayofyear, *month, *day;
41416818Swall 
41516818Swall {
41616818Swall 
41716828Smckusick 	int i = 1;				/* for loop index */
41816828Smckusick 	int leap;				/* are we dealing with a leap
41916828Smckusick 						   year? */
42016828Smckusick 						/* Table of the number of days
42116828Smckusick 						   in each month of the year.
42216818Swall 
42316828Smckusick 						     dofy_tab[1] -- regular year
42416828Smckusick 						     dofy_tab[2] -- leap year
42516828Smckusick 									      */
42616818Swall 
42716828Smckusick 	static int dofy_tab[2][13] = {
42816828Smckusick 		{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
42916828Smckusick 		{ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
43016828Smckusick 	};
43116818Swall 
43216828Smckusick 	/*
43316828Smckusick 	 * Are we dealing with a leap year?
43416828Smckusick 	 */
43516828Smckusick 	leap = ((year%4 == 0 && year%100 != 0) || year%100 == 0);
43616818Swall 
43716828Smckusick 	/*
43816828Smckusick 	 * Calculate the month of the year and day of the month.
43916828Smckusick 	 */
44016828Smckusick 	while (dayofyear >= dofy_tab[leap][i]) {
44116828Smckusick 		dayofyear -= dofy_tab[leap][i++];
44216828Smckusick 		++(*month);
44316828Smckusick 	}
44416828Smckusick 	*day = (dayofyear + 1);
44516818Swall }
44616828Smckusick 
44716818Swall /*
44816818Swall  * Print a job name. If the old "at" has been used to create the spoolfile,
44916818Swall  * the three line header that the new version of "at" puts in the spoolfile.
45016818Swall  * Thus, we just print "???".
45116818Swall  */
45216818Swall printjobname(file)
45316818Swall char *file;
45416818Swall {
455*16865Swall 	char *ptr;				/* scratch pointer */
45616828Smckusick 	char jobname[80];			/* the job name */
45716828Smckusick 	FILE *filename;				/* job file in spooling area */
45816818Swall 
45916828Smckusick 	/*
460*16865Swall 	 * Open the job file and grab the second line.
46116828Smckusick 	 */
46216828Smckusick 	printf("   ");
46316818Swall 
46416828Smckusick 	if ((filename = fopen(file,"r")) == NULL) {
46516828Smckusick 		printf("%.27s\n", "???");
46616828Smckusick 		return;
46716828Smckusick 	}
468*16865Swall 	/*
469*16865Swall 	 * We'll yank the first line into the buffer temporarily.
470*16865Swall 	 */
471*16865Swall 	fgets(jobname,80,filename);
472*16865Swall 
473*16865Swall 	/*
474*16865Swall 	 * Now get the job name.
475*16865Swall 	 */
47616828Smckusick 	if (fscanf(filename,"# jobname: %s",jobname) != 1) {
47716828Smckusick 		printf("%.27s\n", "???");
47816828Smckusick 		fclose(filename);
47916828Smckusick 		return;
48016828Smckusick 	}
48116828Smckusick 	fclose(filename);
48216818Swall 
48316828Smckusick 	/*
48416828Smckusick 	 * Put a pointer at the begining of the line and remove the basename
48516828Smckusick 	 * from the job file.
48616828Smckusick 	 */
487*16865Swall 	ptr = jobname;
488*16865Swall 	if ((ptr = (char *)rindex(jobname,'/')) != 0)
489*16865Swall 		++ptr;
49016828Smckusick 	else
491*16865Swall 		ptr = jobname;
49216818Swall 
493*16865Swall 	if (strlen(ptr) > 23)
494*16865Swall 		printf("%.23s ...\n",ptr);
495*16865Swall 	else
496*16865Swall 		printf("%.27s\n",ptr);
49716818Swall }
49816818Swall 
49916818Swall /*
50016818Swall  * Do we want to include a file in the queue? (used by "scandir") We are looking
50116818Swall  * for files with following syntax: yy.ddd.hhhh. so the test is made to see if
50216818Swall  * the file name has three dots in it. This test will suffice since the only
50316818Swall  * other files in /usr/spool/at don't have any dots in their name.
50416818Swall  */
50516818Swall filewanted(direntry)
50616818Swall struct direct *direntry;
50716818Swall {
50816828Smckusick 	int numdot = 0;
50916828Smckusick 	char *filename;
51016818Swall 
51116828Smckusick 	filename = direntry->d_name;
51216828Smckusick 	while (*filename)
51316828Smckusick 		numdot += (*(filename++) == '.');
51416828Smckusick 	return(numdot == 3);
51516818Swall }
51616818Swall 
51716818Swall /*
51816818Swall  * Sort files by time of creation. (used by "scandir")
51916818Swall  */
52016818Swall creation(d1, d2)
52116818Swall struct direct **d1, **d2;
52216818Swall {
52316828Smckusick 	struct stat stbuf1, stbuf2;
52416818Swall 
52516828Smckusick 	if (stat((*d1)->d_name,&stbuf1) < 0)
52616828Smckusick 		return(1);
52716818Swall 
52816828Smckusick 	if (stat((*d2)->d_name,&stbuf2) < 0)
52916828Smckusick 		return(1);
53016818Swall 
53116828Smckusick 	return(stbuf1.st_ctime < stbuf2.st_ctime);
53216818Swall }
53316828Smckusick 
53416818Swall /*
53516818Swall  * Print usage info and exit.
53616818Swall  */
53716818Swall usage()
53816818Swall {
53916828Smckusick 	fprintf(stderr,"usage:	atq [-c] [-n] [name ...]\n");
54016828Smckusick 	exit(1);
54116818Swall }
542