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