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