xref: /original-bsd/usr.bin/at/atrm/atrm.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1983, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1983, 1993\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)atrm.c	8.1 (Berkeley) 06/06/93";
16 #endif /* not lint */
17 
18 /*
19  *	synopsis: atrm [-f] [-i] [-] [[job #] [user] ...]
20  *
21  *
22  *	Remove files from the directory /usr/spool/at. These files
23  *	represent jobs to be run at a later date.
24  *
25  *	Author: Steve Wall
26  *		Computer Systems Research Group
27  *		University of California @ Berkeley
28  *
29  */
30 
31 #include <sys/types.h>
32 #include <sys/dir.h>
33 #include <sys/file.h>
34 #include <sys/stat.h>
35 #include <stdio.h>
36 #include <pwd.h>
37 #include <ctype.h>
38 #include "pathnames.h"
39 
40 #define SUPERUSER	0			/* is user super-user? */
41 #define MAXENTRIES	1000			/* max # of entries allowed */
42 
43 int user;					/* person requesting removal */
44 int fflag = 0;					/* suppress announcements? */
45 int iflag = 0;					/* run interactively? */
46 
47 main(argc,argv)
48 int argc;
49 char **argv;
50 
51 {
52 	register int i;			/* for loop index */
53 	int isuname;			/* is a command line argv a user name?*/
54 	int numjobs;			/* # of jobs in spooling area */
55 	int usage();			/* print usage info and exit */
56 	int allflag = 0;		/* remove all jobs belonging to user? */
57 	int jobno;
58 	int jobexists;			/* does a requested job exist? */
59 	int alphasort();		/* sort jobs by date of execution */
60 	int filewanted();		/* should a file be listed in queue? */
61 	char *myname, *getname();	/* current user's name */
62 	char *owner, *fowner();
63 	struct stat *statptr;		/* pointer to file stat structure */
64 	struct stat *stbuf[MAXENTRIES]; /* array of pointers to stat structs */
65 	struct direct **namelist;	/* names of jobs in spooling area */
66 
67 
68 	/*
69 	 * If job number, user name, or "-" is not specified, just print
70 	 * usage info and exit.
71 	 */
72 	if (argc < 2)
73 		usage();
74 
75 	--argc; ++argv;
76 
77 	/*
78 	 * Process command line flags.
79 	 * Special case the "-" option so that others may be grouped.
80 	 */
81 	while (argc > 0 && **argv == '-') {
82 		if (*(++(*argv)) == '\0') {
83 			++allflag;
84 		} else while (**argv) switch (*(*argv)++) {
85 
86 			case 'f':	++fflag;
87 					break;
88 
89 			case 'i':	++iflag;
90 					break;
91 
92 			default:	usage();
93 		}
94 		++argv; --argc;
95 	}
96 
97 	/*
98 	 * If all jobs are to be removed and extra command line arguments
99 	 * are given, print usage info and exit.
100 	 */
101 	if (allflag && argc)
102 		usage();
103 
104 	/*
105 	 * If only certain jobs are to be removed and no job #'s or user
106 	 * names are specified, print usage info and exit.
107 	 */
108 	if (!allflag && !argc)
109 		usage();
110 
111 	/*
112 	 * If interactive removal and quiet removal are requested, override
113 	 * quiet removal and run interactively.
114 	 */
115 	if (iflag && fflag)
116 		fflag = 0;
117 
118 	/*
119 	 * Move to spooling area and get user id of person requesting removal.
120 	 */
121 	if (chdir(_PATH_ATDIR) == -1) {
122 		perror(_PATH_ATDIR);
123 		exit(1);
124 	}
125 	user = getuid();
126 	myname = getname(user);
127 
128 	/*
129 	 * Get a list of the files in the spooling area.
130 	 */
131 	if ((numjobs = scandir(".",&namelist,filewanted,alphasort)) < 0) {
132 		perror(_PATH_ATDIR);
133 		exit(1);
134 	}
135 
136 	/*
137 	 * Build an array of pointers to the file stats for all jobs in
138 	 * the spooling area.
139 	 */
140 	for (i = 0; i < numjobs; ++i) {
141 		statptr = (struct stat *) malloc(sizeof(struct stat));
142 		if (statptr == NULL) {
143 			perror("malloc");
144 			exit(1);
145 		}
146 		if (stat(namelist[i]->d_name,statptr) < 0) {
147 			perror("stat");
148 			continue;
149 		}
150 		stbuf[i] = statptr;
151 	}
152 
153 	/*
154 	 * If all jobs belonging to the user are to be removed, compare
155 	 * the user's id to the owner of the file. If they match, remove
156 	 * the file. If the user is the super-user, don't bother comparing
157 	 * the id's. After all files are removed, exit (status 0).
158 	 */
159 	if (allflag) {
160 		for (i = 0; i < numjobs; ++i) {
161 			owner = fowner(namelist[i]->d_name);
162 			if (isowner(myname, owner))
163 				removentry(namelist[i]->d_name,
164 				    (int)stbuf[i]->st_ino, NULL);
165 		}
166 		exit(0);
167 	}
168 
169 	/*
170 	 * If only certain jobs are to be removed, interpret each command
171 	 * line argument. A check is done to see if it is a user's name or
172 	 * a job number (inode #). If it's a user's name, compare the argument
173 	 * to the files owner. If it's a job number, compare the argument to
174 	 * the inode number of the file. In either case, if a match occurs,
175 	 * try to remove the file. (The function "isusername" scans the
176 	 * argument to see if it is all digits which we will assume means
177 	 * that it's a job number (a fairly safe assumption?). This is done
178 	 * because we have to determine whether we are dealing with a user
179 	 * name or a job number. By assuming that only arguments that are
180 	 * all digits is a job number, we allow users to have digits in
181 	 * their login name i.e. "johndoe2").
182 	 */
183 
184 	while (argc--) {
185 		jobexists = 0;
186 		isuname = isusername(*argv);
187 		if (!isuname)
188 			jobno = atoi(*argv);
189 		for (i = 0; i < numjobs; ++i) {
190 
191 			/* if the inode number is 0, this entry was removed */
192 			if (stbuf[i]->st_ino == 0)
193 				continue;
194 
195 			owner = fowner(namelist[i]->d_name);
196 			/*
197 			 * if argv is a username, compare it to
198 			 * the owner of the file......
199 			 * otherwise, we assume that the argv is a job # and
200 			 * thus compare argv to the inode (job #) of the file.
201 			 */
202 			if (isuname) {
203 				if (strcmp(*argv, owner))
204 					continue;
205 			} else {
206 				if (stbuf[i]->st_ino != jobno)
207 					continue;
208 			}
209 			++jobexists;
210 			/*
211 			 * if the entry is removed, don't
212 			 * try to remove it again later.
213 			 */
214 			if (user == SUPERUSER || isowner(myname, owner)) {
215 				removentry(namelist[i]->d_name,
216 				    (int)stbuf[i]->st_ino, owner);
217 				stbuf[i]->st_ino = 0;
218 			} else if (!fflag)
219 				printf("%6d: permission denied\n",
220 				    stbuf[i]->st_ino);
221 			if (!isuname)
222 				break;
223 		}
224 
225 		/*
226 		 * If a requested argument doesn't exist, print a message.
227 		 */
228 		if (!jobexists && !fflag && !isuname) {
229 			fprintf(stderr, "%6s: no such job number\n", *argv);
230 		}
231 		++argv;
232 	}
233 	exit(0);
234 }
235 
236 /*
237  * Print usage info and exit.
238  */
239 usage()
240 {
241 	fprintf(stderr,"usage: atrm [-f] [-i] [-] [[job #] [user] ...]\n");
242 	exit(1);
243 }
244 
245 /*
246  * Do we want to include a file in the queue? (used by "scandir") We are looking
247  * for files with following syntax: yy.ddd.hhhh. so the test is made to see if
248  * the file name has three dots in it. This test will suffice since the only
249  * other files in /usr/spool/at don't have any dots in their name.
250  */
251 filewanted(direntry)
252 struct direct *direntry;
253 {
254 	int numdot = 0;			/* number of dots in a filename */
255 	char *filename;			/* filename we are looking at */
256 
257 	filename = direntry->d_name;
258 	while (*filename)
259 		numdot += (*(filename++) == '.');
260 	return(numdot == 3);
261 }
262 
263 /*
264  * Is a command line argument a username? As noted above we will assume
265  * that an argument that is all digits means that it's a job number, not
266  * a user's name. We choose to determine whether an argument is a user name
267  * in this manner because then it's ok for someone to have digits in their
268  * user name.
269  */
270 isusername(string)
271 char *string;
272 {
273 	char *ptr;			/* pointer used for scanning string */
274 
275 	ptr = string;
276 	while (isdigit(*ptr))
277 		++ptr;
278 	return((*ptr == '\0') ? 0 : 1);
279 }
280 
281 /*
282  * Remove an entry from the queue. The access of the file is checked for
283  * write permission (since all jobs are mode 644). If access is granted,
284  * unlink the file. If the fflag (suppress announcements) is not set,
285  * print the job number that we are removing and the result of the access
286  * check (either "permission denied" or "removed"). If we are running
287  * interactively (iflag), prompt the user before we unlink the file. If
288  * the super-user is removing jobs, inform him/her who owns each file before
289  * it is removed.
290  */
291 removentry(filename, inode, owner)
292 char *filename;
293 int inode;
294 char *owner;
295 {
296 
297 	if (!fflag)
298 		printf("%6d: ",inode);
299 
300 	if (iflag) {
301 		if (user == SUPERUSER && owner)
302 			printf("\t(owned by %s) ", owner);
303 		printf("remove? ");
304 		if (!yes())
305 			return;
306 	}
307 	if (unlink(filename) < 0)
308 		perror(filename);
309 	else if (!fflag && !iflag)
310 		printf("removed\n");
311 }
312 
313 /*
314  * See if "name" owns job owned by "jobname".
315  */
316 isowner(name,jobname)
317 char *name;
318 char *jobname;
319 {
320 
321 	return (strcmp(name,jobname) == 0);
322 }
323 
324 /*
325  * Return the owner of the job. This is stored on the first line of the
326  * spoolfile. If we run into trouble getting the name, we'll just return "???".
327  */
328 char *
329 fowner(file)
330 char *file;
331 {
332 	static char owner[128];			/* the owner */
333 	FILE *infile;				/* I/O stream to spoolfile */
334 
335 	/*
336 	 * Open the job file and grab the first line.
337 	 */
338 
339 	if ((infile = fopen(file,"r")) == NULL) {
340 		perror(file);
341 		return ("???");
342 	}
343 
344 	if (fscanf(infile,"# owner: %127s%*[^\n]\n",owner) != 1) {
345 		fclose(infile);
346 		return ("???");
347 	}
348 
349 	fclose(infile);
350 	return (owner);
351 
352 }
353 
354 /*
355  * Get answer to interactive prompts, eating all characters beyond the first
356  * one. If a 'y' is typed, return 1.
357  */
358 yes()
359 {
360 	char ch;				/* dummy variable */
361 	char ch1;				/* dummy variable */
362 
363 	ch = ch1 = getchar();
364 	while (ch1 != '\n' && ch1 != EOF)
365 		ch1 = getchar();
366 	if (isupper(ch))
367 		ch = tolower(ch);
368 	return(ch == 'y');
369 }
370 
371 /*
372  * Get the uid of a person using his/her login name. Return -1 if no
373  * such account name exists.
374  */
375 getid(name)
376 char *name;
377 {
378 
379 	struct passwd *pwdinfo;		/* password info structure */
380 
381 	if ((pwdinfo = getpwnam(name)) == 0)
382 		return(-1);
383 
384 	return(pwdinfo->pw_uid);
385 }
386 
387 /*
388  * Get the full login name of a person using his/her user id.
389  */
390 char *
391 getname(uid)
392 int uid;
393 {
394 	struct passwd *pwdinfo;			/* password info structure */
395 	char *logname, *getlogin();
396 
397 
398 	logname = getlogin();
399 	if (logname == NULL || (pwdinfo = getpwnam(logname)) == NULL ||
400 	    pwdinfo->pw_uid != uid)
401 		pwdinfo = getpwuid(uid);
402 	if (pwdinfo == 0) {
403 		fprintf(stderr, "no name for uid %d?\n", uid);
404 		exit(1);
405 	}
406 	return(pwdinfo->pw_name);
407 }
408