xref: /original-bsd/usr.bin/at/atq/atq.c (revision 50f8aa26)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)atq.c	5.2 (Berkeley) 05/28/86";
15 #endif not lint
16 
17 /*
18  *
19  *	Synopsis:  atq [ -c ] [ -n ] [ name ... ]
20  *
21  *
22  *	Print the queue of files waiting to be executed. These files
23  *	were created by using the "at" command and are located in the
24  *	directory "/usr/spool/at".
25  *
26  *
27  *	Author: Steve Wall
28  *		Computer Systems Research Group
29  *		University of California @ Berkeley
30  *
31  */
32 
33 # include <stdio.h>
34 # include <sys/types.h>
35 # include <sys/file.h>
36 # include <sys/dir.h>
37 # include <sys/stat.h>
38 # include <sys/time.h>
39 # include <pwd.h>
40 # include <ctype.h>
41 
42 # define ATDIR		"/usr/spool/at"			/* spooling area */
43 # define LASTFILE	"/usr/spool/at/lasttimedone"	/* update time record
44 							   file */
45 
46 /*
47  * Months of the year
48  */
49 static char *mthnames[12] = {
50 	"Jan","Feb","Mar","Apr","May","Jun","Jul",
51 	"Aug","Sep","Oct","Nov","Dec",
52 };
53 
54 char *nullentry = NULL;			/* avoid 'namelist' NULL ptr problems */
55 int numentries;				/* number of entries in spooling area */
56 int namewanted = 0;			/* only print jobs belonging to a
57 					   certain person */
58 struct direct **queue;			/* the queue itself */
59 
60 
61 main(argc,argv)
62 int argc;
63 char **argv;
64 {
65 
66 	int cflag = 0;			/* print in order of creation time */
67 	int nflag = 0;			/* just print the number of jobs in
68 					   queue */
69 	int usage();			/* print usage info and exit */
70 	int creation();			/* sort jobs by date of creation */
71 	int alphasort();		/* sort jobs by date of execution */
72 	int filewanted();		/* should a file be included in queue?*/
73 	int printqueue();		/* print the queue */
74 	int countfiles();		/* count the number of files in queue
75 					   for a given person */
76 	char **namelist = &nullentry;	/* array of specific name(s) requested*/
77 
78 
79 	--argc, ++argv;
80 
81 	/*
82 	 * Interpret command line flags if they exist.
83 	 */
84 	while (argc > 0 && **argv == '-') {
85 		(*argv)++;
86 		while (**argv) switch (*(*argv)++) {
87 
88 			case 'c' :	cflag++;
89 					break;
90 
91 			case 'n' :	nflag++;
92 					break;
93 
94 			default	 :	usage();
95 
96 		}
97 		--argc, ++argv;
98 	}
99 
100 	/*
101 	 * If a certain name (or names) is requested, set a pointer to the
102 	 * beginning of the list.
103 	 */
104 	if (argc > 0) {
105 		++namewanted;
106 		namelist = argv;
107 	}
108 
109 	/*
110 	 * Move to the spooling area and scan the directory, placing the
111 	 * files in the queue structure. The queue comes back sorted by
112 	 * execution time or creation time.
113 	 */
114 	if (chdir(ATDIR) == -1) {
115 		perror(ATDIR);
116 		exit(1);
117 	}
118 	if ((numentries = scandir(".",&queue,filewanted, (cflag) ? creation :
119 				alphasort)) < 0) {
120 		perror(ATDIR);
121 		exit(1);
122 	}
123 
124 	/*
125 	 * Either print a message stating:
126 	 *
127 	 *	1) that the spooling area is empty.
128 	 *	2) the number of jobs in the spooling area.
129 	 *	3) the number of jobs in the spooling area belonging to
130 	 *	   a certain person.
131 	 *	4) that the person requested doesn't have any files in the
132 	 *	   spooling area.
133 	 *
134 	 * or send the queue off to "printqueue" for printing.
135 	 *
136 	 * This whole process might seem a bit elaborate, but it's worthwhile
137 	 * to print some informative messages for the user.
138 	 *
139 	 */
140 	if ((numentries == 0) && (!nflag)) {
141 		printf("no files in queue.\n");
142 		exit(0);
143 	}
144 	if (nflag) {
145 		printf("%d\n",(namewanted) ? countfiles(namelist) : numentries);
146 		exit(0);
147 	}
148 	if ((namewanted) && (countfiles(namelist) == 0)) {
149 		printf("no files for %s.\n", (argc == 1) ?
150 					*argv : "specified users");
151 		exit(0);
152 	}
153 	printqueue(namelist);
154 	exit(0);
155 }
156 
157 /*
158  * Count the number of jobs in the spooling area owned by a certain person(s).
159  */
160 countfiles(namelist)
161 char **namelist;
162 {
163 	int i;					/* for loop index */
164 	int entryfound;				/* found file owned by user(s)*/
165 	int numfiles = 0;			/* number of files owned by a
166 						   certain person(s) */
167 	char **ptr;				/* scratch pointer */
168 
169 
170 	/*
171 	 * For each file in the queue, see if the user(s) own the file. We
172 	 * have to use "entryfound" (rather than simply incrementing "numfiles")
173 	 * so that if a person's name appears twice on the command line we
174 	 * don't double the number of files owned by him/her.
175 	 */
176 	for (i = 0; i < numentries ; i++) {
177 		ptr = namelist;
178 		entryfound = 0;
179 
180 		while (*ptr) {
181 			if (isowner(*ptr,queue[i]->d_name))
182 				++entryfound;
183 			++ptr;
184 		}
185 		if (entryfound)
186 			++numfiles;
187 	}
188 	return(numfiles);
189 }
190 
191 /*
192  * Print the queue. If only jobs belonging to a certain person(s) are requested,
193  * only print jobs that belong to that person(s).
194  */
195 printqueue(namelist)
196 char **namelist;
197 {
198 	int i;					/* for loop index */
199 	int rank = 1;				/* rank of a job */
200 	int entryfound;				/* found file owned by user(s)*/
201 	int printrank();			/* print the rank of a job */
202 	int plastrun();				/* print the last time the
203 						   spooling area was updated */
204 	int powner();				/* print the name of the owner
205 						   of the job */
206 	int getid();				/* get uid of a person */
207 	char **ptr;				/* scratch pointer */
208 	struct stat stbuf;			/* buffer for file stats */
209 
210 
211 	/*
212 	 * Print the time the spooling area was last modified and the header
213 	 * for the queue.
214 	 */
215 	plastrun();
216 	printf(" Rank	  Execution Date     Owner     Job #   Job Name\n");
217 
218 	/*
219 	 * Print the queue. If a certain name(s) was requested, print only jobs
220 	 * belonging to that person(s), otherwise print the entire queue.
221 	 * Once again, we have to use "entryfound" (rather than simply
222 	 * comparing each command line argument) so that if a person's name
223 	 * appears twice we don't print each file owned by him/her twice.
224 	 *
225 	 *
226 	 * "printrank", "printdate", and "printjobname" all take existing
227 	 * data and display it in a friendly manner.
228 	 *
229 	 */
230 	for (i = 0; i < numentries; i++) {
231 		if ((stat(queue[i]->d_name, &stbuf)) < 0) {
232 			continue;
233 		}
234 		if (namewanted) {
235 			ptr = namelist;
236 			entryfound = 0;
237 
238 			while (*ptr) {
239 				if (isowner(*ptr,queue[i]->d_name))
240 					++entryfound;
241 				++ptr;
242 			}
243 			if (!entryfound)
244 				continue;
245 		}
246 		printrank(rank++);
247 		printdate(queue[i]->d_name);
248 		powner(queue[i]->d_name);
249 		printf("%5d",stbuf.st_ino);
250 		printjobname(queue[i]->d_name);
251 	}
252 	++ptr;
253 }
254 
255 /*
256  * See if "name" owns "job".
257  */
258 isowner(name,job)
259 char *name;
260 char *job;
261 {
262 	char buf[128];			/* buffer for 1st line of spoolfile
263 					   header */
264 	FILE *infile;			/* I/O stream to spoolfile */
265 
266 	if ((infile = fopen(job,"r")) == NULL) {
267 		fprintf(stderr,"Couldn't open spoolfile ");
268 		perror(job);
269 		return(0);
270 	}
271 
272 	if (fscanf(infile,"# owner: %127s%*[^\n]\n",buf) != 1) {
273 		fclose(infile);
274 		return(0);
275 	}
276 
277 	fclose(infile);
278 	return((strcmp(name,buf) == 0) ? 1 : 0);
279 }
280 
281 /*
282  * Print the owner of the job. This is stored on the first line of the
283  * spoolfile. If we run into trouble getting the name, we'll just print "???".
284  */
285 powner(file)
286 char *file;
287 {
288 	char owner[10];				/* the owner */
289 	FILE *infile;				/* I/O stream to spoolfile */
290 
291 	/*
292 	 * Open the job file and grab the first line.
293 	 */
294 
295 	if ((infile = fopen(file,"r")) == NULL) {
296 		printf("%-10.9s","???");
297 		perror(file);
298 		return;
299 	}
300 
301 	if (fscanf(infile,"# owner: %9s%*[^\n]\n",owner) != 1) {
302 		printf("%-10.9s","???");
303 		fclose(infile);
304 		return;
305 	}
306 
307 	fclose(infile);
308 	printf("%-10.9s",owner);
309 
310 }
311 
312 
313 /*
314  * Get the uid of a person using his/her login name. Return -1 if no
315  * such account name exists.
316  */
317 getid(name)
318 char *name;
319 {
320 
321 	struct passwd *pwdinfo;			/* password info structure */
322 
323 
324 	if ((pwdinfo = getpwnam(name)) == 0)
325 		return(-1);
326 
327 	return(pwdinfo->pw_uid);
328 }
329 
330 /*
331  * Print the time the spooling area was updated.
332  */
333 plastrun()
334 {
335 	struct timeval now;			/* time it is right now */
336 	struct timezone zone;			/* NOT USED */
337 	struct tm *loc;				/* detail of time it is right */
338 	u_long lasttime;			/* last update time in seconds
339 						   since 1/1/70 */
340 	FILE *last;				/* file where last update hour
341 						   is stored */
342 
343 
344 	/*
345 	 * Open the file where the last update time is stored, and grab the
346 	 * last update hour. The update time is measured in seconds since
347 	 * 1/1/70.
348 	 */
349 	if ((last = fopen(LASTFILE,"r")) == NULL) {
350 		perror(LASTFILE);
351 		exit(1);
352 	}
353 	fscanf(last,"%d",(u_long) &lasttime);
354 	fclose(last);
355 
356 	/*
357 	 * Get a broken down representation of the last update time.
358 	 */
359 	loc = localtime(&lasttime);
360 
361 	/*
362 	 * Print the time that the spooling area was last updated.
363 	 */
364 	printf("\n LAST EXECUTION TIME: %s ",mthnames[loc->tm_mon]);
365 	printf("%d, 19%d ",loc->tm_mday,loc->tm_year);
366 	printf("at %d:%02d\n\n",loc->tm_hour,loc->tm_min);
367 }
368 
369 /*
370  * Print the rank of a job. (I've got to admit it, I stole it from "lpq")
371  */
372 static
373 printrank(n)
374 {
375 	static char *r[] = {
376 		"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
377 	};
378 
379 	if ((n/10) == 1)
380 		 printf("%3d%-5s", n,"th");
381 	else
382 		 printf("%3d%-5s", n, r[n%10]);
383 }
384 
385 /*
386  * Print the date that a job is to be executed. This takes some manipulation
387  * of the file name.
388  */
389 printdate(filename)
390 char *filename;
391 {
392 	int yday  =  0;				/* day of year file will be
393 						   executed */
394 	int min	  =  0;				/* min. file will be executed */
395 	int hour  =  0;				/* hour file will be executed */
396 	int day	  =  0;				/* day file will be executed */
397 	int month =  0;				/* month file will be executed*/
398 	int year  =  0;				/* year file will be executed */
399 	int get_mth_day();			/* convert a day of year to a
400 						   month and day of month */
401 	char date[18];				/* reformatted execution date */
402 
403 	/*
404 	 * Pick off the necessary info from the file name and convert the day
405 	 * of year to a month and day of month.
406 	 */
407 	sscanf(filename,"%2d.%3d.%2d%2d",&year,&yday,&hour,&min);
408 	get_mth_day(year,yday,&month,&day);
409 
410 	/*
411 	 * Format the execution date of a job.
412 	 */
413 	sprintf(date,"%3s %2d, 19%2d %02d:%02d",mthnames[month],
414 						    day, year,hour,min);
415 
416 	/*
417 	 * Print the date the job will be executed.
418 	 */
419 	printf("%-21.18s",date);
420 }
421 
422 /*
423  * Given a day of the year, calculate the month and day of month.
424  */
425 get_mth_day(year,dayofyear,month,day)
426 int year, dayofyear, *month, *day;
427 
428 {
429 
430 	int i = 1;				/* for loop index */
431 	int leap;				/* are we dealing with a leap
432 						   year? */
433 						/* Table of the number of days
434 						   in each month of the year.
435 
436 						     dofy_tab[1] -- regular year
437 						     dofy_tab[2] -- leap year
438 									      */
439 
440 	static int dofy_tab[2][13] = {
441 		{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
442 		{ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
443 	};
444 
445 	/*
446 	 * Are we dealing with a leap year?
447 	 */
448 	leap = ((year%4 == 0 && year%100 != 0) || year%100 == 0);
449 
450 	/*
451 	 * Calculate the month of the year and day of the month.
452 	 */
453 	while (dayofyear >= dofy_tab[leap][i]) {
454 		dayofyear -= dofy_tab[leap][i++];
455 		++(*month);
456 	}
457 	*day = (dayofyear + 1);
458 }
459 
460 /*
461  * Print a job name. If the old "at" has been used to create the spoolfile,
462  * the three line header that the new version of "at" puts in the spoolfile.
463  * Thus, we just print "???".
464  */
465 printjobname(file)
466 char *file;
467 {
468 	char *ptr;				/* scratch pointer */
469 	char jobname[28];			/* the job name */
470 	FILE *filename;				/* job file in spooling area */
471 
472 	/*
473 	 * Open the job file and grab the second line.
474 	 */
475 	printf("   ");
476 
477 	if ((filename = fopen(file,"r")) == NULL) {
478 		printf("%.27s\n", "???");
479 		perror(file);
480 		return;
481 	}
482 	/*
483 	 * Skip over the first line.
484 	 */
485 	fscanf(filename,"%*[^\n]\n");
486 
487 	/*
488 	 * Now get the job name.
489 	 */
490 	if (fscanf(filename,"# jobname: %27s%*[^\n]\n",jobname) != 1) {
491 		printf("%.27s\n", "???");
492 		fclose(filename);
493 		return;
494 	}
495 	fclose(filename);
496 
497 	/*
498 	 * Put a pointer at the begining of the line and remove the basename
499 	 * from the job file.
500 	 */
501 	ptr = jobname;
502 	if ((ptr = (char *)rindex(jobname,'/')) != 0)
503 		++ptr;
504 	else
505 		ptr = jobname;
506 
507 	if (strlen(ptr) > 23)
508 		printf("%.23s ...\n",ptr);
509 	else
510 		printf("%.27s\n",ptr);
511 }
512 
513 /*
514  * Do we want to include a file in the queue? (used by "scandir") We are looking
515  * for files with following syntax: yy.ddd.hhhh. so the test is made to see if
516  * the file name has three dots in it. This test will suffice since the only
517  * other files in /usr/spool/at don't have any dots in their name.
518  */
519 filewanted(direntry)
520 struct direct *direntry;
521 {
522 	int numdot = 0;
523 	char *filename;
524 
525 	filename = direntry->d_name;
526 	while (*filename)
527 		numdot += (*(filename++) == '.');
528 	return(numdot == 3);
529 }
530 
531 /*
532  * Sort files by time of creation. (used by "scandir")
533  */
534 creation(d1, d2)
535 struct direct **d1, **d2;
536 {
537 	struct stat stbuf1, stbuf2;
538 
539 	if (stat((*d1)->d_name,&stbuf1) < 0)
540 		return(1);
541 
542 	if (stat((*d2)->d_name,&stbuf2) < 0)
543 		return(1);
544 
545 	return(stbuf1.st_ctime < stbuf2.st_ctime);
546 }
547 
548 /*
549  * Print usage info and exit.
550  */
551 usage()
552 {
553 	fprintf(stderr,"usage:	atq [-c] [-n] [name ...]\n");
554 	exit(1);
555 }
556