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 * 4. Neither the name of the University nor the names of its contributors
141553Srgrimes *    may be used to endorse or promote products derived from this software
151553Srgrimes *    without specific prior written permission.
161553Srgrimes *
171553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271553Srgrimes * SUCH DAMAGE.
281553Srgrimes */
291553Srgrimes
3029780Scharnier#if 0
31117592Sgad#ifndef lint
3215648Sjoergstatic char sccsid[] = "@(#)rmjob.c	8.2 (Berkeley) 4/28/95";
33117592Sgad#endif /* not lint */
3429780Scharnier#endif
35117592Sgad
36117541Sgad#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
37117541Sgad__FBSDID("$FreeBSD$");
381553Srgrimes
391553Srgrimes#include <sys/param.h>
4031492Swollman#include <sys/uio.h>
411553Srgrimes
4231492Swollman#include <ctype.h>
4331492Swollman#include <dirent.h>
44241852Seadler#include <err.h>
4531492Swollman#include <errno.h>
461553Srgrimes#include <signal.h>
4731492Swollman#include <stdio.h>
481553Srgrimes#include <stdlib.h>
491553Srgrimes#include <string.h>
5031492Swollman#define psignal foil_gcc_psignal
5131492Swollman#define	sys_siglist foil_gcc_siglist
5231492Swollman#include <unistd.h>
5331492Swollman#undef psignal
5431492Swollman#undef sys_siglist
5531492Swollman
561553Srgrimes#include "lp.h"
571553Srgrimes#include "lp.local.h"
581553Srgrimes#include "pathnames.h"
591553Srgrimes
601553Srgrimes/*
611553Srgrimes * rmjob - remove the specified jobs from the queue.
621553Srgrimes */
631553Srgrimes
641553Srgrimes/*
651553Srgrimes * Stuff for handling lprm specifications
661553Srgrimes */
671553Srgrimesstatic char	root[] = "root";
681553Srgrimesstatic int	all = 0;		/* eliminate all files (root only) */
691553Srgrimesstatic int	cur_daemon;		/* daemon's pid */
7068342Sgadstatic char	current[7+MAXHOSTNAMELEN];  /* active control file name */
711553Srgrimes
7278146Sgadstatic	void	alarmhandler(int _signo);
7378146Sgadstatic	void	do_unlink(char *_file);
74139464Sgadstatic int	 isowner(char *_owner, char *_file, const char *_cfhost);
7527618Simp
761553Srgrimesvoid
7778146Sgadrmjob(const char *printer)
781553Srgrimes{
791553Srgrimes	register int i, nitems;
80228990Suqs	int assassinated = 0;
811553Srgrimes	struct dirent **files;
821553Srgrimes	char *cp;
8331492Swollman	struct printer myprinter, *pp = &myprinter;
841553Srgrimes
8531492Swollman	init_printer(pp);
8631492Swollman	if ((i = getprintcap(printer, pp)) < 0)
8731492Swollman		fatal(pp, "getprintcap: %s", pcaperr(i));
8831492Swollman	if ((cp = checkremote(pp))) {
891553Srgrimes		printf("Warning: %s\n", cp);
9031492Swollman		free(cp);
9131492Swollman	}
921553Srgrimes
931553Srgrimes	/*
941553Srgrimes	 * If the format was `lprm -' and the user isn't the super-user,
951553Srgrimes	 *  then fake things to look like he said `lprm user'.
961553Srgrimes	 */
971553Srgrimes	if (users < 0) {
981553Srgrimes		if (getuid() == 0)
991553Srgrimes			all = 1;	/* all files in local queue */
1001553Srgrimes		else {
1011553Srgrimes			user[0] = person;
1021553Srgrimes			users = 1;
1031553Srgrimes		}
1041553Srgrimes	}
1051553Srgrimes	if (!strcmp(person, "-all")) {
10678300Sgad		if (from_host == local_host)
10731492Swollman			fatal(pp, "The login name \"-all\" is reserved");
10878300Sgad		all = 1;	/* all those from 'from_host' */
1091553Srgrimes		person = root;
1101553Srgrimes	}
1111553Srgrimes
112241852Seadler	PRIV_START
11331492Swollman	if (chdir(pp->spool_dir) < 0)
11431492Swollman		fatal(pp, "cannot chdir to spool directory");
1151553Srgrimes	if ((nitems = scandir(".", &files, iscf, NULL)) < 0)
11631492Swollman		fatal(pp, "cannot access spool directory");
117241852Seadler	PRIV_END
1181553Srgrimes
1191553Srgrimes	if (nitems) {
1201553Srgrimes		/*
1211553Srgrimes		 * Check for an active printer daemon (in which case we
1221553Srgrimes		 *  kill it if it is reading our file) then remove stuff
1231553Srgrimes		 *  (after which we have to restart the daemon).
1241553Srgrimes		 */
12531492Swollman		if (lockchk(pp, pp->lock_file) && chk(current)) {
126241852Seadler			PRIV_START
127228990Suqs			assassinated = kill(cur_daemon, SIGINT) == 0;
128241852Seadler			PRIV_END
129228990Suqs			if (!assassinated)
13031492Swollman				fatal(pp, "cannot kill printer daemon");
1311553Srgrimes		}
1321553Srgrimes		/*
1331553Srgrimes		 * process the files
1341553Srgrimes		 */
1351553Srgrimes		for (i = 0; i < nitems; i++)
13631492Swollman			process(pp, files[i]->d_name);
1371553Srgrimes	}
13831492Swollman	rmremote(pp);
1391553Srgrimes	/*
1401553Srgrimes	 * Restart the printer daemon if it was killed
1411553Srgrimes	 */
142228990Suqs	if (assassinated && !startdaemon(pp))
14331492Swollman		fatal(pp, "cannot restart printer daemon\n");
1441553Srgrimes	exit(0);
1451553Srgrimes}
1461553Srgrimes
1471553Srgrimes/*
1481553Srgrimes * Process a lock file: collect the pid of the active
1491553Srgrimes *  daemon and the file name of the active spool entry.
1501553Srgrimes * Return boolean indicating existence of a lock file.
1511553Srgrimes */
1521553Srgrimesint
15378146Sgadlockchk(struct printer *pp, char *slockf)
1541553Srgrimes{
1551553Srgrimes	register FILE *fp;
1561553Srgrimes	register int i, n;
1571553Srgrimes
158241852Seadler	PRIV_START
15978146Sgad	if ((fp = fopen(slockf, "r")) == NULL) {
1601553Srgrimes		if (errno == EACCES)
16178146Sgad			fatal(pp, "%s: %s", slockf, strerror(errno));
1621553Srgrimes		else
1631553Srgrimes			return(0);
16427618Simp	}
165241852Seadler	PRIV_END
1661553Srgrimes	if (!getline(fp)) {
1671553Srgrimes		(void) fclose(fp);
1681553Srgrimes		return(0);		/* no daemon present */
1691553Srgrimes	}
1701553Srgrimes	cur_daemon = atoi(line);
17127618Simp	if (kill(cur_daemon, 0) < 0 && errno != EPERM) {
1721553Srgrimes		(void) fclose(fp);
1731553Srgrimes		return(0);		/* no daemon present */
1741553Srgrimes	}
1751553Srgrimes	for (i = 1; (n = fread(current, sizeof(char), sizeof(current), fp)) <= 0; i++) {
1761553Srgrimes		if (i > 5) {
1771553Srgrimes			n = 1;
1781553Srgrimes			break;
1791553Srgrimes		}
1801553Srgrimes		sleep(i);
1811553Srgrimes	}
1821553Srgrimes	current[n-1] = '\0';
1831553Srgrimes	(void) fclose(fp);
1841553Srgrimes	return(1);
1851553Srgrimes}
1861553Srgrimes
1871553Srgrimes/*
1881553Srgrimes * Process a control file.
1891553Srgrimes */
1901553Srgrimesvoid
19178146Sgadprocess(const struct printer *pp, char *file)
1921553Srgrimes{
1931553Srgrimes	FILE *cfp;
1941553Srgrimes
1951553Srgrimes	if (!chk(file))
1961553Srgrimes		return;
197241852Seadler	PRIV_START
1981553Srgrimes	if ((cfp = fopen(file, "r")) == NULL)
19931492Swollman		fatal(pp, "cannot open %s", file);
200241852Seadler	PRIV_END
2011553Srgrimes	while (getline(cfp)) {
2021553Srgrimes		switch (line[0]) {
2031553Srgrimes		case 'U':  /* unlink associated files */
20427509Simp			if (strchr(line+1, '/') || strncmp(line+1, "df", 2))
20527509Simp				break;
20627618Simp			do_unlink(line+1);
2071553Srgrimes		}
2081553Srgrimes	}
2091553Srgrimes	(void) fclose(cfp);
21027618Simp	do_unlink(file);
21127618Simp}
21227618Simp
21327618Simpstatic void
21478146Sgaddo_unlink(char *file)
21527618Simp{
21627618Simp	int	ret;
21727618Simp
21878300Sgad	if (from_host != local_host)
21978300Sgad		printf("%s: ", local_host);
220241852Seadler	PRIV_START
22127618Simp	ret = unlink(file);
222241852Seadler	PRIV_END
22327618Simp	printf(ret ? "cannot dequeue %s\n" : "%s dequeued\n", file);
2241553Srgrimes}
2251553Srgrimes
2261553Srgrimes/*
2271553Srgrimes * Do the dirty work in checking
2281553Srgrimes */
2291553Srgrimesint
23078146Sgadchk(char *file)
2311553Srgrimes{
232139464Sgad	int *r, jnum;
233139464Sgad	char **u;
234139464Sgad	const char *cfhost;
2351553Srgrimes	FILE *cfp;
2361553Srgrimes
2371553Srgrimes	/*
2381553Srgrimes	 * Check for valid cf file name (mostly checking current).
2391553Srgrimes	 */
2401553Srgrimes	if (strlen(file) < 7 || file[0] != 'c' || file[1] != 'f')
2411553Srgrimes		return(0);
2421553Srgrimes
243139464Sgad	jnum = calc_jobnum(file, &cfhost);
244139464Sgad	if (all && (from_host == local_host || !strcmp(from_host, cfhost)))
2451553Srgrimes		return(1);
2461553Srgrimes
2471553Srgrimes	/*
2481553Srgrimes	 * get the owner's name from the control file.
2491553Srgrimes	 */
250241852Seadler	PRIV_START
2511553Srgrimes	if ((cfp = fopen(file, "r")) == NULL)
2521553Srgrimes		return(0);
253241852Seadler	PRIV_END
2541553Srgrimes	while (getline(cfp)) {
2551553Srgrimes		if (line[0] == 'P')
2561553Srgrimes			break;
2571553Srgrimes	}
2581553Srgrimes	(void) fclose(cfp);
2591553Srgrimes	if (line[0] != 'P')
2601553Srgrimes		return(0);
2611553Srgrimes
2621553Srgrimes	if (users == 0 && requests == 0)
263139464Sgad		return(!strcmp(file, current) && isowner(line+1, file, cfhost));
2641553Srgrimes	/*
2651553Srgrimes	 * Check the request list
2661553Srgrimes	 */
2671553Srgrimes	for (r = requ; r < &requ[requests]; r++)
268139464Sgad		if (*r == jnum && isowner(line+1, file, cfhost))
2691553Srgrimes			return(1);
2701553Srgrimes	/*
2711553Srgrimes	 * Check to see if it's in the user list
2721553Srgrimes	 */
2731553Srgrimes	for (u = user; u < &user[users]; u++)
274139464Sgad		if (!strcmp(*u, line+1) && isowner(line+1, file, cfhost))
2751553Srgrimes			return(1);
2761553Srgrimes	return(0);
2771553Srgrimes}
2781553Srgrimes
2791553Srgrimes/*
2801553Srgrimes * If root is removing a file on the local machine, allow it.
2811553Srgrimes * If root is removing a file from a remote machine, only allow
2821553Srgrimes * files sent from the remote machine to be removed.
2831553Srgrimes * Normal users can only remove the file from where it was sent.
2841553Srgrimes */
285139464Sgadstatic int
286139464Sgadisowner(char *owner, char *file, const char *cfhost)
2871553Srgrimes{
28878300Sgad	if (!strcmp(person, root) && (from_host == local_host ||
289139464Sgad	    !strcmp(from_host, cfhost)))
29078146Sgad		return (1);
291139464Sgad	if (!strcmp(person, owner) && !strcmp(from_host, cfhost))
29278146Sgad		return (1);
29378300Sgad	if (from_host != local_host)
29478300Sgad		printf("%s: ", local_host);
2951553Srgrimes	printf("%s: Permission denied\n", file);
2961553Srgrimes	return(0);
2971553Srgrimes}
2981553Srgrimes
2991553Srgrimes/*
3001553Srgrimes * Check to see if we are sending files to a remote machine. If we are,
3011553Srgrimes * then try removing files on the remote machine.
3021553Srgrimes */
3031553Srgrimesvoid
30478146Sgadrmremote(const struct printer *pp)
3051553Srgrimes{
30674126Sgad	int i, elem, firstreq, niov, rem, totlen;
3071553Srgrimes	char buf[BUFSIZ];
30830407Sjoerg	void (*savealrm)(int);
30931492Swollman	struct iovec *iov;
3101553Srgrimes
31131492Swollman	if (!pp->remote)
3121553Srgrimes		return;	/* not sending to a remote machine */
3131553Srgrimes
3141553Srgrimes	/*
3151553Srgrimes	 * Flush stdout so the user can see what has been deleted
3161553Srgrimes	 * while we wait (possibly) for the connection.
3171553Srgrimes	 */
3181553Srgrimes	fflush(stdout);
3191553Srgrimes
32031492Swollman	/*
32131492Swollman	 * Counting:
32231492Swollman	 *	4 == "\5" + remote_queue + " " + person
32331492Swollman	 *	2 * users == " " + user[i] for each user
32431492Swollman	 *	requests == asprintf results for each request
32531492Swollman	 *	1 == "\n"
32631492Swollman	 * Although laborious, doing it this way makes it possible for
32731492Swollman	 * us to process requests of indeterminate length without
32831492Swollman	 * applying an arbitrary limit.  Arbitrary Limits Are Bad (tm).
32931492Swollman	 */
33074126Sgad	if (users > 0)
33174126Sgad		niov = 4 + 2 * users + requests + 1;
33274126Sgad	else
33374126Sgad		niov = 4 + requests + 1;
33431492Swollman	iov = malloc(niov * sizeof *iov);
33531492Swollman	if (iov == 0)
33674126Sgad		fatal(pp, "out of memory in rmremote()");
33731492Swollman	iov[0].iov_base = "\5";
33831492Swollman	iov[1].iov_base = pp->remote_queue;
33931492Swollman	iov[2].iov_base = " ";
34031492Swollman	iov[3].iov_base = all ? "-all" : person;
34174126Sgad	elem = 4;
34231492Swollman	for (i = 0; i < users; i++) {
34374126Sgad		iov[elem].iov_base = " ";
34474126Sgad		iov[elem + 1].iov_base = user[i];
34574126Sgad		elem += 2;
3461553Srgrimes	}
34774126Sgad	firstreq = elem;
34831492Swollman	for (i = 0; i < requests; i++) {
349121525Speter		asprintf((char **)&iov[elem].iov_base, " %d", requ[i]);
35074126Sgad		if (iov[elem].iov_base == 0)
35174126Sgad			fatal(pp, "out of memory in rmremote()");
35274126Sgad		elem++;
3531553Srgrimes	}
35474126Sgad	iov[elem++].iov_base = "\n";
35531492Swollman	for (totlen = i = 0; i < niov; i++)
35631492Swollman		totlen += (iov[i].iov_len = strlen(iov[i].iov_base));
35731492Swollman
35830407Sjoerg	savealrm = signal(SIGALRM, alarmhandler);
35931492Swollman	alarm(pp->conn_timeout);
36031492Swollman	rem = getport(pp, pp->remote_host, 0);
36130407Sjoerg	(void)signal(SIGALRM, savealrm);
3621553Srgrimes	if (rem < 0) {
36378300Sgad		if (from_host != local_host)
36478300Sgad			printf("%s: ", local_host);
36531492Swollman		printf("connection to %s is down\n", pp->remote_host);
3661553Srgrimes	} else {
36731492Swollman		if (writev(rem, iov, niov) != totlen)
36831492Swollman			fatal(pp, "Lost connection");
3691553Srgrimes		while ((i = read(rem, buf, sizeof(buf))) > 0)
3701553Srgrimes			(void) fwrite(buf, 1, i, stdout);
3711553Srgrimes		(void) close(rem);
3721553Srgrimes	}
37331492Swollman	for (i = 0; i < requests; i++)
37474126Sgad		free(iov[firstreq + i].iov_base);
37531492Swollman	free(iov);
3761553Srgrimes}
3771553Srgrimes
3781553Srgrimes/*
3791553Srgrimes * Return 1 if the filename begins with 'cf'
3801553Srgrimes */
3811553Srgrimesint
382201512Skibiscf(const struct dirent *d)
3831553Srgrimes{
3841553Srgrimes	return(d->d_name[0] == 'c' && d->d_name[1] == 'f');
3851553Srgrimes}
38630407Sjoerg
38730407Sjoergvoid
38878146Sgadalarmhandler(int signo __unused)
38930407Sjoerg{
39078146Sgad	/* the signal is ignored */
39178146Sgad	/* (the '__unused' is just to avoid a compile-time warning) */
39230407Sjoerg}
393