Home | History | Annotate | Download | only in cron
      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  *	synopsis: atrm [-f] [-i] [-a] [[job #] [user] ...]
     18  *
     19  *
     20  *	Remove "at" jobs.
     21  */
     22 
     23 #include <stdio.h>
     24 #include <pwd.h>
     25 #include <ctype.h>
     26 #include <sys/types.h>
     27 #include <dirent.h>
     28 #include <sys/file.h>
     29 #include <sys/stat.h>
     30 #include <errno.h>
     31 #include <unistd.h>
     32 #include <locale.h>
     33 #include <strings.h>
     34 #include <stdlib.h>
     35 #include <libintl.h>
     36 #include "cron.h"
     37 #include "getresponse.h"
     38 
     39 extern time_t	num();
     40 extern char	*errmsg();
     41 extern void	audit_at_delete(char *, char *, int);
     42 
     43 #define	SUPERUSER	0			/* is user super-user? */
     44 #define	CANTCD		"can't change directory to the at directory"
     45 #define	NOREADDIR	"can't read the at directory"
     46 
     47 uid_t user;					/* person requesting removal */
     48 int fflag = 0;					/* suppress announcements? */
     49 int iflag = 0;					/* run interactively? */
     50 
     51 char login[UNAMESIZE];
     52 char login_authchk[UNAMESIZE]; /* used for authorization checks */
     53 
     54 #define	INVALIDUSER	"you are not a valid user (no entry in /etc/passwd)"
     55 #define	NOTALLOWED	"you are not authorized to use at.  Sorry."
     56 #define	NAMETOOLONG	"login name too long"
     57 
     58 static void usage(void);
     59 static void atabortperror(char *msg);
     60 static void atabort(char *msg);
     61 static void atperror(char *msg);
     62 static void atperror2(char *msg, char *name);
     63 static void aterror(char *msg);
     64 static void powner(char *file);
     65 
     66 int	getjoblist(struct dirent ***, struct stat ***, int (*)());
     67 int	removentry(char *, struct stat *, uid_t);
     68 
     69 int
     70 main(int argc, char **argv)
     71 {
     72 	int i;				/* for loop index */
     73 	int numjobs;			/* # of jobs in spooling area */
     74 	int allflag = 0;		/* remove all jobs belonging to user? */
     75 	int jobexists;			/* does a requested job exist? */
     76 	char *pp;
     77 	struct dirent **namelist;	/* names of jobs in spooling area */
     78 	struct stat **statlist;
     79 	struct passwd *pwd;
     80 
     81 	/*
     82 	 * If job number, user name, or "-" is not specified, just print
     83 	 * usage info and exit.
     84 	 */
     85 	(void) setlocale(LC_ALL, "");
     86 	(void) textdomain(TEXT_DOMAIN);
     87 	if (argc < 2)
     88 		usage();
     89 
     90 	--argc; ++argv;
     91 
     92 	pp = getuser((user = getuid()));
     93 	if (pp == NULL)
     94 		atabort(INVALIDUSER);
     95 	if (strlcpy(login, pp, sizeof (login)) >= sizeof (login))
     96 		atabort(NAMETOOLONG);
     97 	if (strlcpy(login_authchk, pp, sizeof (login_authchk))
     98 	    >= sizeof (NAMETOOLONG))
     99 		atabort(INVALIDUSER);
    100 	if (!allowed(login, ATALLOW, ATDENY))
    101 		atabort(NOTALLOWED);
    102 
    103 	/*
    104 	 * Process command line flags.
    105 	 * Special case the "-" option so that others may be grouped.
    106 	 */
    107 	while (argc > 0 && **argv == '-') {
    108 		*(*argv)++;
    109 		while (**argv) {
    110 			switch (*(*argv)++) {
    111 
    112 			case 'a':	++allflag;
    113 					break;
    114 
    115 			case 'f':	++fflag;
    116 					break;
    117 
    118 			case 'i':	++iflag;
    119 					break;
    120 
    121 			default:	usage();
    122 			}
    123 		}
    124 		++argv; --argc;
    125 	}
    126 
    127 	/*
    128 	 * If all jobs are to be removed and extra command line arguments
    129 	 * are given, print usage info and exit.
    130 	 */
    131 	if (allflag && argc)
    132 		usage();
    133 
    134 	/*
    135 	 * If only certain jobs are to be removed and no job #'s or user
    136 	 * names are specified, print usage info and exit.
    137 	 */
    138 	if (!allflag && !argc)
    139 		usage();
    140 
    141 	/*
    142 	 * If interactive removal and quiet removal are requested, override
    143 	 * quiet removal and run interactively.
    144 	 */
    145 	if (iflag && fflag)
    146 		fflag = 0;
    147 
    148 
    149 	/*
    150 	 * Move to spooling directory and get a list of the files in the
    151 	 * spooling area.
    152 	 */
    153 	numjobs = getjoblist(&namelist, &statlist, strcmp);
    154 	/*
    155 	 * If all jobs belonging to the user are to be removed, compare
    156 	 * the user's id to the owner of the file. If they match, remove
    157 	 * the file. If the user is the super-user, don't bother comparing
    158 	 * the id's. After all files are removed, exit (status 0).
    159 	 */
    160 	if (allflag) {
    161 		for (i = 0; i < numjobs; ++i) {
    162 			if (cron_admin(login_authchk) ||
    163 			    user == statlist[i]->st_uid)
    164 				(void) removentry(namelist[i]->d_name,
    165 				    statlist[i], user);
    166 		}
    167 		exit(0);
    168 	}
    169 
    170 	/*
    171 	 * If only certain jobs are to be removed, interpret each command
    172 	 * line argument. A check is done to see if it is a user's name or
    173 	 * a job number (inode #). If it's a user's name, compare the argument
    174 	 * to the files owner. If it's a job number, compare the argument to
    175 	 * the file name. In either case, if a match occurs, try to
    176 	 * remove the file.
    177 	 */
    178 
    179 	while (argc--) {
    180 		jobexists = 0;
    181 		for (i = 0; i < numjobs; ++i) {
    182 
    183 			/* if the inode number is 0, this entry was removed */
    184 			if (statlist[i]->st_ino == 0)
    185 				continue;
    186 
    187 			/*
    188 			 * if argv is a username, compare his/her uid to
    189 			 * the uid of the owner of the file......
    190 			 */
    191 			if (pwd = getpwnam(*argv)) {
    192 				if (statlist[i]->st_uid != pwd->pw_uid)
    193 					continue;
    194 			/*
    195 			 * otherwise, we assume that the argv is a job # and
    196 			 * thus compare argv to the file name.
    197 			 */
    198 			} else {
    199 				if (strcmp(namelist[i]->d_name, *argv))
    200 					continue;
    201 			}
    202 			++jobexists;
    203 			/*
    204 			 * if the entry is ultimately removed, don't
    205 			 * try to remove it again later.
    206 			 */
    207 			if (removentry(namelist[i]->d_name, statlist[i],
    208 			    user)) {
    209 				statlist[i]->st_ino = 0;
    210 			}
    211 		}
    212 
    213 		/*
    214 		 * If a requested argument doesn't exist, print a message.
    215 		 */
    216 		if (!jobexists && !fflag) {
    217 			fprintf(stderr, "atrm: %s: no such job number\n",
    218 			    *argv);
    219 		}
    220 		++argv;
    221 	}
    222 	return (0);
    223 }
    224 
    225 /*
    226  * Print usage info and exit.
    227  */
    228 static void
    229 usage(void)
    230 {
    231 	fprintf(stderr, "usage: atrm [-f] [-i] [-a] [[job #] [user] ...]\n");
    232 	exit(1);
    233 }
    234 
    235 
    236 /*
    237  * Remove an entry from the queue. The access of the file is checked for
    238  * write permission (since all jobs are mode 644). If access is granted,
    239  * unlink the file. If the fflag (suppress announcements) is not set,
    240  * print the job number that we are removing and the result of the access
    241  * check (either "permission denied" or "removed"). If we are running
    242  * interactively (iflag), prompt the user before we unlink the file. If
    243  * the super-user is removing jobs, inform him/her who owns each file before
    244  * it is removed.  Return TRUE if file removed, else FALSE.
    245  */
    246 int
    247 removentry(char *filename, struct stat *statptr, uid_t user)
    248 {
    249 	struct passwd *pwd;
    250 	char *pp;
    251 	int r;
    252 
    253 	if (init_yes() < 0) {
    254 		(void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
    255 		    strerror(errno));
    256 		exit(1);
    257 	}
    258 
    259 	if (!fflag)
    260 		printf("%s: ", filename);
    261 
    262 	if (user != statptr->st_uid && !cron_admin(login_authchk)) {
    263 
    264 		if (!fflag) {
    265 			printf("permission denied\n");
    266 		}
    267 		return (0);
    268 
    269 	} else {
    270 		if (iflag) {
    271 			if (cron_admin(login_authchk)) {
    272 				printf("\t(owned by ");
    273 				powner(filename);
    274 				printf(") ");
    275 			}
    276 			printf(gettext("remove it? "));
    277 			if (yes() == 0)
    278 				return (0);
    279 		}
    280 
    281 		if (cron_admin(login_authchk)) {
    282 			pp = getuser((uid_t)statptr->st_uid);
    283 			if (pp == NULL)
    284 				atabort(INVALIDUSER);
    285 			if (strlcpy(login, pp, sizeof (login)) >=
    286 			    sizeof (login))
    287 				atabort(NAMETOOLONG);
    288 		}
    289 		cron_sendmsg(DELETE, login, filename, AT);
    290 		if ((r = unlink(filename)) < 0) {
    291 			if (!fflag) {
    292 				fputs("could not remove\n", stdout);
    293 				(void) fprintf(stderr, "atrm: %s: %s\n",
    294 				    filename, errmsg(errno));
    295 			}
    296 			audit_at_delete(filename, NULL, r);
    297 			return (0);
    298 		}
    299 		audit_at_delete(filename, NULL, r);
    300 		if (!fflag && !iflag)
    301 			printf("removed\n");
    302 		return (1);
    303 	}
    304 }
    305 
    306 /*
    307  * Print the owner of the job. This is the owner of the spoolfile.
    308  * If we run into trouble getting the name, we'll just print "???".
    309  */
    310 static void
    311 powner(char *file)
    312 {
    313 	struct stat statb;
    314 	char *getname();
    315 
    316 	if (stat(file, &statb) < 0) {
    317 		printf("%s", "???");
    318 		(void) fprintf(stderr, "atrm: Couldn't stat spoolfile %s: %s\n",
    319 		    file, errmsg(errno));
    320 		return;
    321 	}
    322 
    323 	printf("%s", getname(statb.st_uid));
    324 }
    325 
    326 
    327 int
    328 getjoblist(struct dirent ***namelistp, struct stat ***statlistp,
    329     int (*sortfunc)())
    330 {
    331 	int numjobs;
    332 	struct dirent **namelist;
    333 	int i;
    334 	struct stat *statptr;	/* pointer to file stat structure */
    335 	struct stat **statlist;
    336 	extern int filewanted();	/* should a file be listed in queue? */
    337 	if (chdir(ATDIR) < 0)
    338 		atabortperror(CANTCD);
    339 
    340 	/*
    341 	 * Get a list of the files in the spooling area.
    342 	 */
    343 	if ((numjobs = scandir(".", namelistp, filewanted, sortfunc)) < 0)
    344 		atabortperror(NOREADDIR);
    345 
    346 	if ((statlist =
    347 	    (struct stat **)malloc(numjobs * sizeof (struct stat ***)))
    348 	    == NULL)
    349 		atabort("Out of memory");
    350 
    351 	namelist = *namelistp;
    352 
    353 	/*
    354 	 * Build an array of pointers to the file stats for all jobs in
    355 	 * the spooling area.
    356 	 */
    357 	for (i = 0; i < numjobs; ++i) {
    358 		statptr = (struct stat *)malloc(sizeof (struct stat));
    359 		if (statptr == NULL)
    360 			atabort("Out of memory");
    361 		if (stat(namelist[i]->d_name, statptr) < 0) {
    362 			atperror2("Can't stat", namelist[i]->d_name);
    363 			continue;
    364 		}
    365 		statlist[i] = statptr;
    366 	}
    367 
    368 	*statlistp = statlist;
    369 	return (numjobs);
    370 }
    371 
    372 /*
    373  * Get the full login name of a person using his/her user id.
    374  */
    375 char *
    376 getname(uid_t uid)
    377 {
    378 	struct passwd *pwdinfo;		/* password info structure */
    379 
    380 
    381 	if ((pwdinfo = getpwuid(uid)) == 0)
    382 		return ("???");
    383 	return (pwdinfo->pw_name);
    384 }
    385 
    386 static void
    387 aterror(char *msg)
    388 {
    389 	fprintf(stderr, "atrm: %s\n", msg);
    390 }
    391 
    392 static void
    393 atperror(char *msg)
    394 {
    395 	fprintf(stderr, "atrm: %s: %s\n", msg, errmsg(errno));
    396 }
    397 
    398 static void
    399 atperror2(char *msg, char *name)
    400 {
    401 	fprintf(stderr, "atrm: %s %s: %s\n", msg, name, errmsg(errno));
    402 }
    403 
    404 static void
    405 atabort(char *msg)
    406 {
    407 	aterror(msg);
    408 	exit(1);
    409 }
    410 
    411 static void
    412 atabortperror(char *msg)
    413 {
    414 	atperror(msg);
    415 	exit(1);
    416 }
    417