xref: /illumos-gate/usr/src/cmd/cron/atq.c (revision ffb6483089015eb90be1f5e7fc2a96c9929546a6)
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  * Copyright 2021 Joyent, Inc.
6  */
7 
8 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
9 /*	  All Rights Reserved  	*/
10 
11 
12 /*
13  * Copyright (c) 1983 Regents of the University of California.
14  * All rights reserved.  The Berkeley software License Agreement
15  * specifies the terms and conditions for redistribution.
16  */
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 (cron_admin(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 (!cron_admin(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 that user.
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 of their files 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 their 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 their 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[19];				/* 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 	snprintf(date, sizeof (date), "%3s %2d, %4d %02d:%02d",
390 	    mthnames[unpackeddate->tm_mon],
391 	    unpackeddate->tm_mday, unpackeddate->tm_year,
392 	    unpackeddate->tm_hour, unpackeddate->tm_min);
393 
394 	/*
395 	 * Print the date the job will be executed.
396 	 */
397 	printf("%-21.18s", date);
398 }
399 
400 /*
401  * Print a job name. If the old "at" has been used to create the spoolfile,
402  * the three line header that the new version of "at" puts in the spoolfile.
403  * Thus, we just print "???".
404  */
405 static void
406 printjobname(char *file)
407 {
408 	char *ptr;				/* scratch pointer */
409 	char jobname[28];			/* the job name */
410 	FILE *filename;				/* job file in spooling area */
411 
412 	/*
413 	 * Open the job file and grab the third line.
414 	 */
415 	printf("     ");
416 
417 	if ((filename = fopen(file, "r")) == NULL) {
418 		printf("%.27s\n", "???");
419 		(void) fprintf(stderr, "atq: Can't open job file %s: %s\n",
420 		    file, errmsg(errno));
421 		return;
422 	}
423 	/*
424 	 * Skip over the first and second lines.
425 	 */
426 	fscanf(filename, "%*[^\n]\n");
427 
428 	/*
429 	 * Now get the job name.
430 	 */
431 	if (fscanf(filename, ": jobname: %27s%*[^\n]\n", jobname) != 1) {
432 		printf("%.27s\n", "???");
433 		fclose(filename);
434 		return;
435 	}
436 	fclose(filename);
437 
438 	/*
439 	 * Put a pointer at the begining of the line and remove the basename
440 	 * from the job file.
441 	 */
442 	ptr = jobname;
443 	if ((ptr = (char *)strrchr(jobname, '/')) != 0)
444 		++ptr;
445 	else
446 		ptr = jobname;
447 
448 	if (strlen(ptr) > 23)
449 		printf("%.23s ...\n", ptr);
450 	else
451 		printf("%.27s\n", ptr);
452 }
453 
454 
455 
456 /*
457  * Sort files by queue, time of creation, and sequence. (used by "scandir")
458  */
459 int
460 creation(struct dirent **d1, struct dirent **d2)
461 {
462 	char *p1, *p2;
463 	int i;
464 	struct stat stbuf1, stbuf2;
465 	int seq1, seq2;
466 
467 	if ((p1 = strchr((*d1)->d_name, '.')) == NULL)
468 		return (0);
469 	if ((p2 = strchr((*d2)->d_name, '.')) == NULL)
470 		return (0);
471 	p1++;
472 	p2++;
473 	if ((i = *p1++ - *p2++) != 0)
474 		return (i);
475 
476 	if (stat((*d1)->d_name, &stbuf1) < 0)
477 		return (0);
478 
479 	if (stat((*d2)->d_name, &stbuf2) < 0)
480 		return (0);
481 
482 	if (stbuf1.st_ctime < stbuf2.st_ctime)
483 		return (-1);
484 	else if (stbuf1.st_ctime > stbuf2.st_ctime)
485 		return (1);
486 	p1++;
487 	p2++;
488 	seq1 = atoi(p1);
489 	seq2 = atoi(p2);
490 	return (seq1 - seq2);
491 }
492 
493 /*
494  * Sort files by queue, time of execution, and sequence. (used by "scandir")
495  */
496 int
497 execution(struct dirent **d1, struct dirent **d2)
498 {
499 	char *p1, *p2;
500 	int i;
501 	char *name1, *name2;
502 	time_t time1, time2;
503 	int seq1, seq2;
504 
505 	name1 = (*d1)->d_name;
506 	name2 = (*d2)->d_name;
507 	if ((p1 = strchr(name1, '.')) == NULL)
508 		return (1);
509 	if ((p2 = strchr(name2, '.')) == NULL)
510 		return (1);
511 	p1++;
512 	p2++;
513 	if ((i = *p1++ - *p2++) != 0)
514 		return (i);
515 
516 	time1 = num(&name1);
517 	time2 = num(&name2);
518 
519 	if (time1 < time2)
520 		return (-1);
521 	else if (time1 > time2)
522 		return (1);
523 	p1++;
524 	p2++;
525 	seq1 = atoi(p1);
526 	seq2 = atoi(p2);
527 	return (seq1 - seq2);
528 }
529 
530 
531 /*
532  * Print usage info and exit.
533  */
534 static void
535 usage(void)
536 {
537 	fprintf(stderr, "usage:	atq [-c] [-n] [name ...]\n");
538 	exit(1);
539 }
540 
541 static void
542 aterror(char *msg)
543 {
544 	fprintf(stderr, "atq: %s\n", msg);
545 }
546 
547 static void
548 atperror(char *msg)
549 {
550 	fprintf(stderr, "atq: %s: %s\n", msg, errmsg(errno));
551 }
552 
553 static void
554 atabort(char *msg)
555 {
556 	aterror(msg);
557 	exit(1);
558 }
559 
560 static void
561 atabortperror(char *msg)
562 {
563 	atperror(msg);
564 	exit(1);
565 }
566