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