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