rmjob.c revision 201512
1169691Skan/*
2169691Skan * Copyright (c) 1983, 1993
3169691Skan *	The Regents of the University of California.  All rights reserved.
4169691Skan *
5169691Skan * Redistribution and use in source and binary forms, with or without
6169691Skan * modification, are permitted provided that the following conditions
7169691Skan * are met:
8169691Skan * 1. Redistributions of source code must retain the above copyright
9169691Skan *    notice, this list of conditions and the following disclaimer.
10169691Skan * 2. Redistributions in binary form must reproduce the above copyright
11169691Skan *    notice, this list of conditions and the following disclaimer in the
12169691Skan *    documentation and/or other materials provided with the distribution.
13169691Skan * 3. All advertising materials mentioning features or use of this software
14169691Skan *    must display the following acknowledgement:
15169691Skan *	This product includes software developed by the University of
16169691Skan *	California, Berkeley and its contributors.
17169691Skan * 4. Neither the name of the University nor the names of its contributors
18169691Skan *    may be used to endorse or promote products derived from this software
19169691Skan *    without specific prior written permission.
20169691Skan *
21169691Skan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22169691Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23169691Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24169691Skan * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25169691Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26169691Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27169691Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28169691Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29169691Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30169691Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31169691Skan * SUCH DAMAGE.
32169691Skan */
33169691Skan
34169691Skan#if 0
35169691Skan#ifndef lint
36169691Skanstatic char sccsid[] = "@(#)rmjob.c	8.2 (Berkeley) 4/28/95";
37169691Skan#endif /* not lint */
38169691Skan#endif
39169691Skan
40169691Skan#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
41169691Skan__FBSDID("$FreeBSD: head/usr.sbin/lpr/common_source/rmjob.c 201512 2010-01-04 15:40:17Z kib $");
42169691Skan
43169691Skan#include <sys/param.h>
44169691Skan#include <sys/uio.h>
45169691Skan
46169691Skan#include <ctype.h>
47169691Skan#include <dirent.h>
48169691Skan#include <errno.h>
49169691Skan#include <signal.h>
50169691Skan#include <stdio.h>
51169691Skan#include <stdlib.h>
52169691Skan#include <string.h>
53169691Skan#define psignal foil_gcc_psignal
54169691Skan#define	sys_siglist foil_gcc_siglist
55169691Skan#include <unistd.h>
56169691Skan#undef psignal
57169691Skan#undef sys_siglist
58169691Skan
59169691Skan#include "lp.h"
60169691Skan#include "lp.local.h"
61169691Skan#include "pathnames.h"
62169691Skan
63169691Skan/*
64169691Skan * rmjob - remove the specified jobs from the queue.
65169691Skan */
66169691Skan
67169691Skan/*
68169691Skan * Stuff for handling lprm specifications
69169691Skan */
70169691Skanstatic char	root[] = "root";
71169691Skanstatic int	all = 0;		/* eliminate all files (root only) */
72169691Skanstatic int	cur_daemon;		/* daemon's pid */
73169691Skanstatic char	current[7+MAXHOSTNAMELEN];  /* active control file name */
74169691Skan
75169691Skanextern uid_t	uid, euid;		/* real and effective user id's */
76169691Skan
77169691Skanstatic	void	alarmhandler(int _signo);
78169691Skanstatic	void	do_unlink(char *_file);
79169691Skanstatic int	 isowner(char *_owner, char *_file, const char *_cfhost);
80169691Skan
81169691Skanvoid
82169691Skanrmjob(const char *printer)
83169691Skan{
84169691Skan	register int i, nitems;
85169691Skan	int assasinated = 0;
86169691Skan	struct dirent **files;
87169691Skan	char *cp;
88169691Skan	struct printer myprinter, *pp = &myprinter;
89169691Skan
90169691Skan	init_printer(pp);
91169691Skan	if ((i = getprintcap(printer, pp)) < 0)
92169691Skan		fatal(pp, "getprintcap: %s", pcaperr(i));
93169691Skan	if ((cp = checkremote(pp))) {
94169691Skan		printf("Warning: %s\n", cp);
95169691Skan		free(cp);
96169691Skan	}
97169691Skan
98169691Skan	/*
99169691Skan	 * If the format was `lprm -' and the user isn't the super-user,
100169691Skan	 *  then fake things to look like he said `lprm user'.
101169691Skan	 */
102169691Skan	if (users < 0) {
103169691Skan		if (getuid() == 0)
104169691Skan			all = 1;	/* all files in local queue */
105169691Skan		else {
106169691Skan			user[0] = person;
107169691Skan			users = 1;
108169691Skan		}
109169691Skan	}
110169691Skan	if (!strcmp(person, "-all")) {
111169691Skan		if (from_host == local_host)
112169691Skan			fatal(pp, "The login name \"-all\" is reserved");
113169691Skan		all = 1;	/* all those from 'from_host' */
114169691Skan		person = root;
115169691Skan	}
116169691Skan
117169691Skan	seteuid(euid);
118169691Skan	if (chdir(pp->spool_dir) < 0)
119169691Skan		fatal(pp, "cannot chdir to spool directory");
120169691Skan	if ((nitems = scandir(".", &files, iscf, NULL)) < 0)
121169691Skan		fatal(pp, "cannot access spool directory");
122169691Skan	seteuid(uid);
123169691Skan
124169691Skan	if (nitems) {
125169691Skan		/*
126169691Skan		 * Check for an active printer daemon (in which case we
127169691Skan		 *  kill it if it is reading our file) then remove stuff
128169691Skan		 *  (after which we have to restart the daemon).
129169691Skan		 */
130169691Skan		if (lockchk(pp, pp->lock_file) && chk(current)) {
131169691Skan			seteuid(euid);
132169691Skan			assasinated = kill(cur_daemon, SIGINT) == 0;
133169691Skan			seteuid(uid);
134169691Skan			if (!assasinated)
135169691Skan				fatal(pp, "cannot kill printer daemon");
136169691Skan		}
137169691Skan		/*
138169691Skan		 * process the files
139169691Skan		 */
140169691Skan		for (i = 0; i < nitems; i++)
141169691Skan			process(pp, files[i]->d_name);
142169691Skan	}
143169691Skan	rmremote(pp);
144169691Skan	/*
145169691Skan	 * Restart the printer daemon if it was killed
146169691Skan	 */
147169691Skan	if (assasinated && !startdaemon(pp))
148169691Skan		fatal(pp, "cannot restart printer daemon\n");
149169691Skan	exit(0);
150169691Skan}
151169691Skan
152169691Skan/*
153169691Skan * Process a lock file: collect the pid of the active
154169691Skan *  daemon and the file name of the active spool entry.
155169691Skan * Return boolean indicating existence of a lock file.
156169691Skan */
157169691Skanint
158169691Skanlockchk(struct printer *pp, char *slockf)
159169691Skan{
160169691Skan	register FILE *fp;
161169691Skan	register int i, n;
162169691Skan
163169691Skan	seteuid(euid);
164169691Skan	if ((fp = fopen(slockf, "r")) == NULL) {
165169691Skan		if (errno == EACCES)
166169691Skan			fatal(pp, "%s: %s", slockf, strerror(errno));
167169691Skan		else
168169691Skan			return(0);
169169691Skan	}
170169691Skan	seteuid(uid);
171169691Skan	if (!getline(fp)) {
172169691Skan		(void) fclose(fp);
173169691Skan		return(0);		/* no daemon present */
174169691Skan	}
175169691Skan	cur_daemon = atoi(line);
176169691Skan	if (kill(cur_daemon, 0) < 0 && errno != EPERM) {
177169691Skan		(void) fclose(fp);
178169691Skan		return(0);		/* no daemon present */
179169691Skan	}
180169691Skan	for (i = 1; (n = fread(current, sizeof(char), sizeof(current), fp)) <= 0; i++) {
181169691Skan		if (i > 5) {
182169691Skan			n = 1;
183169691Skan			break;
184169691Skan		}
185169691Skan		sleep(i);
186169691Skan	}
187169691Skan	current[n-1] = '\0';
188169691Skan	(void) fclose(fp);
189169691Skan	return(1);
190169691Skan}
191169691Skan
192169691Skan/*
193169691Skan * Process a control file.
194169691Skan */
195169691Skanvoid
196169691Skanprocess(const struct printer *pp, char *file)
197169691Skan{
198169691Skan	FILE *cfp;
199169691Skan
200169691Skan	if (!chk(file))
201169691Skan		return;
202169691Skan	seteuid(euid);
203169691Skan	if ((cfp = fopen(file, "r")) == NULL)
204169691Skan		fatal(pp, "cannot open %s", file);
205169691Skan	seteuid(uid);
206169691Skan	while (getline(cfp)) {
207169691Skan		switch (line[0]) {
208169691Skan		case 'U':  /* unlink associated files */
209169691Skan			if (strchr(line+1, '/') || strncmp(line+1, "df", 2))
210169691Skan				break;
211169691Skan			do_unlink(line+1);
212169691Skan		}
213169691Skan	}
214169691Skan	(void) fclose(cfp);
215169691Skan	do_unlink(file);
216169691Skan}
217169691Skan
218169691Skanstatic void
219169691Skando_unlink(char *file)
220169691Skan{
221169691Skan	int	ret;
222169691Skan
223169691Skan	if (from_host != local_host)
224169691Skan		printf("%s: ", local_host);
225169691Skan	seteuid(euid);
226169691Skan	ret = unlink(file);
227169691Skan	seteuid(uid);
228169691Skan	printf(ret ? "cannot dequeue %s\n" : "%s dequeued\n", file);
229169691Skan}
230169691Skan
231169691Skan/*
232169691Skan * Do the dirty work in checking
233169691Skan */
234169691Skanint
235169691Skanchk(char *file)
236169691Skan{
237169691Skan	int *r, jnum;
238169691Skan	char **u;
239169691Skan	const char *cfhost;
240169691Skan	FILE *cfp;
241169691Skan
242169691Skan	/*
243169691Skan	 * Check for valid cf file name (mostly checking current).
244169691Skan	 */
245169691Skan	if (strlen(file) < 7 || file[0] != 'c' || file[1] != 'f')
246169691Skan		return(0);
247169691Skan
248169691Skan	jnum = calc_jobnum(file, &cfhost);
249169691Skan	if (all && (from_host == local_host || !strcmp(from_host, cfhost)))
250169691Skan		return(1);
251169691Skan
252169691Skan	/*
253169691Skan	 * get the owner's name from the control file.
254169691Skan	 */
255169691Skan	seteuid(euid);
256169691Skan	if ((cfp = fopen(file, "r")) == NULL)
257169691Skan		return(0);
258169691Skan	seteuid(uid);
259169691Skan	while (getline(cfp)) {
260169691Skan		if (line[0] == 'P')
261169691Skan			break;
262169691Skan	}
263169691Skan	(void) fclose(cfp);
264169691Skan	if (line[0] != 'P')
265169691Skan		return(0);
266169691Skan
267169691Skan	if (users == 0 && requests == 0)
268169691Skan		return(!strcmp(file, current) && isowner(line+1, file, cfhost));
269169691Skan	/*
270169691Skan	 * Check the request list
271169691Skan	 */
272169691Skan	for (r = requ; r < &requ[requests]; r++)
273169691Skan		if (*r == jnum && isowner(line+1, file, cfhost))
274169691Skan			return(1);
275169691Skan	/*
276169691Skan	 * Check to see if it's in the user list
277169691Skan	 */
278169691Skan	for (u = user; u < &user[users]; u++)
279169691Skan		if (!strcmp(*u, line+1) && isowner(line+1, file, cfhost))
280169691Skan			return(1);
281169691Skan	return(0);
282169691Skan}
283169691Skan
284169691Skan/*
285169691Skan * If root is removing a file on the local machine, allow it.
286169691Skan * If root is removing a file from a remote machine, only allow
287169691Skan * files sent from the remote machine to be removed.
288169691Skan * Normal users can only remove the file from where it was sent.
289169691Skan */
290169691Skanstatic int
291169691Skanisowner(char *owner, char *file, const char *cfhost)
292169691Skan{
293169691Skan	if (!strcmp(person, root) && (from_host == local_host ||
294169691Skan	    !strcmp(from_host, cfhost)))
295169691Skan		return (1);
296169691Skan	if (!strcmp(person, owner) && !strcmp(from_host, cfhost))
297169691Skan		return (1);
298169691Skan	if (from_host != local_host)
299169691Skan		printf("%s: ", local_host);
300169691Skan	printf("%s: Permission denied\n", file);
301169691Skan	return(0);
302169691Skan}
303169691Skan
304169691Skan/*
305169691Skan * Check to see if we are sending files to a remote machine. If we are,
306169691Skan * then try removing files on the remote machine.
307169691Skan */
308169691Skanvoid
309169691Skanrmremote(const struct printer *pp)
310169691Skan{
311169691Skan	int i, elem, firstreq, niov, rem, totlen;
312169691Skan	char buf[BUFSIZ];
313169691Skan	void (*savealrm)(int);
314169691Skan	struct iovec *iov;
315169691Skan
316169691Skan	if (!pp->remote)
317169691Skan		return;	/* not sending to a remote machine */
318169691Skan
319169691Skan	/*
320169691Skan	 * Flush stdout so the user can see what has been deleted
321169691Skan	 * while we wait (possibly) for the connection.
322169691Skan	 */
323169691Skan	fflush(stdout);
324169691Skan
325169691Skan	/*
326169691Skan	 * Counting:
327169691Skan	 *	4 == "\5" + remote_queue + " " + person
328169691Skan	 *	2 * users == " " + user[i] for each user
329169691Skan	 *	requests == asprintf results for each request
330169691Skan	 *	1 == "\n"
331169691Skan	 * Although laborious, doing it this way makes it possible for
332169691Skan	 * us to process requests of indeterminate length without
333169691Skan	 * applying an arbitrary limit.  Arbitrary Limits Are Bad (tm).
334169691Skan	 */
335169691Skan	if (users > 0)
336169691Skan		niov = 4 + 2 * users + requests + 1;
337169691Skan	else
338169691Skan		niov = 4 + requests + 1;
339169691Skan	iov = malloc(niov * sizeof *iov);
340169691Skan	if (iov == 0)
341169691Skan		fatal(pp, "out of memory in rmremote()");
342169691Skan	iov[0].iov_base = "\5";
343169691Skan	iov[1].iov_base = pp->remote_queue;
344169691Skan	iov[2].iov_base = " ";
345169691Skan	iov[3].iov_base = all ? "-all" : person;
346169691Skan	elem = 4;
347169691Skan	for (i = 0; i < users; i++) {
348169691Skan		iov[elem].iov_base = " ";
349169691Skan		iov[elem + 1].iov_base = user[i];
350169691Skan		elem += 2;
351169691Skan	}
352169691Skan	firstreq = elem;
353169691Skan	for (i = 0; i < requests; i++) {
354169691Skan		asprintf((char **)&iov[elem].iov_base, " %d", requ[i]);
355169691Skan		if (iov[elem].iov_base == 0)
356169691Skan			fatal(pp, "out of memory in rmremote()");
357169691Skan		elem++;
358169691Skan	}
359169691Skan	iov[elem++].iov_base = "\n";
360169691Skan	for (totlen = i = 0; i < niov; i++)
361169691Skan		totlen += (iov[i].iov_len = strlen(iov[i].iov_base));
362169691Skan
363169691Skan	savealrm = signal(SIGALRM, alarmhandler);
364169691Skan	alarm(pp->conn_timeout);
365169691Skan	rem = getport(pp, pp->remote_host, 0);
366169691Skan	(void)signal(SIGALRM, savealrm);
367169691Skan	if (rem < 0) {
368169691Skan		if (from_host != local_host)
369169691Skan			printf("%s: ", local_host);
370169691Skan		printf("connection to %s is down\n", pp->remote_host);
371169691Skan	} else {
372169691Skan		if (writev(rem, iov, niov) != totlen)
373169691Skan			fatal(pp, "Lost connection");
374169691Skan		while ((i = read(rem, buf, sizeof(buf))) > 0)
375169691Skan			(void) fwrite(buf, 1, i, stdout);
376169691Skan		(void) close(rem);
377169691Skan	}
378169691Skan	for (i = 0; i < requests; i++)
379169691Skan		free(iov[firstreq + i].iov_base);
380169691Skan	free(iov);
381169691Skan}
382169691Skan
383169691Skan/*
384169691Skan * Return 1 if the filename begins with 'cf'
385169691Skan */
386169691Skanint
387169691Skaniscf(const struct dirent *d)
388169691Skan{
389169691Skan	return(d->d_name[0] == 'c' && d->d_name[1] == 'f');
390169691Skan}
391169691Skan
392169691Skanvoid
393169691Skanalarmhandler(int signo __unused)
394169691Skan{
395169691Skan	/* the signal is ignored */
396169691Skan	/* (the '__unused' is just to avoid a compile-time warning) */
397169691Skan}
398169691Skan