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