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
main(int argc,char ** argv)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
usage(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
removentry(char * filename,struct stat * statptr,uid_t user)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
powner(char * file)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
getjoblist(struct dirent *** namelistp,struct stat *** statlistp,int (* sortfunc)())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 *
getname(uid_t uid)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
aterror(char * msg)388 aterror(char *msg)
389 {
390 fprintf(stderr, "atrm: %s\n", msg);
391 }
392
393 static void
atperror(char * msg)394 atperror(char *msg)
395 {
396 fprintf(stderr, "atrm: %s: %s\n", msg, errmsg(errno));
397 }
398
399 static void
atperror2(char * msg,char * name)400 atperror2(char *msg, char *name)
401 {
402 fprintf(stderr, "atrm: %s %s: %s\n", msg, name, errmsg(errno));
403 }
404
405 static void
atabort(char * msg)406 atabort(char *msg)
407 {
408 aterror(msg);
409 exit(1);
410 }
411
412 static void
atabortperror(char * msg)413 atabortperror(char *msg)
414 {
415 atperror(msg);
416 exit(1);
417 }
418