xref: /csrg-svn/usr.bin/at/atrm/atrm.c (revision 61913)
148234Sbostic /*-
2*61913Sbostic  * Copyright (c) 1983, 1993
3*61913Sbostic  *	The Regents of the University of California.  All rights reserved.
448234Sbostic  *
548234Sbostic  * %sccs.include.proprietary.c%
622420Sdist  */
722420Sdist 
816820Swall #ifndef lint
9*61913Sbostic static char copyright[] =
10*61913Sbostic "@(#) Copyright (c) 1983, 1993\n\
11*61913Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1248234Sbostic #endif /* not lint */
1316820Swall 
1422420Sdist #ifndef lint
15*61913Sbostic static char sccsid[] = "@(#)atrm.c	8.1 (Berkeley) 06/06/93";
1648234Sbostic #endif /* not lint */
1722420Sdist 
1816820Swall /*
1916829Smckusick  *	synopsis: atrm [-f] [-i] [-] [[job #] [user] ...]
2016820Swall  *
2116820Swall  *
2216829Smckusick  *	Remove files from the directory /usr/spool/at. These files
2316829Smckusick  *	represent jobs to be run at a later date.
2416820Swall  *
2516829Smckusick  *	Author: Steve Wall
2616829Smckusick  *		Computer Systems Research Group
2716829Smckusick  *		University of California @ Berkeley
2816820Swall  *
2916820Swall  */
3016820Swall 
3116820Swall #include <sys/types.h>
3216820Swall #include <sys/dir.h>
3316820Swall #include <sys/file.h>
3416820Swall #include <sys/stat.h>
3537347Sbostic #include <stdio.h>
3637347Sbostic #include <pwd.h>
3737347Sbostic #include <ctype.h>
3837962Sbostic #include "pathnames.h"
3916820Swall 
4016829Smckusick #define SUPERUSER	0			/* is user super-user? */
4116829Smckusick #define MAXENTRIES	1000			/* max # of entries allowed */
4216820Swall 
4316829Smckusick int user;					/* person requesting removal */
4416829Smckusick int fflag = 0;					/* suppress announcements? */
4516829Smckusick int iflag = 0;					/* run interactively? */
4616820Swall 
main(argc,argv)4716820Swall main(argc,argv)
4816820Swall int argc;
4916820Swall char **argv;
5016820Swall 
5116820Swall {
5230410Skarels 	register int i;			/* for loop index */
5316829Smckusick 	int isuname;			/* is a command line argv a user name?*/
5416829Smckusick 	int numjobs;			/* # of jobs in spooling area */
5516829Smckusick 	int usage();			/* print usage info and exit */
5616829Smckusick 	int allflag = 0;		/* remove all jobs belonging to user? */
5730410Skarels 	int jobno;
5816829Smckusick 	int jobexists;			/* does a requested job exist? */
5916829Smckusick 	int alphasort();		/* sort jobs by date of execution */
6016829Smckusick 	int filewanted();		/* should a file be listed in queue? */
6130410Skarels 	char *myname, *getname();	/* current user's name */
6230410Skarels 	char *owner, *fowner();
6316829Smckusick 	struct stat *statptr;		/* pointer to file stat structure */
6416829Smckusick 	struct stat *stbuf[MAXENTRIES]; /* array of pointers to stat structs */
6516829Smckusick 	struct direct **namelist;	/* names of jobs in spooling area */
6616820Swall 
6716820Swall 
6816829Smckusick 	/*
6916829Smckusick 	 * If job number, user name, or "-" is not specified, just print
7016829Smckusick 	 * usage info and exit.
7116829Smckusick 	 */
7216829Smckusick 	if (argc < 2)
7316829Smckusick 		usage();
7416820Swall 
7516829Smckusick 	--argc; ++argv;
7616820Swall 
7716829Smckusick 	/*
7816829Smckusick 	 * Process command line flags.
7928873Smckusick 	 * Special case the "-" option so that others may be grouped.
8016829Smckusick 	 */
8128873Smckusick 	while (argc > 0 && **argv == '-') {
8228873Smckusick 		if (*(++(*argv)) == '\0') {
8328873Smckusick 			++allflag;
8428873Smckusick 		} else while (**argv) switch (*(*argv)++) {
8516820Swall 
8616829Smckusick 			case 'f':	++fflag;
8716829Smckusick 					break;
8816829Smckusick 
8916829Smckusick 			case 'i':	++iflag;
9016829Smckusick 					break;
9116829Smckusick 
9216829Smckusick 			default:	usage();
9316829Smckusick 		}
9416829Smckusick 		++argv; --argc;
9516829Smckusick 	}
9616820Swall 
9716829Smckusick 	/*
9816829Smckusick 	 * If all jobs are to be removed and extra command line arguments
9916829Smckusick 	 * are given, print usage info and exit.
10016829Smckusick 	 */
10116829Smckusick 	if (allflag && argc)
10216829Smckusick 		usage();
10316820Swall 
10416829Smckusick 	/*
10516829Smckusick 	 * If only certain jobs are to be removed and no job #'s or user
10616829Smckusick 	 * names are specified, print usage info and exit.
10716829Smckusick 	 */
10816829Smckusick 	if (!allflag && !argc)
10916829Smckusick 		usage();
11016820Swall 
11116829Smckusick 	/*
11216829Smckusick 	 * If interactive removal and quiet removal are requested, override
11316829Smckusick 	 * quiet removal and run interactively.
11416829Smckusick 	 */
11516829Smckusick 	if (iflag && fflag)
11616829Smckusick 		fflag = 0;
11716820Swall 
11816829Smckusick 	/*
11916829Smckusick 	 * Move to spooling area and get user id of person requesting removal.
12016829Smckusick 	 */
12137962Sbostic 	if (chdir(_PATH_ATDIR) == -1) {
12237962Sbostic 		perror(_PATH_ATDIR);
12316829Smckusick 		exit(1);
12416829Smckusick 	}
12516829Smckusick 	user = getuid();
12630410Skarels 	myname = getname(user);
12716820Swall 
12816829Smckusick 	/*
12916829Smckusick 	 * Get a list of the files in the spooling area.
13016829Smckusick 	 */
13116866Swall 	if ((numjobs = scandir(".",&namelist,filewanted,alphasort)) < 0) {
13237962Sbostic 		perror(_PATH_ATDIR);
13316829Smckusick 		exit(1);
13416829Smckusick 	}
13516820Swall 
13616829Smckusick 	/*
13716829Smckusick 	 * Build an array of pointers to the file stats for all jobs in
13816829Smckusick 	 * the spooling area.
13916829Smckusick 	 */
14016829Smckusick 	for (i = 0; i < numjobs; ++i) {
14116829Smckusick 		statptr = (struct stat *) malloc(sizeof(struct stat));
14216829Smckusick 		if (statptr == NULL) {
14316829Smckusick 			perror("malloc");
14416829Smckusick 			exit(1);
14516829Smckusick 		}
14616829Smckusick 		if (stat(namelist[i]->d_name,statptr) < 0) {
14716829Smckusick 			perror("stat");
14816829Smckusick 			continue;
14916829Smckusick 		}
15016829Smckusick 		stbuf[i] = statptr;
15116829Smckusick 	}
15216820Swall 
15316829Smckusick 	/*
15416829Smckusick 	 * If all jobs belonging to the user are to be removed, compare
15516829Smckusick 	 * the user's id to the owner of the file. If they match, remove
15616829Smckusick 	 * the file. If the user is the super-user, don't bother comparing
15716829Smckusick 	 * the id's. After all files are removed, exit (status 0).
15816829Smckusick 	 */
15916829Smckusick 	if (allflag) {
16016829Smckusick 		for (i = 0; i < numjobs; ++i) {
16130410Skarels 			owner = fowner(namelist[i]->d_name);
16230410Skarels 			if (isowner(myname, owner))
16330410Skarels 				removentry(namelist[i]->d_name,
16430410Skarels 				    (int)stbuf[i]->st_ino, NULL);
16516829Smckusick 		}
16616829Smckusick 		exit(0);
16716829Smckusick 	}
16816820Swall 
16916829Smckusick 	/*
17016829Smckusick 	 * If only certain jobs are to be removed, interpret each command
17116829Smckusick 	 * line argument. A check is done to see if it is a user's name or
17216829Smckusick 	 * a job number (inode #). If it's a user's name, compare the argument
17316829Smckusick 	 * to the files owner. If it's a job number, compare the argument to
17416829Smckusick 	 * the inode number of the file. In either case, if a match occurs,
17516829Smckusick 	 * try to remove the file. (The function "isusername" scans the
17616829Smckusick 	 * argument to see if it is all digits which we will assume means
17716829Smckusick 	 * that it's a job number (a fairly safe assumption?). This is done
17816829Smckusick 	 * because we have to determine whether we are dealing with a user
17916829Smckusick 	 * name or a job number. By assuming that only arguments that are
18016829Smckusick 	 * all digits is a job number, we allow users to have digits in
18116829Smckusick 	 * their login name i.e. "johndoe2").
18216829Smckusick 	 */
18316820Swall 
18416829Smckusick 	while (argc--) {
18516829Smckusick 		jobexists = 0;
18616829Smckusick 		isuname = isusername(*argv);
18730410Skarels 		if (!isuname)
18830410Skarels 			jobno = atoi(*argv);
18916829Smckusick 		for (i = 0; i < numjobs; ++i) {
19016820Swall 
19128873Smckusick 			/* if the inode number is 0, this entry was removed */
19228873Smckusick 			if (stbuf[i]->st_ino == 0)
19328873Smckusick 				continue;
19428873Smckusick 
19530410Skarels 			owner = fowner(namelist[i]->d_name);
19616829Smckusick 			/*
19730410Skarels 			 * if argv is a username, compare it to
19830410Skarels 			 * the owner of the file......
19930410Skarels 			 * otherwise, we assume that the argv is a job # and
20030410Skarels 			 * thus compare argv to the inode (job #) of the file.
20116829Smckusick 			 */
20216829Smckusick 			if (isuname) {
20330410Skarels 				if (strcmp(*argv, owner))
20416829Smckusick 					continue;
20516829Smckusick 			} else {
20630410Skarels 				if (stbuf[i]->st_ino != jobno)
20716829Smckusick 					continue;
20816829Smckusick 			}
20916829Smckusick 			++jobexists;
21028873Smckusick 			/*
21130410Skarels 			 * if the entry is removed, don't
21228873Smckusick 			 * try to remove it again later.
21328873Smckusick 			 */
21430410Skarels 			if (user == SUPERUSER || isowner(myname, owner)) {
21530410Skarels 				removentry(namelist[i]->d_name,
21630410Skarels 				    (int)stbuf[i]->st_ino, owner);
21728873Smckusick 				stbuf[i]->st_ino = 0;
21830410Skarels 			} else if (!fflag)
21930410Skarels 				printf("%6d: permission denied\n",
22030410Skarels 				    stbuf[i]->st_ino);
22130410Skarels 			if (!isuname)
22230410Skarels 				break;
22316829Smckusick 		}
22416820Swall 
22516829Smckusick 		/*
22616829Smckusick 		 * If a requested argument doesn't exist, print a message.
22716829Smckusick 		 */
22816829Smckusick 		if (!jobexists && !fflag && !isuname) {
22928873Smckusick 			fprintf(stderr, "%6s: no such job number\n", *argv);
23016829Smckusick 		}
23116829Smckusick 		++argv;
23216829Smckusick 	}
23316829Smckusick 	exit(0);
23416820Swall }
23516820Swall 
23616820Swall /*
23716820Swall  * Print usage info and exit.
23816820Swall  */
usage()23916820Swall usage()
24016820Swall {
24116829Smckusick 	fprintf(stderr,"usage: atrm [-f] [-i] [-] [[job #] [user] ...]\n");
24216829Smckusick 	exit(1);
24316820Swall }
24416820Swall 
24516820Swall /*
24616820Swall  * Do we want to include a file in the queue? (used by "scandir") We are looking
24716820Swall  * for files with following syntax: yy.ddd.hhhh. so the test is made to see if
24816820Swall  * the file name has three dots in it. This test will suffice since the only
24916820Swall  * other files in /usr/spool/at don't have any dots in their name.
25016820Swall  */
25116820Swall filewanted(direntry)
25216820Swall struct direct *direntry;
25316820Swall {
25416829Smckusick 	int numdot = 0;			/* number of dots in a filename */
25516829Smckusick 	char *filename;			/* filename we are looking at */
25616820Swall 
25716829Smckusick 	filename = direntry->d_name;
25816829Smckusick 	while (*filename)
25916829Smckusick 		numdot += (*(filename++) == '.');
26016829Smckusick 	return(numdot == 3);
26116820Swall }
26216820Swall 
26316820Swall /*
26416820Swall  * Is a command line argument a username? As noted above we will assume
26516820Swall  * that an argument that is all digits means that it's a job number, not
26616820Swall  * a user's name. We choose to determine whether an argument is a user name
26716820Swall  * in this manner because then it's ok for someone to have digits in their
26816820Swall  * user name.
26916820Swall  */
isusername(string)27016820Swall isusername(string)
27116820Swall char *string;
27216820Swall {
27316829Smckusick 	char *ptr;			/* pointer used for scanning string */
27416820Swall 
27516829Smckusick 	ptr = string;
27616829Smckusick 	while (isdigit(*ptr))
27716829Smckusick 		++ptr;
27816829Smckusick 	return((*ptr == '\0') ? 0 : 1);
27916820Swall }
28016820Swall 
28116820Swall /*
28216820Swall  * Remove an entry from the queue. The access of the file is checked for
28316820Swall  * write permission (since all jobs are mode 644). If access is granted,
28416820Swall  * unlink the file. If the fflag (suppress announcements) is not set,
28516820Swall  * print the job number that we are removing and the result of the access
28616866Swall  * check (either "permission denied" or "removed"). If we are running
28716866Swall  * interactively (iflag), prompt the user before we unlink the file. If
28816866Swall  * the super-user is removing jobs, inform him/her who owns each file before
28930410Skarels  * it is removed.
29016820Swall  */
removentry(filename,inode,owner)29130410Skarels removentry(filename, inode, owner)
29216820Swall char *filename;
29316820Swall int inode;
29430410Skarels char *owner;
29516820Swall {
29616820Swall 
29716829Smckusick 	if (!fflag)
29816829Smckusick 		printf("%6d: ",inode);
29916820Swall 
30030410Skarels 	if (iflag) {
30130410Skarels 		if (user == SUPERUSER && owner)
30230410Skarels 			printf("\t(owned by %s) ", owner);
30330410Skarels 		printf("remove? ");
30430410Skarels 		if (!yes())
30530410Skarels 			return;
30616829Smckusick 	}
30730410Skarels 	if (unlink(filename) < 0)
30830410Skarels 		perror(filename);
30930410Skarels 	else if (!fflag && !iflag)
31030410Skarels 		printf("removed\n");
31116820Swall }
31216820Swall 
31316820Swall /*
31430410Skarels  * See if "name" owns job owned by "jobname".
31516866Swall  */
isowner(name,jobname)31630410Skarels isowner(name,jobname)
31716866Swall char *name;
31830410Skarels char *jobname;
31916866Swall {
32016866Swall 
32130410Skarels 	return (strcmp(name,jobname) == 0);
32216866Swall }
32316866Swall 
32416866Swall /*
32530410Skarels  * Return the owner of the job. This is stored on the first line of the
32630410Skarels  * spoolfile. If we run into trouble getting the name, we'll just return "???".
32716866Swall  */
32830410Skarels char *
fowner(file)32930410Skarels fowner(file)
33016866Swall char *file;
33116866Swall {
33230410Skarels 	static char owner[128];			/* the owner */
33316866Swall 	FILE *infile;				/* I/O stream to spoolfile */
33416866Swall 
33516866Swall 	/*
33616866Swall 	 * Open the job file and grab the first line.
33716866Swall 	 */
33816866Swall 
33916866Swall 	if ((infile = fopen(file,"r")) == NULL) {
34016866Swall 		perror(file);
34130410Skarels 		return ("???");
34216866Swall 	}
34316866Swall 
34428873Smckusick 	if (fscanf(infile,"# owner: %127s%*[^\n]\n",owner) != 1) {
34516866Swall 		fclose(infile);
34630410Skarels 		return ("???");
34716866Swall 	}
34816866Swall 
34916866Swall 	fclose(infile);
35030410Skarels 	return (owner);
35116866Swall 
35216866Swall }
35316866Swall 
35416866Swall /*
35516820Swall  * Get answer to interactive prompts, eating all characters beyond the first
35616820Swall  * one. If a 'y' is typed, return 1.
35716820Swall  */
yes()35816820Swall yes()
35916820Swall {
36016829Smckusick 	char ch;				/* dummy variable */
36116829Smckusick 	char ch1;				/* dummy variable */
36216820Swall 
36316829Smckusick 	ch = ch1 = getchar();
36416829Smckusick 	while (ch1 != '\n' && ch1 != EOF)
36516829Smckusick 		ch1 = getchar();
36628873Smckusick 	if (isupper(ch))
36728873Smckusick 		ch = tolower(ch);
36816829Smckusick 	return(ch == 'y');
36916820Swall }
37016820Swall 
37116820Swall /*
37216820Swall  * Get the uid of a person using his/her login name. Return -1 if no
37316820Swall  * such account name exists.
37416820Swall  */
getid(name)37516820Swall getid(name)
37616820Swall char *name;
37716820Swall {
37816820Swall 
37916829Smckusick 	struct passwd *pwdinfo;		/* password info structure */
38016829Smckusick 
38116829Smckusick 	if ((pwdinfo = getpwnam(name)) == 0)
38216829Smckusick 		return(-1);
38316820Swall 
38416829Smckusick 	return(pwdinfo->pw_uid);
38516820Swall }
38616820Swall 
38716820Swall /*
38816820Swall  * Get the full login name of a person using his/her user id.
38916820Swall  */
39016820Swall char *
getname(uid)39116820Swall getname(uid)
39216820Swall int uid;
39316820Swall {
39416829Smckusick 	struct passwd *pwdinfo;			/* password info structure */
39530410Skarels 	char *logname, *getlogin();
39616829Smckusick 
39716820Swall 
39830410Skarels 	logname = getlogin();
39930410Skarels 	if (logname == NULL || (pwdinfo = getpwnam(logname)) == NULL ||
40030410Skarels 	    pwdinfo->pw_uid != uid)
40130410Skarels 		pwdinfo = getpwuid(uid);
40230410Skarels 	if (pwdinfo == 0) {
40330410Skarels 		fprintf(stderr, "no name for uid %d?\n", uid);
40430410Skarels 		exit(1);
40530410Skarels 	}
40616829Smckusick 	return(pwdinfo->pw_name);
40716820Swall }
408