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