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.
7 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
8 /* All Rights Reserved */
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.
19 * Synopsis: atq [ -c ] [ -n ] [ name ... ]
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.
28 #include <sys/types.h>
42 extern char *errmsg();
43 extern char *strchr();
48 static char *mthnames
[12] = {
49 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
50 "Aug", "Sep", "Oct", "Nov", "Dec",
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 */
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."
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
);
71 main(int argc
, char **argv
)
74 struct passwd
*pp
; /* password file entry pointer */
77 int cflag
= 0; /* print in order of creation time */
78 int nflag
= 0; /* just print the number of jobs in */
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 */
93 (void) setlocale(LC_ALL
, "");
94 pp
= getpwuid(getuid());
95 pr
.pw_uid
= pp
->pw_uid
;
96 pr
.pw_name
= pp
->pw_name
;
100 if (!allowed(pp
->pw_name
, ATALLOW
, ATDENY
))
104 * Interpret command line flags if they exist.
106 while (argc
> 0 && **argv
== '-') {
109 switch (*(*argv
)++) {
125 * If a certain name (or names) is requested, set a pointer to the
126 * beginning of the list.
130 uidlist
= (uid_t
*)malloc(argc
* sizeof (uid_t
));
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
]);
141 uidlist
[argnum
] = pp
->pw_uid
;
149 printf("Printing queue information only "
150 "for %s:\n", pr
.pw_name
);
152 printf("atq: Non-priviledged user cannot "
153 "request information regarding other "
157 } else if (!cron_admin(pr
.pw_name
)) {
158 /* no argument specified and the invoker is not root */
161 if ((uidlist
= (uid_t
*)malloc(sizeof (uid_t
))) == NULL
)
162 atabortperror("can't allocate list of users");
163 *uidlist
= pr
.pw_uid
;
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.
171 if (chdir(ATDIR
) == -1)
172 atabortperror(ATDIR
);
173 if ((numentries
= scandir(".", &queue
, filewanted
,
174 (cflag
) ? creation
: execution
)) < 0)
175 atabortperror(ATDIR
);
179 * Either print a message stating:
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
185 * 4) that the person requested doesn't have any files in the
188 * or send the queue off to "printqueue" for printing.
190 * This whole process might seem a bit elaborate, but it's worthwhile
191 * to print some informative messages for the user.
194 if ((numentries
== 0) && (!nflag
)) {
195 printf("no files in queue.\n");
199 printf("%d\n", (namewanted
) ?
200 countfiles(uidlist
, argnum
) : numentries
);
203 if ((namewanted
) && (countfiles(uidlist
, argnum
) == 0)) {
205 if (argnum
!= argc
) c
= pr
.pw_name
;
207 printf("no files for %s.\n", (argnum
== 1) ?
208 c
: "specified users");
211 printqueue(uidlist
, argnum
);
216 * Count the number of jobs in the spooling area owned by a certain person(s).
219 countfiles(uid_t
*uidlist
, int nuids
)
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 */
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.
235 for (i
= 0; i
< numentries
; i
++) {
236 if ((stat(queue
[i
]->d_name
, &stbuf
)) < 0) {
242 for (j
= 0; j
< nuids
; j
++) {
243 if (*ptr
== stbuf
.st_uid
)
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).
258 printqueue(uid_t
*uidlist
, int nuids
)
260 int i
, j
; /* for loop indices */
261 int rank
; /* rank of a job */
262 int entryfound
; /* found file owned by users */
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 */
270 * Print the header for the queue.
272 printf(" Rank Execution Date Owner Job "
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.
283 * "printrank", "printdate", and "printjobname" all take existing
284 * data and display it in a friendly manner.
288 for (i
= 0; i
< numentries
; i
++) {
289 if ((stat(queue
[i
]->d_name
, &stbuf
)) < 0) {
292 curqueue
= *(strchr(queue
[i
]->d_name
, '.') + 1);
293 if (curqueue
!= lastqueue
) {
295 lastqueue
= curqueue
;
301 for (j
= 0; j
< nuids
; j
++) {
302 if (*ptr
== stbuf
.st_uid
)
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
);
320 * Get the uid of a person using their login name. Return -1 if no
321 * such account name exists.
327 struct passwd
*pwdinfo
; /* password info structure */
330 if ((pwdinfo
= getpwnam(name
)) == 0)
333 return (pwdinfo
->pw_uid
);
337 * Get the full login name of a person using their user id.
342 struct passwd
*pwdinfo
; /* password info structure */
345 if ((pwdinfo
= getpwuid(uid
)) == 0)
347 return (pwdinfo
->pw_name
);
351 * Print the rank of a job. (I've got to admit it, I stole it from "lpq")
357 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
361 printf("%3d%-5s", n
, "th");
363 printf("%3d%-5s", n
, r
[n
%10]);
367 * Print the date that a job is to be executed. This takes some manipulation
371 printdate(char *filename
)
374 struct tm
*unpackeddate
;
375 char date
[18]; /* reformatted execution date */
378 * Convert the file name to a date.
380 jobdate
= num(&filename
);
381 unpackeddate
= localtime(&jobdate
);
383 /* years since 1900 + base century 1900 */
384 unpackeddate
->tm_year
+= 1900;
386 * Format the execution date of a job.
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
);
393 * Print the date the job will be executed.
395 printf("%-21.18s", date
);
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 "???".
404 printjobname(char *file
)
406 char *ptr
; /* scratch pointer */
407 char jobname
[28]; /* the job name */
408 FILE *filename
; /* job file in spooling area */
411 * Open the job file and grab the third line.
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
));
422 * Skip over the first and second lines.
424 fscanf(filename
, "%*[^\n]\n");
427 * Now get the job name.
429 if (fscanf(filename
, ": jobname: %27s%*[^\n]\n", jobname
) != 1) {
430 printf("%.27s\n", "???");
437 * Put a pointer at the begining of the line and remove the basename
441 if ((ptr
= (char *)strrchr(jobname
, '/')) != 0)
446 if (strlen(ptr
) > 23)
447 printf("%.23s ...\n", ptr
);
449 printf("%.27s\n", ptr
);
455 * Sort files by queue, time of creation, and sequence. (used by "scandir")
458 creation(struct dirent
**d1
, struct dirent
**d2
)
462 struct stat stbuf1
, stbuf2
;
465 if ((p1
= strchr((*d1
)->d_name
, '.')) == NULL
)
467 if ((p2
= strchr((*d2
)->d_name
, '.')) == NULL
)
471 if ((i
= *p1
++ - *p2
++) != 0)
474 if (stat((*d1
)->d_name
, &stbuf1
) < 0)
477 if (stat((*d2
)->d_name
, &stbuf2
) < 0)
480 if (stbuf1
.st_ctime
< stbuf2
.st_ctime
)
482 else if (stbuf1
.st_ctime
> stbuf2
.st_ctime
)
488 return (seq1
- seq2
);
492 * Sort files by queue, time of execution, and sequence. (used by "scandir")
495 execution(struct dirent
**d1
, struct dirent
**d2
)
503 name1
= (*d1
)->d_name
;
504 name2
= (*d2
)->d_name
;
505 if ((p1
= strchr(name1
, '.')) == NULL
)
507 if ((p2
= strchr(name2
, '.')) == NULL
)
511 if ((i
= *p1
++ - *p2
++) != 0)
519 else if (time1
> time2
)
525 return (seq1
- seq2
);
530 * Print usage info and exit.
535 fprintf(stderr
, "usage: atq [-c] [-n] [name ...]\n");
542 fprintf(stderr
, "atq: %s\n", msg
);
548 fprintf(stderr
, "atq: %s: %s\n", msg
, errmsg(errno
));
559 atabortperror(char *msg
)