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
main(int argc,char ** argv)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 if (pp == NULL)
97 atabort(INVALIDUSER);
98 if (!allowed(pp->pw_name, ATALLOW, ATDENY))
99 atabort(NOTALLOWED);
100
101 pr.pw_uid = pp->pw_uid;
102 pr.pw_name = pp->pw_name;
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
countfiles(uid_t * uidlist,int nuids)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
printqueue(uid_t * uidlist,int nuids)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
getid(char * name)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 *
getname(uid_t uid)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
printrank(int n)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
printdate(char * filename)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
printjobname(char * file)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
creation(struct dirent ** d1,struct dirent ** d2)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
execution(struct dirent ** d1,struct dirent ** d2)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
usage(void)535 usage(void)
536 {
537 fprintf(stderr, "usage: atq [-c] [-n] [name ...]\n");
538 exit(1);
539 }
540
541 static void
aterror(char * msg)542 aterror(char *msg)
543 {
544 fprintf(stderr, "atq: %s\n", msg);
545 }
546
547 static void
atperror(char * msg)548 atperror(char *msg)
549 {
550 fprintf(stderr, "atq: %s: %s\n", msg, errmsg(errno));
551 }
552
553 static void
atabort(char * msg)554 atabort(char *msg)
555 {
556 aterror(msg);
557 exit(1);
558 }
559
560 static void
atabortperror(char * msg)561 atabortperror(char *msg)
562 {
563 atperror(msg);
564 exit(1);
565 }
566