xref: /csrg-svn/usr.bin/at/atq/atq.c (revision 22419)
1*22419Sdist /*
2*22419Sdist  * Copyright (c) 1983 Regents of the University of California.
3*22419Sdist  * All rights reserved.  The Berkeley software License Agreement
4*22419Sdist  * specifies the terms and conditions for redistribution.
5*22419Sdist  */
6*22419Sdist 
716818Swall #ifndef lint
8*22419Sdist char copyright[] =
9*22419Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\
10*22419Sdist  All rights reserved.\n";
1116828Smckusick #endif not lint
1216818Swall 
13*22419Sdist #ifndef lint
14*22419Sdist static char sccsid[] = "@(#)atq.c	5.1 (Berkeley) 06/06/85";
15*22419Sdist #endif not lint
16*22419Sdist 
1716818Swall /*
1816818Swall  *
1916828Smckusick  *	Synopsis:  atq [ -c ] [ -n ] [ name ... ]
2016818Swall  *
2116818Swall  *
2216828Smckusick  *	Print the queue of files waiting to be executed. These files
2316828Smckusick  *	were created by using the "at" command and are located in the
2416828Smckusick  *	directory "/usr/spool/at".
2516818Swall  *
2616818Swall  *
2716828Smckusick  *	Author: Steve Wall
2816828Smckusick  *		Computer Systems Research Group
2916828Smckusick  *		University of California @ Berkeley
3016818Swall  *
3116818Swall  */
3216818Swall 
3316818Swall # include <stdio.h>
3416818Swall # include <sys/types.h>
3516818Swall # include <sys/file.h>
3616818Swall # include <sys/dir.h>
3716818Swall # include <sys/stat.h>
3816818Swall # include <sys/time.h>
3916818Swall # include <pwd.h>
4016818Swall # include <ctype.h>
4116818Swall 
4216828Smckusick # define ATDIR		"/usr/spool/at"			/* spooling area */
4316828Smckusick # define LASTFILE	"/usr/spool/at/lasttimedone"	/* update time record
4416828Smckusick 							   file */
4516818Swall 
4616818Swall /*
4716818Swall  * Months of the year
4816818Swall  */
4916818Swall static char *mthnames[12] = {
5016828Smckusick 	"Jan","Feb","Mar","Apr","May","Jun","Jul",
5116828Smckusick 	"Aug","Sep","Oct","Nov","Dec",
5216818Swall };
5316818Swall 
5416818Swall 
5516828Smckusick int numentries;				/* number of entries in spooling area */
5616828Smckusick int namewanted = 0;			/* only print jobs belonging to a
5716828Smckusick 					   certain person */
5816828Smckusick struct direct **queue;			/* the queue itself */
5916818Swall 
6016818Swall 
6116818Swall main(argc,argv)
6216818Swall int argc;
6316818Swall char **argv;
6416818Swall {
6516818Swall 
6616828Smckusick 	int cflag = 0;			/* print in order of creation time */
6716828Smckusick 	int nflag = 0;			/* just print the number of jobs in
6816828Smckusick 					   queue */
6916828Smckusick 	int usage();			/* print usage info and exit */
7016828Smckusick 	int creation();			/* sort jobs by date of creation */
7116828Smckusick 	int alphasort();		/* sort jobs by date of execution */
7216828Smckusick 	int filewanted();		/* should a file be included in queue?*/
7316828Smckusick 	int printqueue();		/* print the queue */
7416828Smckusick 	int countfiles();		/* count the number of files in queue
7516828Smckusick 					   for a given person */
7616828Smckusick 	char **namelist;		/* array of specific name(s) requested*/
7716818Swall 
7816818Swall 
7916828Smckusick 	--argc, ++argv;
8016818Swall 
8116828Smckusick 	/*
8216828Smckusick 	 * Interpret command line flags if they exist.
8316828Smckusick 	 */
8416828Smckusick 	while (argc > 0 && **argv == '-') {
8516828Smckusick 		(*argv)++;
8616828Smckusick 		while (**argv) switch (*(*argv)++) {
8716818Swall 
8816828Smckusick 			case 'c' :	cflag++;
8916828Smckusick 					break;
9016818Swall 
9116828Smckusick 			case 'n' :	nflag++;
9216828Smckusick 					break;
9316818Swall 
9416828Smckusick 			default	 :	usage();
9516818Swall 
9616828Smckusick 		}
9716828Smckusick 		--argc, ++argv;
9816828Smckusick 	}
9916818Swall 
10016828Smckusick 	/*
10116828Smckusick 	 * If a certain name (or names) is requested, set a pointer to the
10216828Smckusick 	 * beginning of the list.
10316828Smckusick 	 */
10416828Smckusick 	if (**argv) {
10516828Smckusick 		++namewanted;
10616828Smckusick 		namelist = argv;
10716828Smckusick 	}
10816818Swall 
10916828Smckusick 	/*
11016828Smckusick 	 * Move to the spooling area and scan the directory, placing the
11116828Smckusick 	 * files in the queue structure. The queue comes back sorted by
11216828Smckusick 	 * execution time or creation time.
11316828Smckusick 	 */
11416828Smckusick 	if (chdir(ATDIR) == -1) {
11516828Smckusick 		perror(ATDIR);
11616828Smckusick 		exit(1);
11716828Smckusick 	}
11816828Smckusick 	if ((numentries = scandir(".",&queue,filewanted, (cflag) ? creation :
11916828Smckusick 				alphasort)) < 0) {
12016828Smckusick 		perror(ATDIR);
12116828Smckusick 		exit(1);
12216828Smckusick 	}
12316818Swall 
12416828Smckusick 	/*
12516828Smckusick 	 * Either print a message stating:
12616828Smckusick 	 *
12716828Smckusick 	 *	1) that the spooling area is empty.
12816828Smckusick 	 *	2) the number of jobs in the spooling area.
12916828Smckusick 	 *	3) the number of jobs in the spooling area belonging to
13016828Smckusick 	 *	   a certain person.
13116828Smckusick 	 *	4) that the person requested doesn't have any files in the
13216828Smckusick 	 *	   spooling area.
13316828Smckusick 	 *
13416828Smckusick 	 * or send the queue off to "printqueue" for printing.
13516828Smckusick 	 *
13616828Smckusick 	 * This whole process might seem a bit elaborate, but it's worthwhile
13716828Smckusick 	 * to print some informative messages for the user.
13816828Smckusick 	 *
13916828Smckusick 	 */
14016828Smckusick 	if ((numentries == 0) && (!nflag)) {
14116828Smckusick 		printf("no files in queue.\n");
14216828Smckusick 		exit(0);
14316828Smckusick 	}
14416828Smckusick 	if (nflag) {
14516828Smckusick 		printf("%d\n",(namewanted) ? countfiles(namelist) : numentries);
14616828Smckusick 		exit(0);
14716828Smckusick 	}
14816828Smckusick 	if ((namewanted) && (countfiles(namelist) == 0)) {
14916828Smckusick 		printf("no files for %s.\n", (argc == 1) ?
15016828Smckusick 					*argv : "specified users");
15116828Smckusick 		exit(0);
15216828Smckusick 	}
15316828Smckusick 	printqueue(namelist);
15416828Smckusick 	exit(0);
15516818Swall }
15616818Swall 
15716818Swall /*
15816818Swall  * Count the number of jobs in the spooling area owned by a certain person(s).
15916818Swall  */
16016818Swall countfiles(namelist)
16116818Swall char **namelist;
16216818Swall {
16316828Smckusick 	int i;					/* for loop index */
16416828Smckusick 	int entryfound;				/* found file owned by user(s)*/
16516828Smckusick 	int numfiles = 0;			/* number of files owned by a
16616828Smckusick 						   certain person(s) */
16716828Smckusick 	char **ptr;				/* scratch pointer */
16816818Swall 
16916818Swall 
17016828Smckusick 	/*
17116828Smckusick 	 * For each file in the queue, see if the user(s) own the file. We
17216828Smckusick 	 * have to use "entryfound" (rather than simply incrementing "numfiles")
17316828Smckusick 	 * so that if a person's name appears twice on the command line we
17416828Smckusick 	 * don't double the number of files owned by him/her.
17516828Smckusick 	 */
17616828Smckusick 	for (i = 0; i < numentries ; i++) {
17716828Smckusick 		ptr = namelist;
17816828Smckusick 		entryfound = 0;
17916818Swall 
18016828Smckusick 		while (*ptr) {
18116865Swall 			if (isowner(*ptr,queue[i]->d_name))
18216828Smckusick 				++entryfound;
18316828Smckusick 			++ptr;
18416828Smckusick 		}
18516828Smckusick 		if (entryfound)
18616828Smckusick 			++numfiles;
18716828Smckusick 	}
18816828Smckusick 	return(numfiles);
18916818Swall }
19016818Swall 
19116818Swall /*
19216818Swall  * Print the queue. If only jobs belonging to a certain person(s) are requested,
19316818Swall  * only print jobs that belong to that person(s).
19416818Swall  */
19516818Swall printqueue(namelist)
19616818Swall char **namelist;
19716818Swall {
19816828Smckusick 	int i;					/* for loop index */
19916828Smckusick 	int rank = 1;				/* rank of a job */
20016828Smckusick 	int entryfound;				/* found file owned by user(s)*/
20116828Smckusick 	int printrank();			/* print the rank of a job */
20216828Smckusick 	int plastrun();				/* print the last time the
20316828Smckusick 						   spooling area was updated */
20416865Swall 	int powner();				/* print the name of the owner
20516865Swall 						   of the job */
20616828Smckusick 	int getid();				/* get uid of a person */
20716828Smckusick 	char **ptr;				/* scratch pointer */
20816828Smckusick 	struct stat stbuf;			/* buffer for file stats */
20916818Swall 
21016818Swall 
21116828Smckusick 	/*
21216828Smckusick 	 * Print the time the spooling area was last modified and the header
21316828Smckusick 	 * for the queue.
21416828Smckusick 	 */
21516828Smckusick 	plastrun();
21616828Smckusick 	printf(" Rank	  Execution Date     Owner     Job #   Job Name\n");
21716818Swall 
21816828Smckusick 	/*
21916828Smckusick 	 * Print the queue. If a certain name(s) was requested, print only jobs
22016828Smckusick 	 * belonging to that person(s), otherwise print the entire queue.
22116828Smckusick 	 * Once again, we have to use "entryfound" (rather than simply
22216828Smckusick 	 * comparing each command line argument) so that if a person's name
22316828Smckusick 	 * appears twice we don't print each file owned by him/her twice.
22416828Smckusick 	 *
22516828Smckusick 	 *
22616828Smckusick 	 * "printrank", "printdate", and "printjobname" all take existing
22716828Smckusick 	 * data and display it in a friendly manner.
22816828Smckusick 	 *
22916828Smckusick 	 */
23016828Smckusick 	for (i = 0; i < numentries; i++) {
23116828Smckusick 		if ((stat(queue[i]->d_name, &stbuf)) < 0) {
23216828Smckusick 			continue;
23316828Smckusick 		}
23416828Smckusick 		if (namewanted) {
23516828Smckusick 			ptr = namelist;
23616828Smckusick 			entryfound = 0;
23716818Swall 
23816828Smckusick 			while (*ptr) {
23916865Swall 				if (isowner(*ptr,queue[i]->d_name))
24016828Smckusick 					++entryfound;
24116828Smckusick 				++ptr;
24216828Smckusick 			}
24316828Smckusick 			if (!entryfound)
24416828Smckusick 				continue;
24516828Smckusick 		}
24616828Smckusick 		printrank(rank++);
24716828Smckusick 		printdate(queue[i]->d_name);
24816865Swall 		powner(queue[i]->d_name);
24916828Smckusick 		printf("%5d",stbuf.st_ino);
25016828Smckusick 		printjobname(queue[i]->d_name);
25116828Smckusick 	}
25216828Smckusick 	++ptr;
25316818Swall }
25416818Swall 
25516818Swall /*
25616865Swall  * See if "name" owns "job".
25716818Swall  */
25816865Swall isowner(name,job)
25916865Swall char *name;
26016865Swall char *job;
26116818Swall {
26216865Swall 	char buf[30];			/* buffer for 1st line of spoolfile
26316865Swall 					   header */
26416865Swall 	FILE *infile;			/* I/O stream to spoolfile */
26516818Swall 
26616865Swall 	if ((infile = fopen(job,"r")) == NULL) {
26716865Swall 		fprintf(stderr,"Couldn't open spoolfile");
26816865Swall 		perror(job);
26916865Swall 		return(0);
27016828Smckusick 	}
27116865Swall 
27216865Swall 	if (fscanf(infile,"# owner: %s\n",buf) != 1) {
27316865Swall 		fclose(infile);
27416865Swall 		return(0);
27516865Swall 	}
27616865Swall 
27716865Swall 	fclose(infile);
27816865Swall 	return((strcmp(name,buf) == 0) ? 1 : 0);
27916818Swall }
28016865Swall 
28116865Swall /*
28216865Swall  * Print the owner of the job. This is stored on the first line of the
28316865Swall  * spoolfile. If we run into trouble getting the name, we'll just print "???".
28416865Swall  */
28516865Swall powner(file)
28616865Swall char *file;
28716865Swall {
28816865Swall 	char owner[80];				/* the owner */
28916865Swall 	FILE *infile;				/* I/O stream to spoolfile */
29016865Swall 
29116865Swall 	/*
29216865Swall 	 * Open the job file and grab the first line.
29316865Swall 	 */
29416865Swall 
29516865Swall 	if ((infile = fopen(file,"r")) == NULL) {
29616865Swall 		printf("%-10.9s","???");
29716865Swall 		return;
29816865Swall 	}
29916865Swall 
30016865Swall 	if (fscanf(infile,"# owner: %s",owner) != 1) {
30116865Swall 		printf("%-10.9s","???");
30216865Swall 		fclose(infile);
30316865Swall 		return;
30416865Swall 	}
30516865Swall 
30616865Swall 	fclose(infile);
30716865Swall 	printf("%-10.9s",owner);
30816865Swall 
30916865Swall }
31016828Smckusick 
31116865Swall 
31216818Swall /*
31316818Swall  * Get the uid of a person using his/her login name. Return -1 if no
31416818Swall  * such account name exists.
31516818Swall  */
31616818Swall getid(name)
31716818Swall char *name;
31816818Swall {
31916818Swall 
32016828Smckusick 	struct passwd *pwdinfo;			/* password info structure */
32116818Swall 
32216818Swall 
32316828Smckusick 	if ((pwdinfo = getpwnam(name)) == 0)
32416828Smckusick 		return(-1);
32516818Swall 
32616828Smckusick 	return(pwdinfo->pw_uid);
32716818Swall }
32816818Swall 
32916818Swall /*
33016818Swall  * Print the time the spooling area was updated.
33116818Swall  */
33216818Swall plastrun()
33316818Swall {
33416828Smckusick 	struct timeval now;			/* time it is right now */
33516828Smckusick 	struct timezone zone;			/* NOT USED */
33616828Smckusick 	struct tm *loc;				/* detail of time it is right */
33716828Smckusick 	u_long lasttime;			/* last update time in seconds
33816828Smckusick 						   since 1/1/70 */
33916828Smckusick 	FILE *last;				/* file where last update hour
34016828Smckusick 						   is stored */
34116818Swall 
34216818Swall 
34316828Smckusick 	/*
34416828Smckusick 	 * Open the file where the last update time is stored, and grab the
34516828Smckusick 	 * last update hour. The update time is measured in seconds since
34616828Smckusick 	 * 1/1/70.
34716828Smckusick 	 */
34816828Smckusick 	if ((last = fopen(LASTFILE,"r")) == NULL) {
34916828Smckusick 		perror(LASTFILE);
35016828Smckusick 		exit(1);
35116828Smckusick 	}
35216828Smckusick 	fscanf(last,"%d",(u_long) &lasttime);
35316828Smckusick 	fclose(last);
35416818Swall 
35516828Smckusick 	/*
35616828Smckusick 	 * Get a broken down representation of the last update time.
35716828Smckusick 	 */
35816828Smckusick 	loc = localtime(&lasttime);
35916818Swall 
36016828Smckusick 	/*
36116828Smckusick 	 * Print the time that the spooling area was last updated.
36216828Smckusick 	 */
36316828Smckusick 	printf("\n LAST EXECUTION TIME: %s ",mthnames[loc->tm_mon]);
36416828Smckusick 	printf("%d, 19%d ",loc->tm_mday,loc->tm_year);
36516828Smckusick 	printf("at %d:%02d\n\n",loc->tm_hour,loc->tm_min);
36616818Swall }
36716818Swall 
36816818Swall /*
36916818Swall  * Print the rank of a job. (I've got to admit it, I stole it from "lpq")
37016818Swall  */
37116818Swall static
37216818Swall printrank(n)
37316818Swall {
37416828Smckusick 	static char *r[] = {
37516828Smckusick 		"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
37616828Smckusick 	};
37716818Swall 
37816828Smckusick 	if ((n/10) == 1)
37916828Smckusick 		 printf("%3d%-5s", n,"th");
38016828Smckusick 	else
38116828Smckusick 		 printf("%3d%-5s", n, r[n%10]);
38216818Swall }
38316818Swall 
38416818Swall /*
38516818Swall  * Print the date that a job is to be executed. This takes some manipulation
38616818Swall  * of the file name.
38716818Swall  */
38816818Swall printdate(filename)
38916818Swall char *filename;
39016818Swall {
39116828Smckusick 	int yday  =  0;				/* day of year file will be
39216828Smckusick 						   executed */
39316828Smckusick 	int min	  =  0;				/* min. file will be executed */
39416828Smckusick 	int hour  =  0;				/* hour file will be executed */
39516828Smckusick 	int day	  =  0;				/* day file will be executed */
39616828Smckusick 	int month =  0;				/* month file will be executed*/
39716828Smckusick 	int year  =  0;				/* year file will be executed */
39816828Smckusick 	int get_mth_day();			/* convert a day of year to a
39916828Smckusick 						   month and day of month */
40016828Smckusick 	char date[18];				/* reformatted execution date */
40116818Swall 
40216828Smckusick 	/*
40316828Smckusick 	 * Pick off the necessary info from the file name and convert the day
40416828Smckusick 	 * of year to a month and day of month.
40516828Smckusick 	 */
40616828Smckusick 	sscanf(filename,"%2d.%3d.%2d%2d",&year,&yday,&hour,&min);
40716828Smckusick 	get_mth_day(year,yday,&month,&day);
40816818Swall 
40916828Smckusick 	/*
41016828Smckusick 	 * Format the execution date of a job.
41116828Smckusick 	 */
41216828Smckusick 	sprintf(date,"%3s %2d, 19%2d %02d:%02d",mthnames[month],
41316828Smckusick 						    day, year,hour,min);
41416818Swall 
41516828Smckusick 	/*
41616828Smckusick 	 * Print the date the job will be executed.
41716828Smckusick 	 */
41816828Smckusick 	printf("%-21.18s",date);
41916818Swall }
42016818Swall 
42116818Swall /*
42216818Swall  * Given a day of the year, calculate the month and day of month.
42316818Swall  */
42416818Swall get_mth_day(year,dayofyear,month,day)
42516818Swall int year, dayofyear, *month, *day;
42616818Swall 
42716818Swall {
42816818Swall 
42916828Smckusick 	int i = 1;				/* for loop index */
43016828Smckusick 	int leap;				/* are we dealing with a leap
43116828Smckusick 						   year? */
43216828Smckusick 						/* Table of the number of days
43316828Smckusick 						   in each month of the year.
43416818Swall 
43516828Smckusick 						     dofy_tab[1] -- regular year
43616828Smckusick 						     dofy_tab[2] -- leap year
43716828Smckusick 									      */
43816818Swall 
43916828Smckusick 	static int dofy_tab[2][13] = {
44016828Smckusick 		{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
44116828Smckusick 		{ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
44216828Smckusick 	};
44316818Swall 
44416828Smckusick 	/*
44516828Smckusick 	 * Are we dealing with a leap year?
44616828Smckusick 	 */
44716828Smckusick 	leap = ((year%4 == 0 && year%100 != 0) || year%100 == 0);
44816818Swall 
44916828Smckusick 	/*
45016828Smckusick 	 * Calculate the month of the year and day of the month.
45116828Smckusick 	 */
45216828Smckusick 	while (dayofyear >= dofy_tab[leap][i]) {
45316828Smckusick 		dayofyear -= dofy_tab[leap][i++];
45416828Smckusick 		++(*month);
45516828Smckusick 	}
45616828Smckusick 	*day = (dayofyear + 1);
45716818Swall }
45816828Smckusick 
45916818Swall /*
46016818Swall  * Print a job name. If the old "at" has been used to create the spoolfile,
46116818Swall  * the three line header that the new version of "at" puts in the spoolfile.
46216818Swall  * Thus, we just print "???".
46316818Swall  */
46416818Swall printjobname(file)
46516818Swall char *file;
46616818Swall {
46716865Swall 	char *ptr;				/* scratch pointer */
46816828Smckusick 	char jobname[80];			/* the job name */
46916828Smckusick 	FILE *filename;				/* job file in spooling area */
47016818Swall 
47116828Smckusick 	/*
47216865Swall 	 * Open the job file and grab the second line.
47316828Smckusick 	 */
47416828Smckusick 	printf("   ");
47516818Swall 
47616828Smckusick 	if ((filename = fopen(file,"r")) == NULL) {
47716828Smckusick 		printf("%.27s\n", "???");
47816828Smckusick 		return;
47916828Smckusick 	}
48016865Swall 	/*
48116865Swall 	 * We'll yank the first line into the buffer temporarily.
48216865Swall 	 */
48316865Swall 	fgets(jobname,80,filename);
48416865Swall 
48516865Swall 	/*
48616865Swall 	 * Now get the job name.
48716865Swall 	 */
48816828Smckusick 	if (fscanf(filename,"# jobname: %s",jobname) != 1) {
48916828Smckusick 		printf("%.27s\n", "???");
49016828Smckusick 		fclose(filename);
49116828Smckusick 		return;
49216828Smckusick 	}
49316828Smckusick 	fclose(filename);
49416818Swall 
49516828Smckusick 	/*
49616828Smckusick 	 * Put a pointer at the begining of the line and remove the basename
49716828Smckusick 	 * from the job file.
49816828Smckusick 	 */
49916865Swall 	ptr = jobname;
50016865Swall 	if ((ptr = (char *)rindex(jobname,'/')) != 0)
50116865Swall 		++ptr;
50216828Smckusick 	else
50316865Swall 		ptr = jobname;
50416818Swall 
50516865Swall 	if (strlen(ptr) > 23)
50616865Swall 		printf("%.23s ...\n",ptr);
50716865Swall 	else
50816865Swall 		printf("%.27s\n",ptr);
50916818Swall }
51016818Swall 
51116818Swall /*
51216818Swall  * Do we want to include a file in the queue? (used by "scandir") We are looking
51316818Swall  * for files with following syntax: yy.ddd.hhhh. so the test is made to see if
51416818Swall  * the file name has three dots in it. This test will suffice since the only
51516818Swall  * other files in /usr/spool/at don't have any dots in their name.
51616818Swall  */
51716818Swall filewanted(direntry)
51816818Swall struct direct *direntry;
51916818Swall {
52016828Smckusick 	int numdot = 0;
52116828Smckusick 	char *filename;
52216818Swall 
52316828Smckusick 	filename = direntry->d_name;
52416828Smckusick 	while (*filename)
52516828Smckusick 		numdot += (*(filename++) == '.');
52616828Smckusick 	return(numdot == 3);
52716818Swall }
52816818Swall 
52916818Swall /*
53016818Swall  * Sort files by time of creation. (used by "scandir")
53116818Swall  */
53216818Swall creation(d1, d2)
53316818Swall struct direct **d1, **d2;
53416818Swall {
53516828Smckusick 	struct stat stbuf1, stbuf2;
53616818Swall 
53716828Smckusick 	if (stat((*d1)->d_name,&stbuf1) < 0)
53816828Smckusick 		return(1);
53916818Swall 
54016828Smckusick 	if (stat((*d2)->d_name,&stbuf2) < 0)
54116828Smckusick 		return(1);
54216818Swall 
54316828Smckusick 	return(stbuf1.st_ctime < stbuf2.st_ctime);
54416818Swall }
54516828Smckusick 
54616818Swall /*
54716818Swall  * Print usage info and exit.
54816818Swall  */
54916818Swall usage()
55016818Swall {
55116828Smckusick 	fprintf(stderr,"usage:	atq [-c] [-n] [name ...]\n");
55216828Smckusick 	exit(1);
55316818Swall }
554