Commit | Line | Data |
---|---|---|
2791ff57 KB |
1 | /*- |
2 | * Copyright (c) 1983 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * %sccs.include.proprietary.c% | |
d0aeaf5a DF |
6 | */ |
7 | ||
8 | #ifndef lint | |
9 | char copyright[] = | |
2791ff57 | 10 | "@(#) Copyright (c) 1983 The Regents of the University of California.\n\ |
d0aeaf5a | 11 | All rights reserved.\n"; |
2791ff57 | 12 | #endif /* not lint */ |
d0aeaf5a | 13 | |
45dbe918 | 14 | #ifndef lint |
2791ff57 KB |
15 | static char sccsid[] = "@(#)atrm.c 5.6 (Berkeley) %G%"; |
16 | #endif /* not lint */ | |
45dbe918 SW |
17 | |
18 | /* | |
d6fff2c5 | 19 | * synopsis: atrm [-f] [-i] [-] [[job #] [user] ...] |
45dbe918 SW |
20 | * |
21 | * | |
d6fff2c5 KM |
22 | * Remove files from the directory /usr/spool/at. These files |
23 | * represent jobs to be run at a later date. | |
45dbe918 | 24 | * |
d6fff2c5 KM |
25 | * Author: Steve Wall |
26 | * Computer Systems Research Group | |
27 | * University of California @ Berkeley | |
45dbe918 SW |
28 | * |
29 | */ | |
30 | ||
45dbe918 SW |
31 | #include <sys/types.h> |
32 | #include <sys/dir.h> | |
33 | #include <sys/file.h> | |
34 | #include <sys/stat.h> | |
0006add0 KB |
35 | #include <stdio.h> |
36 | #include <pwd.h> | |
37 | #include <ctype.h> | |
7abf8d65 | 38 | #include "pathnames.h" |
45dbe918 | 39 | |
d6fff2c5 KM |
40 | #define SUPERUSER 0 /* is user super-user? */ |
41 | #define MAXENTRIES 1000 /* max # of entries allowed */ | |
45dbe918 | 42 | |
d6fff2c5 KM |
43 | int user; /* person requesting removal */ |
44 | int fflag = 0; /* suppress announcements? */ | |
45 | int iflag = 0; /* run interactively? */ | |
45dbe918 SW |
46 | |
47 | main(argc,argv) | |
48 | int argc; | |
49 | char **argv; | |
50 | ||
51 | { | |
bb994b7d | 52 | register int i; /* for loop index */ |
d6fff2c5 KM |
53 | int isuname; /* is a command line argv a user name?*/ |
54 | int numjobs; /* # of jobs in spooling area */ | |
55 | int usage(); /* print usage info and exit */ | |
56 | int allflag = 0; /* remove all jobs belonging to user? */ | |
bb994b7d | 57 | int jobno; |
d6fff2c5 KM |
58 | int jobexists; /* does a requested job exist? */ |
59 | int alphasort(); /* sort jobs by date of execution */ | |
60 | int filewanted(); /* should a file be listed in queue? */ | |
bb994b7d MK |
61 | char *myname, *getname(); /* current user's name */ |
62 | char *owner, *fowner(); | |
d6fff2c5 KM |
63 | struct stat *statptr; /* pointer to file stat structure */ |
64 | struct stat *stbuf[MAXENTRIES]; /* array of pointers to stat structs */ | |
65 | struct direct **namelist; /* names of jobs in spooling area */ | |
66 | ||
67 | ||
68 | /* | |
69 | * If job number, user name, or "-" is not specified, just print | |
70 | * usage info and exit. | |
71 | */ | |
72 | if (argc < 2) | |
73 | usage(); | |
74 | ||
75 | --argc; ++argv; | |
76 | ||
77 | /* | |
78 | * Process command line flags. | |
e0799b82 | 79 | * Special case the "-" option so that others may be grouped. |
d6fff2c5 | 80 | */ |
e0799b82 KM |
81 | while (argc > 0 && **argv == '-') { |
82 | if (*(++(*argv)) == '\0') { | |
83 | ++allflag; | |
84 | } else while (**argv) switch (*(*argv)++) { | |
d6fff2c5 KM |
85 | |
86 | case 'f': ++fflag; | |
87 | break; | |
88 | ||
89 | case 'i': ++iflag; | |
90 | break; | |
91 | ||
92 | default: usage(); | |
93 | } | |
94 | ++argv; --argc; | |
95 | } | |
96 | ||
97 | /* | |
98 | * If all jobs are to be removed and extra command line arguments | |
99 | * are given, print usage info and exit. | |
100 | */ | |
101 | if (allflag && argc) | |
102 | usage(); | |
103 | ||
104 | /* | |
105 | * If only certain jobs are to be removed and no job #'s or user | |
106 | * names are specified, print usage info and exit. | |
107 | */ | |
108 | if (!allflag && !argc) | |
109 | usage(); | |
110 | ||
111 | /* | |
112 | * If interactive removal and quiet removal are requested, override | |
113 | * quiet removal and run interactively. | |
114 | */ | |
115 | if (iflag && fflag) | |
116 | fflag = 0; | |
117 | ||
118 | /* | |
119 | * Move to spooling area and get user id of person requesting removal. | |
120 | */ | |
7abf8d65 KB |
121 | if (chdir(_PATH_ATDIR) == -1) { |
122 | perror(_PATH_ATDIR); | |
d6fff2c5 KM |
123 | exit(1); |
124 | } | |
125 | user = getuid(); | |
bb994b7d | 126 | myname = getname(user); |
d6fff2c5 KM |
127 | |
128 | /* | |
129 | * Get a list of the files in the spooling area. | |
130 | */ | |
7e1f2388 | 131 | if ((numjobs = scandir(".",&namelist,filewanted,alphasort)) < 0) { |
7abf8d65 | 132 | perror(_PATH_ATDIR); |
d6fff2c5 KM |
133 | exit(1); |
134 | } | |
135 | ||
136 | /* | |
137 | * Build an array of pointers to the file stats for all jobs in | |
138 | * the spooling area. | |
139 | */ | |
140 | for (i = 0; i < numjobs; ++i) { | |
141 | statptr = (struct stat *) malloc(sizeof(struct stat)); | |
142 | if (statptr == NULL) { | |
143 | perror("malloc"); | |
144 | exit(1); | |
145 | } | |
146 | if (stat(namelist[i]->d_name,statptr) < 0) { | |
147 | perror("stat"); | |
148 | continue; | |
149 | } | |
150 | stbuf[i] = statptr; | |
151 | } | |
152 | ||
153 | /* | |
154 | * If all jobs belonging to the user are to be removed, compare | |
155 | * the user's id to the owner of the file. If they match, remove | |
156 | * the file. If the user is the super-user, don't bother comparing | |
157 | * the id's. After all files are removed, exit (status 0). | |
158 | */ | |
159 | if (allflag) { | |
160 | for (i = 0; i < numjobs; ++i) { | |
bb994b7d MK |
161 | owner = fowner(namelist[i]->d_name); |
162 | if (isowner(myname, owner)) | |
163 | removentry(namelist[i]->d_name, | |
164 | (int)stbuf[i]->st_ino, NULL); | |
d6fff2c5 KM |
165 | } |
166 | exit(0); | |
167 | } | |
168 | ||
169 | /* | |
170 | * If only certain jobs are to be removed, interpret each command | |
171 | * line argument. A check is done to see if it is a user's name or | |
172 | * a job number (inode #). If it's a user's name, compare the argument | |
173 | * to the files owner. If it's a job number, compare the argument to | |
174 | * the inode number of the file. In either case, if a match occurs, | |
175 | * try to remove the file. (The function "isusername" scans the | |
176 | * argument to see if it is all digits which we will assume means | |
177 | * that it's a job number (a fairly safe assumption?). This is done | |
178 | * because we have to determine whether we are dealing with a user | |
179 | * name or a job number. By assuming that only arguments that are | |
180 | * all digits is a job number, we allow users to have digits in | |
181 | * their login name i.e. "johndoe2"). | |
182 | */ | |
183 | ||
184 | while (argc--) { | |
185 | jobexists = 0; | |
186 | isuname = isusername(*argv); | |
bb994b7d MK |
187 | if (!isuname) |
188 | jobno = atoi(*argv); | |
d6fff2c5 KM |
189 | for (i = 0; i < numjobs; ++i) { |
190 | ||
e0799b82 KM |
191 | /* if the inode number is 0, this entry was removed */ |
192 | if (stbuf[i]->st_ino == 0) | |
193 | continue; | |
194 | ||
bb994b7d | 195 | owner = fowner(namelist[i]->d_name); |
d6fff2c5 | 196 | /* |
bb994b7d MK |
197 | * if argv is a username, compare it to |
198 | * the owner of the file...... | |
d6fff2c5 KM |
199 | * otherwise, we assume that the argv is a job # and |
200 | * thus compare argv to the inode (job #) of the file. | |
201 | */ | |
bb994b7d MK |
202 | if (isuname) { |
203 | if (strcmp(*argv, owner)) | |
204 | continue; | |
d6fff2c5 | 205 | } else { |
bb994b7d | 206 | if (stbuf[i]->st_ino != jobno) |
d6fff2c5 KM |
207 | continue; |
208 | } | |
209 | ++jobexists; | |
e0799b82 | 210 | /* |
bb994b7d | 211 | * if the entry is removed, don't |
e0799b82 KM |
212 | * try to remove it again later. |
213 | */ | |
bb994b7d MK |
214 | if (user == SUPERUSER || isowner(myname, owner)) { |
215 | removentry(namelist[i]->d_name, | |
216 | (int)stbuf[i]->st_ino, owner); | |
e0799b82 | 217 | stbuf[i]->st_ino = 0; |
bb994b7d MK |
218 | } else if (!fflag) |
219 | printf("%6d: permission denied\n", | |
220 | stbuf[i]->st_ino); | |
221 | if (!isuname) | |
222 | break; | |
d6fff2c5 KM |
223 | } |
224 | ||
225 | /* | |
226 | * If a requested argument doesn't exist, print a message. | |
227 | */ | |
228 | if (!jobexists && !fflag && !isuname) { | |
e0799b82 | 229 | fprintf(stderr, "%6s: no such job number\n", *argv); |
d6fff2c5 KM |
230 | } |
231 | ++argv; | |
232 | } | |
233 | exit(0); | |
45dbe918 SW |
234 | } |
235 | ||
236 | /* | |
237 | * Print usage info and exit. | |
238 | */ | |
239 | usage() | |
240 | { | |
d6fff2c5 KM |
241 | fprintf(stderr,"usage: atrm [-f] [-i] [-] [[job #] [user] ...]\n"); |
242 | exit(1); | |
45dbe918 SW |
243 | } |
244 | ||
245 | /* | |
246 | * Do we want to include a file in the queue? (used by "scandir") We are looking | |
247 | * for files with following syntax: yy.ddd.hhhh. so the test is made to see if | |
248 | * the file name has three dots in it. This test will suffice since the only | |
249 | * other files in /usr/spool/at don't have any dots in their name. | |
250 | */ | |
251 | filewanted(direntry) | |
252 | struct direct *direntry; | |
253 | { | |
d6fff2c5 KM |
254 | int numdot = 0; /* number of dots in a filename */ |
255 | char *filename; /* filename we are looking at */ | |
45dbe918 | 256 | |
d6fff2c5 KM |
257 | filename = direntry->d_name; |
258 | while (*filename) | |
259 | numdot += (*(filename++) == '.'); | |
260 | return(numdot == 3); | |
45dbe918 SW |
261 | } |
262 | ||
263 | /* | |
264 | * Is a command line argument a username? As noted above we will assume | |
265 | * that an argument that is all digits means that it's a job number, not | |
266 | * a user's name. We choose to determine whether an argument is a user name | |
267 | * in this manner because then it's ok for someone to have digits in their | |
268 | * user name. | |
269 | */ | |
270 | isusername(string) | |
271 | char *string; | |
272 | { | |
d6fff2c5 | 273 | char *ptr; /* pointer used for scanning string */ |
45dbe918 | 274 | |
d6fff2c5 KM |
275 | ptr = string; |
276 | while (isdigit(*ptr)) | |
277 | ++ptr; | |
278 | return((*ptr == '\0') ? 0 : 1); | |
45dbe918 SW |
279 | } |
280 | ||
281 | /* | |
282 | * Remove an entry from the queue. The access of the file is checked for | |
283 | * write permission (since all jobs are mode 644). If access is granted, | |
284 | * unlink the file. If the fflag (suppress announcements) is not set, | |
285 | * print the job number that we are removing and the result of the access | |
7e1f2388 SW |
286 | * check (either "permission denied" or "removed"). If we are running |
287 | * interactively (iflag), prompt the user before we unlink the file. If | |
288 | * the super-user is removing jobs, inform him/her who owns each file before | |
bb994b7d | 289 | * it is removed. |
45dbe918 | 290 | */ |
bb994b7d | 291 | removentry(filename, inode, owner) |
45dbe918 SW |
292 | char *filename; |
293 | int inode; | |
bb994b7d | 294 | char *owner; |
45dbe918 SW |
295 | { |
296 | ||
d6fff2c5 KM |
297 | if (!fflag) |
298 | printf("%6d: ",inode); | |
299 | ||
bb994b7d MK |
300 | if (iflag) { |
301 | if (user == SUPERUSER && owner) | |
302 | printf("\t(owned by %s) ", owner); | |
303 | printf("remove? "); | |
304 | if (!yes()) | |
305 | return; | |
d6fff2c5 | 306 | } |
bb994b7d MK |
307 | if (unlink(filename) < 0) |
308 | perror(filename); | |
309 | else if (!fflag && !iflag) | |
310 | printf("removed\n"); | |
45dbe918 SW |
311 | } |
312 | ||
7e1f2388 | 313 | /* |
bb994b7d | 314 | * See if "name" owns job owned by "jobname". |
7e1f2388 | 315 | */ |
bb994b7d | 316 | isowner(name,jobname) |
7e1f2388 | 317 | char *name; |
bb994b7d | 318 | char *jobname; |
7e1f2388 | 319 | { |
7e1f2388 | 320 | |
bb994b7d | 321 | return (strcmp(name,jobname) == 0); |
7e1f2388 SW |
322 | } |
323 | ||
324 | /* | |
bb994b7d MK |
325 | * Return the owner of the job. This is stored on the first line of the |
326 | * spoolfile. If we run into trouble getting the name, we'll just return "???". | |
7e1f2388 | 327 | */ |
bb994b7d MK |
328 | char * |
329 | fowner(file) | |
7e1f2388 SW |
330 | char *file; |
331 | { | |
bb994b7d | 332 | static char owner[128]; /* the owner */ |
7e1f2388 SW |
333 | FILE *infile; /* I/O stream to spoolfile */ |
334 | ||
335 | /* | |
336 | * Open the job file and grab the first line. | |
337 | */ | |
338 | ||
339 | if ((infile = fopen(file,"r")) == NULL) { | |
7e1f2388 | 340 | perror(file); |
bb994b7d | 341 | return ("???"); |
7e1f2388 SW |
342 | } |
343 | ||
e0799b82 | 344 | if (fscanf(infile,"# owner: %127s%*[^\n]\n",owner) != 1) { |
7e1f2388 | 345 | fclose(infile); |
bb994b7d | 346 | return ("???"); |
7e1f2388 SW |
347 | } |
348 | ||
349 | fclose(infile); | |
bb994b7d | 350 | return (owner); |
7e1f2388 SW |
351 | |
352 | } | |
353 | ||
45dbe918 SW |
354 | /* |
355 | * Get answer to interactive prompts, eating all characters beyond the first | |
356 | * one. If a 'y' is typed, return 1. | |
357 | */ | |
358 | yes() | |
359 | { | |
d6fff2c5 KM |
360 | char ch; /* dummy variable */ |
361 | char ch1; /* dummy variable */ | |
45dbe918 | 362 | |
d6fff2c5 KM |
363 | ch = ch1 = getchar(); |
364 | while (ch1 != '\n' && ch1 != EOF) | |
365 | ch1 = getchar(); | |
e0799b82 KM |
366 | if (isupper(ch)) |
367 | ch = tolower(ch); | |
d6fff2c5 | 368 | return(ch == 'y'); |
45dbe918 SW |
369 | } |
370 | ||
371 | /* | |
372 | * Get the uid of a person using his/her login name. Return -1 if no | |
373 | * such account name exists. | |
374 | */ | |
375 | getid(name) | |
376 | char *name; | |
377 | { | |
378 | ||
d6fff2c5 KM |
379 | struct passwd *pwdinfo; /* password info structure */ |
380 | ||
381 | if ((pwdinfo = getpwnam(name)) == 0) | |
382 | return(-1); | |
45dbe918 | 383 | |
d6fff2c5 | 384 | return(pwdinfo->pw_uid); |
45dbe918 SW |
385 | } |
386 | ||
387 | /* | |
388 | * Get the full login name of a person using his/her user id. | |
389 | */ | |
390 | char * | |
391 | getname(uid) | |
392 | int uid; | |
393 | { | |
d6fff2c5 | 394 | struct passwd *pwdinfo; /* password info structure */ |
bb994b7d | 395 | char *logname, *getlogin(); |
d6fff2c5 | 396 | |
45dbe918 | 397 | |
bb994b7d MK |
398 | logname = getlogin(); |
399 | if (logname == NULL || (pwdinfo = getpwnam(logname)) == NULL || | |
400 | pwdinfo->pw_uid != uid) | |
401 | pwdinfo = getpwuid(uid); | |
402 | if (pwdinfo == 0) { | |
403 | fprintf(stderr, "no name for uid %d?\n", uid); | |
404 | exit(1); | |
405 | } | |
d6fff2c5 | 406 | return(pwdinfo->pw_name); |
45dbe918 | 407 | } |