rmjob.c revision 78146
11553Srgrimes/*
21553Srgrimes * Copyright (c) 1983, 1993
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes * Redistribution and use in source and binary forms, with or without
61553Srgrimes * modification, are permitted provided that the following conditions
71553Srgrimes * are met:
81553Srgrimes * 1. Redistributions of source code must retain the above copyright
91553Srgrimes *    notice, this list of conditions and the following disclaimer.
101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111553Srgrimes *    notice, this list of conditions and the following disclaimer in the
121553Srgrimes *    documentation and/or other materials provided with the distribution.
131553Srgrimes * 3. All advertising materials mentioning features or use of this software
141553Srgrimes *    must display the following acknowledgement:
151553Srgrimes *	This product includes software developed by the University of
161553Srgrimes *	California, Berkeley and its contributors.
171553Srgrimes * 4. Neither the name of the University nor the names of its contributors
181553Srgrimes *    may be used to endorse or promote products derived from this software
191553Srgrimes *    without specific prior written permission.
201553Srgrimes *
211553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311553Srgrimes * SUCH DAMAGE.
321553Srgrimes */
331553Srgrimes
341553Srgrimes#ifndef lint
3529780Scharnier#if 0
3615648Sjoergstatic char sccsid[] = "@(#)rmjob.c	8.2 (Berkeley) 4/28/95";
3729780Scharnier#endif
3829780Scharnierstatic const char rcsid[] =
3950479Speter  "$FreeBSD: head/usr.sbin/lpr/common_source/rmjob.c 78146 2001-06-12 16:38:20Z gad $";
401553Srgrimes#endif /* not lint */
411553Srgrimes
421553Srgrimes#include <sys/param.h>
4331492Swollman#include <sys/uio.h>
441553Srgrimes
4531492Swollman#include <ctype.h>
4631492Swollman#include <dirent.h>
4731492Swollman#include <errno.h>
481553Srgrimes#include <signal.h>
4931492Swollman#include <stdio.h>
501553Srgrimes#include <stdlib.h>
511553Srgrimes#include <string.h>
5231492Swollman#define psignal foil_gcc_psignal
5331492Swollman#define	sys_siglist foil_gcc_siglist
5431492Swollman#include <unistd.h>
5531492Swollman#undef psignal
5631492Swollman#undef sys_siglist
5731492Swollman
581553Srgrimes#include "lp.h"
591553Srgrimes#include "lp.local.h"
601553Srgrimes#include "pathnames.h"
611553Srgrimes
621553Srgrimes/*
631553Srgrimes * rmjob - remove the specified jobs from the queue.
641553Srgrimes */
651553Srgrimes
661553Srgrimes/*
671553Srgrimes * Stuff for handling lprm specifications
681553Srgrimes */
691553Srgrimesstatic char	root[] = "root";
701553Srgrimesstatic int	all = 0;		/* eliminate all files (root only) */
711553Srgrimesstatic int	cur_daemon;		/* daemon's pid */
7268342Sgadstatic char	current[7+MAXHOSTNAMELEN];  /* active control file name */
731553Srgrimes
7427618Simpextern uid_t	uid, euid;		/* real and effective user id's */
7527618Simp
7678146Sgadstatic	void	alarmhandler(int _signo);
7778146Sgadstatic	void	do_unlink(char *_file);
7827618Simp
791553Srgrimesvoid
8078146Sgadrmjob(const char *printer)
811553Srgrimes{
821553Srgrimes	register int i, nitems;
831553Srgrimes	int assasinated = 0;
841553Srgrimes	struct dirent **files;
851553Srgrimes	char *cp;
8631492Swollman	struct printer myprinter, *pp = &myprinter;
871553Srgrimes
8831492Swollman	init_printer(pp);
8931492Swollman	if ((i = getprintcap(printer, pp)) < 0)
9031492Swollman		fatal(pp, "getprintcap: %s", pcaperr(i));
9131492Swollman	if ((cp = checkremote(pp))) {
921553Srgrimes		printf("Warning: %s\n", cp);
9331492Swollman		free(cp);
9431492Swollman	}
951553Srgrimes
961553Srgrimes	/*
971553Srgrimes	 * If the format was `lprm -' and the user isn't the super-user,
981553Srgrimes	 *  then fake things to look like he said `lprm user'.
991553Srgrimes	 */
1001553Srgrimes	if (users < 0) {
1011553Srgrimes		if (getuid() == 0)
1021553Srgrimes			all = 1;	/* all files in local queue */
1031553Srgrimes		else {
1041553Srgrimes			user[0] = person;
1051553Srgrimes			users = 1;
1061553Srgrimes		}
1071553Srgrimes	}
1081553Srgrimes	if (!strcmp(person, "-all")) {
1091553Srgrimes		if (from == host)
11031492Swollman			fatal(pp, "The login name \"-all\" is reserved");
1111553Srgrimes		all = 1;	/* all those from 'from' */
1121553Srgrimes		person = root;
1131553Srgrimes	}
1141553Srgrimes
11527618Simp	seteuid(euid);
11631492Swollman	if (chdir(pp->spool_dir) < 0)
11731492Swollman		fatal(pp, "cannot chdir to spool directory");
1181553Srgrimes	if ((nitems = scandir(".", &files, iscf, NULL)) < 0)
11931492Swollman		fatal(pp, "cannot access spool directory");
12027618Simp	seteuid(uid);
1211553Srgrimes
1221553Srgrimes	if (nitems) {
1231553Srgrimes		/*
1241553Srgrimes		 * Check for an active printer daemon (in which case we
1251553Srgrimes		 *  kill it if it is reading our file) then remove stuff
1261553Srgrimes		 *  (after which we have to restart the daemon).
1271553Srgrimes		 */
12831492Swollman		if (lockchk(pp, pp->lock_file) && chk(current)) {
12927618Simp			seteuid(euid);
1301553Srgrimes			assasinated = kill(cur_daemon, SIGINT) == 0;
13127618Simp			seteuid(uid);
1321553Srgrimes			if (!assasinated)
13331492Swollman				fatal(pp, "cannot kill printer daemon");
1341553Srgrimes		}
1351553Srgrimes		/*
1361553Srgrimes		 * process the files
1371553Srgrimes		 */
1381553Srgrimes		for (i = 0; i < nitems; i++)
13931492Swollman			process(pp, files[i]->d_name);
1401553Srgrimes	}
14131492Swollman	rmremote(pp);
1421553Srgrimes	/*
1431553Srgrimes	 * Restart the printer daemon if it was killed
1441553Srgrimes	 */
14531492Swollman	if (assasinated && !startdaemon(pp))
14631492Swollman		fatal(pp, "cannot restart printer daemon\n");
1471553Srgrimes	exit(0);
1481553Srgrimes}
1491553Srgrimes
1501553Srgrimes/*
1511553Srgrimes * Process a lock file: collect the pid of the active
1521553Srgrimes *  daemon and the file name of the active spool entry.
1531553Srgrimes * Return boolean indicating existence of a lock file.
1541553Srgrimes */
1551553Srgrimesint
15678146Sgadlockchk(struct printer *pp, char *slockf)
1571553Srgrimes{
1581553Srgrimes	register FILE *fp;
1591553Srgrimes	register int i, n;
1601553Srgrimes
16127618Simp	seteuid(euid);
16278146Sgad	if ((fp = fopen(slockf, "r")) == NULL) {
1631553Srgrimes		if (errno == EACCES)
16478146Sgad			fatal(pp, "%s: %s", slockf, strerror(errno));
1651553Srgrimes		else
1661553Srgrimes			return(0);
16727618Simp	}
16827618Simp	seteuid(uid);
1691553Srgrimes	if (!getline(fp)) {
1701553Srgrimes		(void) fclose(fp);
1711553Srgrimes		return(0);		/* no daemon present */
1721553Srgrimes	}
1731553Srgrimes	cur_daemon = atoi(line);
17427618Simp	if (kill(cur_daemon, 0) < 0 && errno != EPERM) {
1751553Srgrimes		(void) fclose(fp);
1761553Srgrimes		return(0);		/* no daemon present */
1771553Srgrimes	}
1781553Srgrimes	for (i = 1; (n = fread(current, sizeof(char), sizeof(current), fp)) <= 0; i++) {
1791553Srgrimes		if (i > 5) {
1801553Srgrimes			n = 1;
1811553Srgrimes			break;
1821553Srgrimes		}
1831553Srgrimes		sleep(i);
1841553Srgrimes	}
1851553Srgrimes	current[n-1] = '\0';
1861553Srgrimes	(void) fclose(fp);
1871553Srgrimes	return(1);
1881553Srgrimes}
1891553Srgrimes
1901553Srgrimes/*
1911553Srgrimes * Process a control file.
1921553Srgrimes */
1931553Srgrimesvoid
19478146Sgadprocess(const struct printer *pp, char *file)
1951553Srgrimes{
1961553Srgrimes	FILE *cfp;
1971553Srgrimes
1981553Srgrimes	if (!chk(file))
1991553Srgrimes		return;
20027618Simp	seteuid(euid);
2011553Srgrimes	if ((cfp = fopen(file, "r")) == NULL)
20231492Swollman		fatal(pp, "cannot open %s", file);
20327618Simp	seteuid(uid);
2041553Srgrimes	while (getline(cfp)) {
2051553Srgrimes		switch (line[0]) {
2061553Srgrimes		case 'U':  /* unlink associated files */
20727509Simp			if (strchr(line+1, '/') || strncmp(line+1, "df", 2))
20827509Simp				break;
20927618Simp			do_unlink(line+1);
2101553Srgrimes		}
2111553Srgrimes	}
2121553Srgrimes	(void) fclose(cfp);
21327618Simp	do_unlink(file);
21427618Simp}
21527618Simp
21627618Simpstatic void
21778146Sgaddo_unlink(char *file)
21827618Simp{
21927618Simp	int	ret;
22027618Simp
2211553Srgrimes	if (from != host)
2221553Srgrimes		printf("%s: ", host);
22327618Simp	seteuid(euid);
22427618Simp	ret = unlink(file);
22527618Simp	seteuid(uid);
22627618Simp	printf(ret ? "cannot dequeue %s\n" : "%s dequeued\n", file);
2271553Srgrimes}
2281553Srgrimes
2291553Srgrimes/*
2301553Srgrimes * Do the dirty work in checking
2311553Srgrimes */
2321553Srgrimesint
23378146Sgadchk(char *file)
2341553Srgrimes{
2351553Srgrimes	register int *r, n;
2361553Srgrimes	register char **u, *cp;
2371553Srgrimes	FILE *cfp;
2381553Srgrimes
2391553Srgrimes	/*
2401553Srgrimes	 * Check for valid cf file name (mostly checking current).
2411553Srgrimes	 */
2421553Srgrimes	if (strlen(file) < 7 || file[0] != 'c' || file[1] != 'f')
2431553Srgrimes		return(0);
2441553Srgrimes
2451553Srgrimes	if (all && (from == host || !strcmp(from, file+6)))
2461553Srgrimes		return(1);
2471553Srgrimes
2481553Srgrimes	/*
2491553Srgrimes	 * get the owner's name from the control file.
2501553Srgrimes	 */
25127618Simp	seteuid(euid);
2521553Srgrimes	if ((cfp = fopen(file, "r")) == NULL)
2531553Srgrimes		return(0);
25427618Simp	seteuid(uid);
2551553Srgrimes	while (getline(cfp)) {
2561553Srgrimes		if (line[0] == 'P')
2571553Srgrimes			break;
2581553Srgrimes	}
2591553Srgrimes	(void) fclose(cfp);
2601553Srgrimes	if (line[0] != 'P')
2611553Srgrimes		return(0);
2621553Srgrimes
2631553Srgrimes	if (users == 0 && requests == 0)
2641553Srgrimes		return(!strcmp(file, current) && isowner(line+1, file));
2651553Srgrimes	/*
2661553Srgrimes	 * Check the request list
2671553Srgrimes	 */
2681553Srgrimes	for (n = 0, cp = file+3; isdigit(*cp); )
2691553Srgrimes		n = n * 10 + (*cp++ - '0');
2701553Srgrimes	for (r = requ; r < &requ[requests]; r++)
2711553Srgrimes		if (*r == n && isowner(line+1, file))
2721553Srgrimes			return(1);
2731553Srgrimes	/*
2741553Srgrimes	 * Check to see if it's in the user list
2751553Srgrimes	 */
2761553Srgrimes	for (u = user; u < &user[users]; u++)
2771553Srgrimes		if (!strcmp(*u, line+1) && isowner(line+1, file))
2781553Srgrimes			return(1);
2791553Srgrimes	return(0);
2801553Srgrimes}
2811553Srgrimes
2821553Srgrimes/*
2831553Srgrimes * If root is removing a file on the local machine, allow it.
2841553Srgrimes * If root is removing a file from a remote machine, only allow
2851553Srgrimes * files sent from the remote machine to be removed.
2861553Srgrimes * Normal users can only remove the file from where it was sent.
2871553Srgrimes */
2881553Srgrimesint
28978146Sgadisowner(char *owner, char *file)
2901553Srgrimes{
2911553Srgrimes	if (!strcmp(person, root) && (from == host || !strcmp(from, file+6)))
29278146Sgad		return (1);
2931553Srgrimes	if (!strcmp(person, owner) && !strcmp(from, file+6))
29478146Sgad		return (1);
2951553Srgrimes	if (from != host)
2961553Srgrimes		printf("%s: ", host);
2971553Srgrimes	printf("%s: Permission denied\n", file);
2981553Srgrimes	return(0);
2991553Srgrimes}
3001553Srgrimes
3011553Srgrimes/*
3021553Srgrimes * Check to see if we are sending files to a remote machine. If we are,
3031553Srgrimes * then try removing files on the remote machine.
3041553Srgrimes */
3051553Srgrimesvoid
30678146Sgadrmremote(const struct printer *pp)
3071553Srgrimes{
30874126Sgad	int i, elem, firstreq, niov, rem, totlen;
3091553Srgrimes	char buf[BUFSIZ];
31030407Sjoerg	void (*savealrm)(int);
31131492Swollman	struct iovec *iov;
3121553Srgrimes
31331492Swollman	if (!pp->remote)
3141553Srgrimes		return;	/* not sending to a remote machine */
3151553Srgrimes
3161553Srgrimes	/*
3171553Srgrimes	 * Flush stdout so the user can see what has been deleted
3181553Srgrimes	 * while we wait (possibly) for the connection.
3191553Srgrimes	 */
3201553Srgrimes	fflush(stdout);
3211553Srgrimes
32231492Swollman	/*
32331492Swollman	 * Counting:
32431492Swollman	 *	4 == "\5" + remote_queue + " " + person
32531492Swollman	 *	2 * users == " " + user[i] for each user
32631492Swollman	 *	requests == asprintf results for each request
32731492Swollman	 *	1 == "\n"
32831492Swollman	 * Although laborious, doing it this way makes it possible for
32931492Swollman	 * us to process requests of indeterminate length without
33031492Swollman	 * applying an arbitrary limit.  Arbitrary Limits Are Bad (tm).
33131492Swollman	 */
33274126Sgad	if (users > 0)
33374126Sgad		niov = 4 + 2 * users + requests + 1;
33474126Sgad	else
33574126Sgad		niov = 4 + requests + 1;
33631492Swollman	iov = malloc(niov * sizeof *iov);
33731492Swollman	if (iov == 0)
33874126Sgad		fatal(pp, "out of memory in rmremote()");
33931492Swollman	iov[0].iov_base = "\5";
34031492Swollman	iov[1].iov_base = pp->remote_queue;
34131492Swollman	iov[2].iov_base = " ";
34231492Swollman	iov[3].iov_base = all ? "-all" : person;
34374126Sgad	elem = 4;
34431492Swollman	for (i = 0; i < users; i++) {
34574126Sgad		iov[elem].iov_base = " ";
34674126Sgad		iov[elem + 1].iov_base = user[i];
34774126Sgad		elem += 2;
3481553Srgrimes	}
34974126Sgad	firstreq = elem;
35031492Swollman	for (i = 0; i < requests; i++) {
35174126Sgad		asprintf(&iov[elem].iov_base, " %d", requ[i]);
35274126Sgad		if (iov[elem].iov_base == 0)
35374126Sgad			fatal(pp, "out of memory in rmremote()");
35474126Sgad		elem++;
3551553Srgrimes	}
35674126Sgad	iov[elem++].iov_base = "\n";
35731492Swollman	for (totlen = i = 0; i < niov; i++)
35831492Swollman		totlen += (iov[i].iov_len = strlen(iov[i].iov_base));
35931492Swollman
36030407Sjoerg	savealrm = signal(SIGALRM, alarmhandler);
36131492Swollman	alarm(pp->conn_timeout);
36231492Swollman	rem = getport(pp, pp->remote_host, 0);
36330407Sjoerg	(void)signal(SIGALRM, savealrm);
3641553Srgrimes	if (rem < 0) {
3651553Srgrimes		if (from != host)
3661553Srgrimes			printf("%s: ", host);
36731492Swollman		printf("connection to %s is down\n", pp->remote_host);
3681553Srgrimes	} else {
36931492Swollman		if (writev(rem, iov, niov) != totlen)
37031492Swollman			fatal(pp, "Lost connection");
3711553Srgrimes		while ((i = read(rem, buf, sizeof(buf))) > 0)
3721553Srgrimes			(void) fwrite(buf, 1, i, stdout);
3731553Srgrimes		(void) close(rem);
3741553Srgrimes	}
37531492Swollman	for (i = 0; i < requests; i++)
37674126Sgad		free(iov[firstreq + i].iov_base);
37731492Swollman	free(iov);
3781553Srgrimes}
3791553Srgrimes
3801553Srgrimes/*
3811553Srgrimes * Return 1 if the filename begins with 'cf'
3821553Srgrimes */
3831553Srgrimesint
38478146Sgadiscf(struct dirent *d)
3851553Srgrimes{
3861553Srgrimes	return(d->d_name[0] == 'c' && d->d_name[1] == 'f');
3871553Srgrimes}
38830407Sjoerg
38930407Sjoergvoid
39078146Sgadalarmhandler(int signo __unused)
39130407Sjoerg{
39278146Sgad	/* the signal is ignored */
39378146Sgad	/* (the '__unused' is just to avoid a compile-time warning) */
39430407Sjoerg}
395