1 /*
2 * Copyright 2009 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 /*
17 *
18 * Synopsis: atq [ -c ] [ -n ] [ name ... ]
19 *
20 *
21 * Print the queue of files waiting to be executed. These files
22 * were created by using the "at" command and are located in the
23 * directory defined by ATDIR.
24 */
25
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <sys/file.h>
29 #include <dirent.h>
30 #include <sys/stat.h>
31 #include <time.h>
32 #include <pwd.h>
33 #include <ctype.h>
34 #include <unistd.h>
35 #include <locale.h>
36 #include <errno.h>
37 #include <stdlib.h>
38 #include <string.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 static void atabortperror(char *msg);
60 static void atabort(char *msg);
61 static void aterror(char *msg);
62 static void atperror(char *msg);
63 static void usage(void);
64 static void printjobname(char *file);
65 static void printdate(char *filename);
66 static void printrank(int n);
67 static void printqueue(uid_t *uidlist, int nuids);
68
69 int
main(int argc,char ** argv)70 main(int argc, char **argv)
71 {
72
73 struct passwd *pp; /* password file entry pointer */
74 struct passwd pr;
75 int i;
76 int cflag = 0; /* print in order of creation time */
77 int nflag = 0; /* just print the number of jobs in */
78 /* queue */
79 extern int creation(); /* sort jobs by date of creation */
80 extern int execution(); /* sort jobs by date of execution */
81 int filewanted(); /* should file be included in queue? */
82 int countfiles(); /* count the number of files in queue */
83 /* for a given person */
84 uid_t *uidlist = NULL; /* array of spec. owner ID(s) requ. */
85 int argnum = 0; /* number of names passed as arg't */
86 int badarg = 0;
87 char *c;
88
89
90 --argc, ++argv;
91
92 (void) setlocale(LC_ALL, "");
93 pp = getpwuid(getuid());
94 pr.pw_uid = pp->pw_uid;
95 pr.pw_name = pp->pw_name;
96
97 if (pp == NULL)
98 atabort(INVALIDUSER);
99 if (!allowed(pp->pw_name, ATALLOW, ATDENY))
100 atabort(NOTALLOWED);
101
102 /*
103 * Interpret command line flags if they exist.
104 */
105 while (argc > 0 && **argv == '-') {
106 (*argv)++;
107 while (**argv) {
108 switch (*(*argv)++) {
109
110 case 'c' : cflag++;
111 break;
112
113 case 'n' : nflag++;
114 break;
115
116 default : usage();
117
118 }
119 }
120 --argc, ++argv;
121 }
122
123 /*
124 * If a certain name (or names) is requested, set a pointer to the
125 * beginning of the list.
126 */
127 if (argc > 0) {
128 ++namewanted;
129 uidlist = (uid_t *)malloc(argc * sizeof (uid_t));
130 if (uidlist == NULL)
131 atabortperror("can't allocate list of users");
132 for (i = 0; i < argc; i++) {
133 if (cron_admin(pr.pw_name) ||
134 strcmp(pr.pw_name, argv[i]) == 0) {
135 if ((pp = getpwnam(argv[i])) == NULL) {
136 (void) fprintf(stderr,
137 "atq: No such user %s\n", argv[i]);
138 exit(1);
139 }
140 uidlist[argnum] = pp->pw_uid;
141 argnum++;
142 }
143 else
144 badarg++;
145 }
146 if (badarg)
147 if (argnum)
148 printf("Printing queue information only "
149 "for %s:\n", pr.pw_name);
150 else {
151 printf("atq: Non-priviledged user cannot "
152 "request information regarding other "
153 "users\n");
154 exit(1);
155 }
156 } else if (!cron_admin(pr.pw_name)) {
157 /* no argument specified and the invoker is not root */
158 ++namewanted;
159 argnum = 1;
160 if ((uidlist = (uid_t *)malloc(sizeof (uid_t))) == NULL)
161 atabortperror("can't allocate list of users");
162 *uidlist = pr.pw_uid;
163 }
164
165 /*
166 * Move to the spooling area and scan the directory, placing the
167 * files in the queue structure. The queue comes back sorted by
168 * execution time or creation time.
169 */
170 if (chdir(ATDIR) == -1)
171 atabortperror(ATDIR);
172 if ((numentries = scandir(".", &queue, filewanted,
173 (cflag) ? creation : execution)) < 0)
174 atabortperror(ATDIR);
175
176
177 /*
178 * Either print a message stating:
179 *
180 * 1) that the spooling area is empty.
181 * 2) the number of jobs in the spooling area.
182 * 3) the number of jobs in the spooling area belonging to
183 * a certain person.
184 * 4) that the person requested doesn't have any files in the
185 * spooling area.
186 *
187 * or send the queue off to "printqueue" for printing.
188 *
189 * This whole process might seem a bit elaborate, but it's worthwhile
190 * to print some informative messages for the user.
191 *
192 */
193 if ((numentries == 0) && (!nflag)) {
194 printf("no files in queue.\n");
195 exit(0);
196 }
197 if (nflag) {
198 printf("%d\n", (namewanted) ?
199 countfiles(uidlist, argnum) : numentries);
200 exit(0);
201 }
202 if ((namewanted) && (countfiles(uidlist, argnum) == 0)) {
203 if (argnum == 1)
204 if (argnum != argc) c = pr.pw_name;
205 else c = *argv;
206 printf("no files for %s.\n", (argnum == 1) ?
207 c : "specified users");
208 exit(0);
209 }
210 printqueue(uidlist, argnum);
211 return (0);
212 }
213
214 /*
215 * Count the number of jobs in the spooling area owned by a certain person(s).
216 */
217 int
countfiles(uid_t * uidlist,int nuids)218 countfiles(uid_t *uidlist, int nuids)
219 {
220 int i, j; /* for loop indices */
221 int entryfound; /* found file owned by users */
222 int numfiles = 0; /* number of files owned by a */
223 /* certain person(s) */
224 uid_t *ptr; /* scratch pointer */
225 struct stat stbuf; /* buffer for file stats */
226
227
228 /*
229 * For each file in the queue, see if the user(s) own the file. We
230 * have to use "entryfound" (rather than simply incrementing "numfiles")
231 * so that if a person's name appears twice on the command line we
232 * don't double the number of files owned by him/her.
233 */
234 for (i = 0; i < numentries; i++) {
235 if ((stat(queue[i]->d_name, &stbuf)) < 0) {
236 continue;
237 }
238 ptr = uidlist;
239 entryfound = 0;
240
241 for (j = 0; j < nuids; j++) {
242 if (*ptr == stbuf.st_uid)
243 ++entryfound;
244 ++ptr;
245 }
246 if (entryfound)
247 ++numfiles;
248 }
249 return (numfiles);
250 }
251
252 /*
253 * Print the queue. If only jobs belonging to a certain person(s) are requested,
254 * only print jobs that belong to that person(s).
255 */
256 static void
printqueue(uid_t * uidlist,int nuids)257 printqueue(uid_t *uidlist, int nuids)
258 {
259 int i, j; /* for loop indices */
260 int rank; /* rank of a job */
261 int entryfound; /* found file owned by users */
262 char *getname();
263 uid_t *ptr; /* scratch pointer */
264 struct stat stbuf; /* buffer for file stats */
265 char curqueue; /* queue of current job */
266 char lastqueue; /* queue of previous job */
267
268 /*
269 * Print the header for the queue.
270 */
271 printf(" Rank Execution Date Owner Job "
272 "Queue Job Name\n");
273
274 /*
275 * Print the queue. If a certain name(s) was requested, print only jobs
276 * belonging to that person(s), otherwise print the entire queue.
277 * Once again, we have to use "entryfound" (rather than simply
278 * comparing each command line argument) so that if a person's name
279 * appears twice we don't print each file owned by him/her twice.
280 *
281 *
282 * "printrank", "printdate", and "printjobname" all take existing
283 * data and display it in a friendly manner.
284 *
285 */
286 lastqueue = '\0';
287 for (i = 0; i < numentries; i++) {
288 if ((stat(queue[i]->d_name, &stbuf)) < 0) {
289 continue;
290 }
291 curqueue = *(strchr(queue[i]->d_name, '.') + 1);
292 if (curqueue != lastqueue) {
293 rank = 1;
294 lastqueue = curqueue;
295 }
296 if (namewanted) {
297 ptr = uidlist;
298 entryfound = 0;
299
300 for (j = 0; j < nuids; j++) {
301 if (*ptr == stbuf.st_uid)
302 ++entryfound;
303 ++ptr;
304 }
305 if (!entryfound)
306 continue;
307 }
308 printrank(rank++);
309 printdate(queue[i]->d_name);
310 printf("%-10s ", getname(stbuf.st_uid));
311 printf("%-14s ", queue[i]->d_name);
312 printf(" %c", curqueue);
313 printjobname(queue[i]->d_name);
314 }
315 ++ptr;
316 }
317
318 /*
319 * Get the uid of a person using his/her login name. Return -1 if no
320 * such account name exists.
321 */
322 uid_t
getid(char * name)323 getid(char *name)
324 {
325
326 struct passwd *pwdinfo; /* password info structure */
327
328
329 if ((pwdinfo = getpwnam(name)) == 0)
330 return ((uid_t)-1);
331
332 return (pwdinfo->pw_uid);
333 }
334
335 /*
336 * Get the full login name of a person using his/her user id.
337 */
338 char *
getname(uid_t uid)339 getname(uid_t uid)
340 {
341 struct passwd *pwdinfo; /* password info structure */
342
343
344 if ((pwdinfo = getpwuid(uid)) == 0)
345 return ("???");
346 return (pwdinfo->pw_name);
347 }
348
349 /*
350 * Print the rank of a job. (I've got to admit it, I stole it from "lpq")
351 */
352 static void
printrank(int n)353 printrank(int n)
354 {
355 static char *r[] = {
356 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
357 };
358
359 if ((n/10) == 1)
360 printf("%3d%-5s", n, "th");
361 else
362 printf("%3d%-5s", n, r[n%10]);
363 }
364
365 /*
366 * Print the date that a job is to be executed. This takes some manipulation
367 * of the file name.
368 */
369 static void
printdate(char * filename)370 printdate(char *filename)
371 {
372 time_t jobdate;
373 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 static void
printjobname(char * file)403 printjobname(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 "scandir")
455 */
456 int
creation(struct dirent ** d1,struct dirent ** d2)457 creation(struct dirent **d1, struct dirent **d2)
458 {
459 char *p1, *p2;
460 int i;
461 struct stat stbuf1, stbuf2;
462 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 "scandir")
492 */
493 int
execution(struct dirent ** d1,struct dirent ** d2)494 execution(struct dirent **d1, struct dirent **d2)
495 {
496 char *p1, *p2;
497 int i;
498 char *name1, *name2;
499 time_t time1, time2;
500 int seq1, seq2;
501
502 name1 = (*d1)->d_name;
503 name2 = (*d2)->d_name;
504 if ((p1 = strchr(name1, '.')) == NULL)
505 return (1);
506 if ((p2 = strchr(name2, '.')) == NULL)
507 return (1);
508 p1++;
509 p2++;
510 if ((i = *p1++ - *p2++) != 0)
511 return (i);
512
513 time1 = num(&name1);
514 time2 = num(&name2);
515
516 if (time1 < time2)
517 return (-1);
518 else if (time1 > time2)
519 return (1);
520 p1++;
521 p2++;
522 seq1 = atoi(p1);
523 seq2 = atoi(p2);
524 return (seq1 - seq2);
525 }
526
527
528 /*
529 * Print usage info and exit.
530 */
531 static void
usage(void)532 usage(void)
533 {
534 fprintf(stderr, "usage: atq [-c] [-n] [name ...]\n");
535 exit(1);
536 }
537
538 static void
aterror(char * msg)539 aterror(char *msg)
540 {
541 fprintf(stderr, "atq: %s\n", msg);
542 }
543
544 static void
atperror(char * msg)545 atperror(char *msg)
546 {
547 fprintf(stderr, "atq: %s: %s\n", msg, errmsg(errno));
548 }
549
550 static void
atabort(char * msg)551 atabort(char *msg)
552 {
553 aterror(msg);
554 exit(1);
555 }
556
557 static void
atabortperror(char * msg)558 atabortperror(char *msg)
559 {
560 atperror(msg);
561 exit(1);
562 }
563