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