xref: /illumos-gate/usr/src/cmd/cron/atq.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
2 /*	  All Rights Reserved  	*/
3 
4 
5 /*
6  * Copyright (c) 1983 Regents of the University of California.
7  * All rights reserved.  The Berkeley software License Agreement
8  * specifies the terms and conditions for redistribution.
9  */
10 
11 /*
12  * Copyright 1984-1988, 2002-2003 Sun Microsystems, Inc.  All rights reserved.
13  * Use is subject to license terms.
14  */
15 
16 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.6 */
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 defined by ATDIR.
26  */
27 
28 #include <stdio.h>
29 #include <sys/types.h>
30 #include <sys/file.h>
31 #include <dirent.h>
32 #include <sys/stat.h>
33 #include <time.h>
34 #include <pwd.h>
35 #include <ctype.h>
36 #include <unistd.h>
37 #include <locale.h>
38 #include <errno.h>
39 #include "cron.h"
40 
41 extern char	*errmsg();
42 extern char	*strchr();
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 int numentries;				/* number of entries in spooling area */
53 int namewanted = 0;			/* print jobs for a certain person */
54 struct dirent **queue;			/* the queue itself */
55 
56 #define	INVALIDUSER	"you are not a valid user (no entry in /etc/passwd)"
57 #define	NOTALLOWED	"you are not authorized to use at.  Sorry."
58 
59 
60 main(argc, argv)
61 int argc;
62 char **argv;
63 {
64 
65 	register struct passwd *pp;	/* password file entry pointer */
66 	struct passwd pr;
67 	register int i;
68 	int cflag = 0;			/* print in order of creation time */
69 	int nflag = 0;			/* just print the number of jobs in */
70 					/* queue */
71 	int usage();			/* print usage info and exit */
72 	extern int creation();		/* sort jobs by date of creation */
73 	extern int execution();		/* sort jobs by date of execution */
74 	int filewanted();		/* should file be included in queue? */
75 	int printqueue();		/* print the queue */
76 	int countfiles();		/* count the number of files in queue */
77 					/* for a given person */
78 	uid_t *uidlist = NULL;		/* array of spec. owner ID(s) requ. */
79 	int argnum = 0;			/* number of names passed as arg't */
80 	int badarg = 0;
81 	char *c;
82 
83 
84 	--argc, ++argv;
85 
86 	(void) setlocale(LC_ALL, "");
87 	pp = getpwuid(getuid());
88 	pr.pw_uid = pp->pw_uid;
89 	pr.pw_name = pp->pw_name;
90 
91 	if (pp == NULL)
92 		atabort(INVALIDUSER);
93 	if (!allowed(pp->pw_name, ATALLOW, ATDENY))
94 		atabort(NOTALLOWED);
95 
96 	/*
97 	 * Interpret command line flags if they exist.
98 	 */
99 	while (argc > 0 && **argv == '-') {
100 		(*argv)++;
101 		while (**argv) {
102 			switch (*(*argv)++) {
103 
104 			case 'c' :	cflag++;
105 					break;
106 
107 			case 'n' :	nflag++;
108 					break;
109 
110 			default	 :	usage();
111 
112 			}
113 		}
114 		--argc, ++argv;
115 	}
116 
117 	/*
118 	 * If a certain name (or names) is requested, set a pointer to the
119 	 * beginning of the list.
120 	 */
121 	if (argc > 0) {
122 		++namewanted;
123 		uidlist = (uid_t *)malloc(argc * sizeof (uid_t));
124 		if (uidlist == NULL)
125 			atabortperror("can't allocate list of users");
126 		for (i = 0; i < argc; i++) {
127 			if ((chkauthattr(CRONADMIN_AUTH, pr.pw_name)) ||
128 					strcmp(pr.pw_name, argv[i]) == 0) {
129 				if ((pp = getpwnam(argv[i])) == NULL) {
130 					(void) fprintf(stderr,
131 					    "atq: No such user %s\n", argv[i]);
132 					exit(1);
133 				}
134 				uidlist[argnum] = pp->pw_uid;
135 				argnum++;
136 			}
137 			else
138 				badarg++;
139 		}
140 		if (badarg)
141 			if (argnum)
142 				printf("Printing queue information only "
143 				    "for %s:\n", pr.pw_name);
144 			else {
145 				printf("atq: Non-priviledged user cannot "
146 				    "request information regarding other "
147 				    "users\n");
148 				exit(1);
149 			}
150 	} else if (!chkauthattr(CRONADMIN_AUTH, pr.pw_name)) {
151 		/* no argument specified and the invoker is not root */
152 		++namewanted;
153 		argnum = 1;
154 		if ((uidlist = (uid_t *)malloc(sizeof (uid_t))) == NULL)
155 			atabortperror("can't allocate list of users");
156 		*uidlist = pr.pw_uid;
157 	}
158 
159 	/*
160 	 * Move to the spooling area and scan the directory, placing the
161 	 * files in the queue structure. The queue comes back sorted by
162 	 * execution time or creation time.
163 	 */
164 	if (chdir(ATDIR) == -1)
165 		atabortperror(ATDIR);
166 	if ((numentries = ascandir(".", &queue, filewanted,
167 	    (cflag) ? creation : execution)) < 0)
168 		atabortperror(ATDIR);
169 
170 
171 	/*
172 	 * Either print a message stating:
173 	 *
174 	 *	1) that the spooling area is empty.
175 	 *	2) the number of jobs in the spooling area.
176 	 *	3) the number of jobs in the spooling area belonging to
177 	 *	   a certain person.
178 	 *	4) that the person requested doesn't have any files in the
179 	 *	   spooling area.
180 	 *
181 	 * or send the queue off to "printqueue" for printing.
182 	 *
183 	 * This whole process might seem a bit elaborate, but it's worthwhile
184 	 * to print some informative messages for the user.
185 	 *
186 	 */
187 	if ((numentries == 0) && (!nflag)) {
188 		printf("no files in queue.\n");
189 		exit(0);
190 	}
191 	if (nflag) {
192 		printf("%d\n", (namewanted) ?
193 		    countfiles(uidlist, argnum) : numentries);
194 		exit(0);
195 	}
196 	if ((namewanted) && (countfiles(uidlist, argnum) == 0)) {
197 		if (argnum == 1)
198 			if (argnum != argc) c = pr.pw_name;
199 			else c = *argv;
200 		printf("no files for %s.\n", (argnum == 1) ?
201 					c : "specified users");
202 		exit(0);
203 	}
204 	printqueue(uidlist, argnum);
205 	exit(0);
206 }
207 
208 /*
209  * Count the number of jobs in the spooling area owned by a certain person(s).
210  */
211 countfiles(uidlist, nuids)
212 uid_t *uidlist;
213 int nuids;
214 {
215 	register int i, j;			/* for loop indices */
216 	int entryfound;				/* found file owned by users */
217 	int numfiles = 0;			/* number of files owned by a */
218 						/* certain person(s) */
219 	register uid_t *ptr;			/* scratch pointer */
220 	struct stat stbuf;			/* buffer for file stats */
221 
222 
223 	/*
224 	 * For each file in the queue, see if the user(s) own the file. We
225 	 * have to use "entryfound" (rather than simply incrementing "numfiles")
226 	 * so that if a person's name appears twice on the command line we
227 	 * don't double the number of files owned by him/her.
228 	 */
229 	for (i = 0; i < numentries; i++) {
230 		if ((stat(queue[i]->d_name, &stbuf)) < 0) {
231 			continue;
232 		}
233 		ptr = uidlist;
234 		entryfound = 0;
235 
236 		for (j = 0; j < nuids; j++) {
237 			if (*ptr == stbuf.st_uid)
238 				++entryfound;
239 			++ptr;
240 		}
241 		if (entryfound)
242 			++numfiles;
243 	}
244 	return (numfiles);
245 }
246 
247 /*
248  * Print the queue. If only jobs belonging to a certain person(s) are requested,
249  * only print jobs that belong to that person(s).
250  */
251 printqueue(uidlist, nuids)
252 uid_t *uidlist;
253 int nuids;
254 {
255 	register int i, j;			/* for loop indices */
256 	int rank;				/* rank of a job */
257 	int entryfound;				/* found file owned by users */
258 	int printrank();			/* print the rank of a job */
259 	char *getname();
260 	register uid_t *ptr;			/* scratch pointer */
261 	struct stat stbuf;			/* buffer for file stats */
262 	char curqueue;				/* queue of current job */
263 	char lastqueue;				/* queue of previous job */
264 
265 	/*
266 	 * Print the header for the queue.
267 	 */
268 	printf(" Rank	  Execution Date     Owner      Job            "
269 	    "Queue   Job Name\n");
270 
271 	/*
272 	 * Print the queue. If a certain name(s) was requested, print only jobs
273 	 * belonging to that person(s), otherwise print the entire queue.
274 	 * Once again, we have to use "entryfound" (rather than simply
275 	 * comparing each command line argument) so that if a person's name
276 	 * appears twice we don't print each file owned by him/her twice.
277 	 *
278 	 *
279 	 * "printrank", "printdate", and "printjobname" all take existing
280 	 * data and display it in a friendly manner.
281 	 *
282 	 */
283 	lastqueue = '\0';
284 	for (i = 0; i < numentries; i++) {
285 		if ((stat(queue[i]->d_name, &stbuf)) < 0) {
286 			continue;
287 		}
288 		curqueue = *(strchr(queue[i]->d_name, '.') + 1);
289 		if (curqueue != lastqueue) {
290 			rank = 1;
291 			lastqueue = curqueue;
292 		}
293 		if (namewanted) {
294 			ptr = uidlist;
295 			entryfound = 0;
296 
297 			for (j = 0; j < nuids; j++) {
298 				if (*ptr == stbuf.st_uid)
299 					++entryfound;
300 				++ptr;
301 			}
302 			if (!entryfound)
303 				continue;
304 		}
305 		printrank(rank++);
306 		printdate(queue[i]->d_name);
307 		printf("%-10s ", getname(stbuf.st_uid));
308 		printf("%-14s ", queue[i]->d_name);
309 		printf("  %c", curqueue);
310 		printjobname(queue[i]->d_name);
311 	}
312 	++ptr;
313 }
314 
315 /*
316  * Get the uid of a person using his/her login name. Return -1 if no
317  * such account name exists.
318  */
319 uid_t
320 getid(name)
321 char *name;
322 {
323 
324 	struct passwd *pwdinfo;			/* password info structure */
325 
326 
327 	if ((pwdinfo = getpwnam(name)) == 0)
328 		return ((uid_t)-1);
329 
330 	return (pwdinfo->pw_uid);
331 }
332 
333 /*
334  * Get the full login name of a person using his/her user id.
335  */
336 char *
337 getname(uid)
338 uid_t uid;
339 {
340 	register struct passwd *pwdinfo;	/* password info structure */
341 
342 
343 	if ((pwdinfo = getpwuid(uid)) == 0)
344 		return ("???");
345 	return (pwdinfo->pw_name);
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 	time_t	jobdate;
372 	extern time_t num();
373 	register struct tm *unpackeddate;
374 	char date[18];				/* reformatted execution date */
375 
376 	/*
377 	 * Convert the file name to a date.
378 	 */
379 	jobdate = num(&filename);
380 	unpackeddate = localtime(&jobdate);
381 
382 	/* years since 1900 + base century 1900 */
383 	unpackeddate->tm_year += 1900;
384 	/*
385 	 * Format the execution date of a job.
386 	 */
387 	sprintf(date, "%3s %2d, %4d %02d:%02d", mthnames[unpackeddate->tm_mon],
388 	    unpackeddate->tm_mday, unpackeddate->tm_year,
389 	    unpackeddate->tm_hour, unpackeddate->tm_min);
390 
391 	/*
392 	 * Print the date the job will be executed.
393 	 */
394 	printf("%-21.18s", date);
395 }
396 
397 /*
398  * Print a job name. If the old "at" has been used to create the spoolfile,
399  * the three line header that the new version of "at" puts in the spoolfile.
400  * Thus, we just print "???".
401  */
402 printjobname(file)
403 char *file;
404 {
405 	char *ptr;				/* scratch pointer */
406 	char jobname[28];			/* the job name */
407 	FILE *filename;				/* job file in spooling area */
408 
409 	/*
410 	 * Open the job file and grab the third line.
411 	 */
412 	printf("     ");
413 
414 	if ((filename = fopen(file, "r")) == NULL) {
415 		printf("%.27s\n", "???");
416 		(void) fprintf(stderr, "atq: Can't open job file %s: %s\n",
417 		    file, errmsg(errno));
418 		return;
419 	}
420 	/*
421 	 * Skip over the first and second lines.
422 	 */
423 	fscanf(filename, "%*[^\n]\n");
424 
425 	/*
426 	 * Now get the job name.
427 	 */
428 	if (fscanf(filename, ": jobname: %27s%*[^\n]\n", jobname) != 1) {
429 		printf("%.27s\n", "???");
430 		fclose(filename);
431 		return;
432 	}
433 	fclose(filename);
434 
435 	/*
436 	 * Put a pointer at the begining of the line and remove the basename
437 	 * from the job file.
438 	 */
439 	ptr = jobname;
440 	if ((ptr = (char *)strrchr(jobname, '/')) != 0)
441 		++ptr;
442 	else
443 		ptr = jobname;
444 
445 	if (strlen(ptr) > 23)
446 		printf("%.23s ...\n", ptr);
447 	else
448 		printf("%.27s\n", ptr);
449 }
450 
451 
452 
453 /*
454  * Sort files by queue, time of creation, and sequence. (used by "ascandir")
455  */
456 creation(d1, d2)
457 struct dirent **d1, **d2;
458 {
459 	register char *p1, *p2;
460 	register int i;
461 	struct stat stbuf1, stbuf2;
462 	register int seq1, seq2;
463 
464 	if ((p1 = strchr((*d1)->d_name, '.')) == NULL)
465 		return (0);
466 	if ((p2 = strchr((*d2)->d_name, '.')) == NULL)
467 		return (0);
468 	p1++;
469 	p2++;
470 	if ((i = *p1++ - *p2++) != 0)
471 		return (i);
472 
473 	if (stat((*d1)->d_name, &stbuf1) < 0)
474 		return (0);
475 
476 	if (stat((*d2)->d_name, &stbuf2) < 0)
477 		return (0);
478 
479 	if (stbuf1.st_ctime < stbuf2.st_ctime)
480 		return (-1);
481 	else if (stbuf1.st_ctime > stbuf2.st_ctime)
482 		return (1);
483 	p1++;
484 	p2++;
485 	seq1 = atoi(p1);
486 	seq2 = atoi(p2);
487 	return (seq1 - seq2);
488 }
489 
490 /*
491  * Sort files by queue, time of execution, and sequence. (used by "ascandir")
492  */
493 execution(d1, d2)
494 struct dirent **d1, **d2;
495 {
496 	register char *p1, *p2;
497 	register int i;
498 	char *name1, *name2;
499 	register time_t time1, time2;
500 	register int seq1, seq2;
501 	extern time_t num();
502 
503 	name1 = (*d1)->d_name;
504 	name2 = (*d2)->d_name;
505 	if ((p1 = strchr(name1, '.')) == NULL)
506 		return (1);
507 	if ((p2 = strchr(name2, '.')) == NULL)
508 		return (1);
509 	p1++;
510 	p2++;
511 	if ((i = *p1++ - *p2++) != 0)
512 		return (i);
513 
514 	time1 = num(&name1);
515 	time2 = num(&name2);
516 
517 	if (time1 < time2)
518 		return (-1);
519 	else if (time1 > time2)
520 		return (1);
521 	p1++;
522 	p2++;
523 	seq1 = atoi(p1);
524 	seq2 = atoi(p2);
525 	return (seq1 - seq2);
526 }
527 
528 
529 /*
530  * Print usage info and exit.
531  */
532 usage()
533 {
534 	fprintf(stderr, "usage:	atq [-c] [-n] [name ...]\n");
535 	exit(1);
536 }
537 
538 aterror(msg)
539 	char *msg;
540 {
541 	fprintf(stderr, "atq: %s\n", msg);
542 }
543 
544 atperror(msg)
545 	char *msg;
546 {
547 	fprintf(stderr, "atq: %s: %s\n", msg, errmsg(errno));
548 }
549 
550 atabort(msg)
551 	char *msg;
552 {
553 	aterror(msg);
554 	exit(1);
555 }
556 
557 atabortperror(msg)
558 	char *msg;
559 {
560 	atperror(msg);
561 	exit(1);
562 }
563