xref: /illumos-gate/usr/src/cmd/cron/atrm.c (revision 2b52f2afd5bf4e315cee202285165b6ff78cc6c2)
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