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