1 /* $OpenBSD: rmjob.c,v 1.16 2003/06/02 23:36:53 millert Exp $ */
2 /* $NetBSD: rmjob.c,v 1.16 2000/04/16 14:43:58 mrg Exp $ */
3
4 /*
5 * Copyright (c) 1983, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #ifndef lint
34 #if 0
35 static const char sccsid[] = "@(#)rmjob.c 8.2 (Berkeley) 4/28/95";
36 #else
37 static const char rcsid[] = "$OpenBSD: rmjob.c,v 1.16 2003/06/02 23:36:53 millert Exp $";
38 #endif
39 #endif /* not lint */
40
41 #include <sys/param.h>
42
43 #include <signal.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <dirent.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <ctype.h>
52 #include "lp.h"
53 #include "lp.local.h"
54 #include "pathnames.h"
55
56 /*
57 * rmjob - remove the specified jobs from the queue.
58 */
59
60 /*
61 * Stuff for handling lprm specifications
62 */
63 extern char *user[]; /* users to process */
64 extern int users; /* # of users in user array */
65 extern int requ[]; /* job number of spool entries */
66 extern int requests; /* # of spool requests */
67 extern char *person; /* name of person doing lprm */
68
69 static char root[] = "root";
70 static int all = 0; /* eliminate all files (root only) */
71 static int cur_daemon; /* daemon's pid */
72 static char current[NAME_MAX]; /* active control file name */
73
74 static void do_unlink(char *);
75 static void alarmer(int);
76 static int lockchk(char *);
77
78 void
rmjob(void)79 rmjob(void)
80 {
81 int i, nitems;
82 int assasinated = 0;
83 struct dirent **files;
84 char *cp;
85
86 if ((i = cgetent(&bp, printcapdb, printer)) == -2)
87 fatal("can't open printer description file");
88 else if (i == -1)
89 fatal("unknown printer");
90 else if (i == -3)
91 fatal("potential reference loop detected in printcap file");
92 if (cgetstr(bp, DEFLP, &LP) < 0)
93 LP = _PATH_DEFDEVLP;
94 if (cgetstr(bp, "rp", &RP) < 0)
95 RP = DEFLP;
96 if (cgetstr(bp, "sd", &SD) < 0)
97 SD = _PATH_DEFSPOOL;
98 if (cgetstr(bp,"lo", &LO) < 0)
99 LO = DEFLOCK;
100 cgetstr(bp, "rm", &RM);
101 if ((cp = checkremote()) != NULL)
102 printf("Warning: %s\n", cp);
103
104 /*
105 * If the format was `lprm -' and the user isn't the super-user,
106 * then fake things to look like he said `lprm user'.
107 */
108 if (users < 0) {
109 if (getuid() == 0)
110 all = 1; /* all files in local queue */
111 else {
112 user[0] = person;
113 users = 1;
114 }
115 }
116 if (!strcmp(person, "-all")) {
117 if (from == host)
118 fatal("The login name \"-all\" is reserved");
119 all = 1; /* all those from 'from' */
120 person = root;
121 }
122
123 PRIV_START;
124 if (chdir(SD) < 0)
125 fatal("cannot chdir to spool directory");
126 if ((nitems = scandir(".", &files, iscf, NULL)) < 0)
127 fatal("cannot access spool directory");
128 PRIV_END;
129
130 if (nitems) {
131 /*
132 * Check for an active printer daemon. If one is running
133 * and it is reading our file, kill it, then remove stuff.
134 * Lastly, restart the daemon if it is not (or no longer)
135 * running.
136 */
137 if (lockchk(LO) && chk(current)) {
138 PRIV_START;
139 assasinated = kill(cur_daemon, SIGINT) == 0;
140 PRIV_END;
141 if (!assasinated)
142 fatal("cannot kill printer daemon");
143 }
144 /*
145 * process the files
146 */
147 for (i = 0; i < nitems; i++)
148 process(files[i]->d_name);
149 }
150 rmremote();
151 /*
152 * Restart the printer daemon if it was killed
153 */
154 if (assasinated && !startdaemon(printer))
155 fatal("cannot restart printer daemon\n");
156 exit(0);
157 }
158
159 /*
160 * Process a lock file: collect the pid of the active
161 * daemon and the file name of the active spool entry.
162 * Return boolean indicating existence of a lock file.
163 */
164 static int
lockchk(char * s)165 lockchk(char *s)
166 {
167 FILE *fp = NULL;
168 int fd, i, n;
169
170 /* NOTE: lock file is owned by root, not the user. */
171 PRIV_START;
172 fd = safe_open(s, O_RDONLY|O_NOFOLLOW, 0);
173 PRIV_END;
174 if (fd < 0 || (fp = fdopen(fd, "r")) == NULL) {
175 if (fd >= 0)
176 close(fd);
177 if (errno == EACCES)
178 fatal("can't access lock file");
179 else
180 return(0);
181 }
182 if (!getline(fp)) {
183 (void)fclose(fp);
184 return(0); /* no daemon present */
185 }
186 cur_daemon = atoi(line);
187 if (kill(cur_daemon, 0) < 0 && errno != EPERM) {
188 (void)fclose(fp);
189 return(0); /* no daemon present */
190 }
191 for (i = 1; (n = fread(current, sizeof(char), sizeof(current), fp)) <= 0; i++) {
192 if (i > 5) {
193 n = 1;
194 break;
195 }
196 sleep(i);
197 }
198 current[n-1] = '\0';
199 (void)fclose(fp);
200 return(1);
201 }
202
203 /*
204 * Process a control file.
205 */
206 void
process(char * file)207 process(char *file)
208 {
209 FILE *cfp = NULL;
210 int fd;
211
212 if (!chk(file))
213 return;
214 PRIV_START;
215 fd = safe_open(file, O_RDONLY|O_NOFOLLOW, 0);
216 PRIV_END;
217 if (fd < 0 || (cfp = fdopen(fd, "r")) == NULL) {
218 if (fd >= 0)
219 close(fd);
220 fatal("cannot open %s", file);
221 }
222 while (getline(cfp)) {
223 switch (line[0]) {
224 case 'U': /* unlink associated files */
225 if (strchr(line+1, '/') || strncmp(line+1, "df", 2))
226 break;
227 do_unlink(line+1);
228 }
229 }
230 (void)fclose(cfp);
231 do_unlink(file);
232 }
233
234 static void
do_unlink(char * file)235 do_unlink(char *file)
236 {
237 int ret;
238
239 if (from != host)
240 printf("%s: ", host);
241 PRIV_START;
242 ret = unlink(file);
243 PRIV_END;
244 printf(ret ? "cannot dequeue %s\n" : "%s dequeued\n", file);
245 }
246
247 /*
248 * Do the dirty work in checking
249 */
250 int
chk(char * file)251 chk(char *file)
252 {
253 int *r, n, fd;
254 char **u, *cp;
255 FILE *cfp = NULL;
256
257 /*
258 * Check for valid cf file name (mostly checking current).
259 */
260 if (strlen(file) < 7 || file[0] != 'c' || file[1] != 'f')
261 return(0);
262
263 if (all && (from == host || !strcmp(from, file+6)))
264 return(1);
265
266 /*
267 * get the owner's name from the control file.
268 */
269 PRIV_START;
270 fd = safe_open(file, O_RDONLY|O_NOFOLLOW, 0);
271 PRIV_END;
272 if (fd < 0 || (cfp = fdopen(fd, "r")) == NULL) {
273 if (fd >= 0)
274 close(fd);
275 return(0);
276 }
277 while (getline(cfp)) {
278 if (line[0] == 'P')
279 break;
280 }
281 (void)fclose(cfp);
282 if (line[0] != 'P')
283 return(0);
284
285 if (users == 0 && requests == 0)
286 return(!strcmp(file, current) && isowner(line+1, file));
287 /*
288 * Check the request list
289 */
290 for (n = 0, cp = file+3; isdigit(*cp); )
291 n = n * 10 + (*cp++ - '0');
292 for (r = requ; r < &requ[requests]; r++)
293 if (*r == n && isowner(line+1, file))
294 return(1);
295 /*
296 * Check to see if it's in the user list
297 */
298 for (u = user; u < &user[users]; u++)
299 if (!strcmp(*u, line+1) && isowner(line+1, file))
300 return(1);
301 return(0);
302 }
303
304 /*
305 * If root is removing a file on the local machine, allow it.
306 * If root is removing a file from a remote machine, only allow
307 * files sent from the remote machine to be removed.
308 * Normal users can only remove the file from where it was sent.
309 */
310 int
isowner(char * owner,char * file)311 isowner(char *owner, char *file)
312 {
313 if (!strcmp(person, root) && (from == host || !strcmp(from, file+6)))
314 return(1);
315 if (!strcmp(person, owner) && !strcmp(from, file+6))
316 return(1);
317 if (from != host)
318 printf("%s: ", host);
319 printf("%s: Permission denied\n", file);
320 return(0);
321 }
322
323 /*
324 * Check to see if we are sending files to a remote machine. If we are,
325 * then try removing files on the remote machine.
326 */
327 void
rmremote(void)328 rmremote(void)
329 {
330 char *cp;
331 int i, rem;
332 size_t n;
333 char buf[BUFSIZ];
334
335 if (!remote)
336 return; /* not sending to a remote machine */
337
338 /*
339 * Flush stdout so the user can see what has been deleted
340 * while we wait (possibly) for the connection.
341 */
342 fflush(stdout);
343
344 /* the trailing space will be replaced with a newline later */
345 n = snprintf(buf, sizeof(buf), "\5%s %s ", RP, all ? "-all" : person);
346 if (n == -1 || n >= sizeof(buf))
347 goto bad;
348 cp = buf + n;
349 for (i = 0; i < users; i++) {
350 n = strlcpy(cp, user[i], sizeof(buf) - (cp - buf + 1));
351 if (n >= sizeof(buf) - (cp - buf + 1))
352 goto bad;
353 cp += n;
354 *cp++ = ' ';
355 }
356 *cp = '\0';
357 for (i = 0; i < requests; i++) {
358 n = snprintf(cp, sizeof(buf) - (cp - buf), "%d ", requ[i]);
359 if (n == -1 || n >= sizeof(buf) - (cp - buf))
360 goto bad;
361 cp += n;
362 }
363 cp[-1] = '\n'; /* replace space with newline, leave the NUL */
364 rem = getport(RM, 0);
365 if (rem < 0) {
366 if (from != host)
367 printf("%s: ", host);
368 printf("connection to %s is down\n", RM);
369 } else {
370 struct sigaction osa, nsa;
371
372 memset(&nsa, 0, sizeof(nsa));
373 nsa.sa_handler = alarmer;
374 sigemptyset(&nsa.sa_mask);
375 nsa.sa_flags = 0;
376 (void)sigaction(SIGALRM, &nsa, &osa);
377 alarm(wait_time);
378
379 i = strlen(buf);
380 if (write(rem, buf, i) != i)
381 fatal("Lost connection");
382 while ((i = read(rem, buf, sizeof(buf))) > 0)
383 (void)fwrite(buf, 1, i, stdout);
384 alarm(0);
385 (void)sigaction(SIGALRM, &osa, NULL);
386 (void)close(rem);
387 }
388 return;
389 bad:
390 printf("remote buffer too large\n");
391 return;
392 }
393
394 static void
alarmer(int s)395 alarmer(int s)
396 {
397 /* nothing */
398 }
399
400 /*
401 * Return 1 if the filename begins with 'cf'
402 */
403 int
iscf(struct dirent * d)404 iscf(struct dirent *d)
405 {
406 return(d->d_name[0] == 'c' && d->d_name[1] == 'f');
407 }
408