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