xref: /openbsd/usr.sbin/lpd/lp_rmjob.c (revision 274d7c50)
1 /*	$OpenBSD: lp_rmjob.c,v 1.1.1.1 2018/04/27 16:14:36 eric Exp $	*/
2 
3 /*
4  * Copyright (c) 2017 Eric Faurot <eric@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <limits.h>
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 
25 #include "lp.h"
26 
27 #include "log.h"
28 
29 static int docheck(struct lp_printer *, const char *, struct lp_jobfilter *,
30     const char *, int, int);
31 static void doremove(int, struct lp_printer *, const char *);
32 
33 int
34 lp_rmjob(int ofd, struct lp_printer *lp, const char *agent,
35     struct lp_jobfilter *jf)
36 {
37 	struct lp_queue q;
38 	char currjob[PATH_MAX];
39 	pid_t currpid;
40 	int active, i, killed = 0;
41 
42 	if ((lp_readqueue(lp, &q)) == -1) {
43 		log_warnx("cannot read queue");
44 		return 0;
45 	}
46 
47 	if (q.count == 0) {
48 		lp_clearqueue(&q);
49 		return 0;
50 	}
51 
52 	/*
53 	 * Find the current task being printed, and kill the printer process
54 	 * if the file is to be removed.
55 	 */
56 	if (lp_getcurrtask(lp, &currpid, currjob, sizeof(currjob)) == -1)
57 		log_warnx("cannot get current task");
58 
59 	if (currjob[0] && docheck(lp, agent, jf, currjob, 1, 0) == 1) {
60 		if (kill(currpid, SIGINT) == -1)
61 			log_warn("lpr: cannot kill printer process %d",
62 			    (int)currpid);
63 		else
64 			killed = 1;
65 	}
66 
67 	for(i = 0; i < q.count; i++) {
68 		active = !strcmp(q.cfname[i], currjob);
69 		switch (docheck(lp, agent, jf, q.cfname[i], active, 0)) {
70 		case 0:
71 			break;
72 		case 1:
73 			doremove(ofd, lp, q.cfname[i]);
74 			break;
75 		case 2:
76 			if (lp->lp_type == PRN_LPR)
77 				dprintf(ofd, "%s: ", lpd_hostname);
78 			dprintf(ofd, "%s: Permission denied\n", q.cfname[i]);
79 			break;
80 		}
81 	}
82 
83 	lp_clearqueue(&q);
84 
85 	return killed;
86 }
87 
88 /*
89  * Check if a file must be removed.
90  *
91  * Return:
92  *	0: no
93  *	1: yes
94  *	2: yes but user has no right to do so
95  */
96 static int
97 docheck(struct lp_printer *lp, const char *agent, struct lp_jobfilter *jf,
98     const char *cfname, int current, int local)
99 {
100 	FILE *fp;
101 	ssize_t len;
102 	size_t linesz = 0;
103 	char *line = NULL, *person = NULL;
104 	int i, own = 0;
105 
106 	/* The "-all" agent means remove all jobs from the client host. */
107 	if (!strcmp(agent, "-all") && !strcmp(LP_JOBHOST(cfname), jf->hostfrom))
108 		return 1;
109 
110 	/*
111 	 * Consider the root user owns local files, and files sent from
112 	 * the same machine.
113 	 */
114 	if (!strcmp(agent, "root"))
115 		own = local || !strcmp(LP_JOBHOST(cfname), jf->hostfrom);
116 
117 	/* Check if the task person matches the agent. */
118 	fp = lp_fopen(lp, cfname);
119 	if (fp == NULL) {
120 		log_warn("cannot open %s", cfname);
121 		return 0;
122 	}
123 	while ((len = getline(&line, &linesz, fp)) != -1) {
124 		if (line[len-1] == '\n')
125 			line[len - 1] = '\0';
126 		if (line[0] == 'P') {
127 			person = line + 1;
128 			if (!strcmp(person, agent) &&
129 			    !strcmp(LP_JOBHOST(cfname), jf->hostfrom))
130 				own = 1;
131 			break;
132 		}
133 	}
134 	fclose(fp);
135 
136 	if (person == NULL) {
137 		free(line);
138 		return 0;
139 	}
140 
141 	/* Remove the current task if the request list is empty. */
142 	if (current && jf->nuser == 0 && jf->njob == 0)
143 		goto remove;
144 
145 	/* Check for matching jobnum. */
146 	for (i = 0; i < jf->njob; i++)
147 		if (jf->jobs[i] == LP_JOBNUM(cfname))
148 			goto remove;
149 
150 	/* Check if person is in user list. */
151 	for (i = 0; i < jf->nuser; i++)
152 		if (!strcmp(jf->users[i], person))
153 			goto remove;
154 
155 	free(line);
156 	return 0;
157 
158     remove:
159 	free(line);
160 	return own ? 1 : 2;
161 }
162 
163 static void
164 doremove(int ofd, struct lp_printer *lp, const char *cfname)
165 {
166 	FILE *fp;
167 	ssize_t len;
168 	size_t linesz = 0;
169 	char *line = NULL;
170 
171 	fp = lp_fopen(lp, cfname);
172 	if (fp == NULL) {
173 		log_warn("cannot open %s", cfname);
174 		return;
175 	}
176 
177 	if (lp->lp_type == PRN_LPR)
178 		dprintf(ofd, "%s: ", lpd_hostname);
179 
180 	/* First, remove the control file. */
181 	if (lp_unlink(lp, cfname) == -1) {
182 		log_warn("cannot unlink %s", cfname);
183 		dprintf(ofd, "cannot dequeue %s\n", cfname);
184 	}
185 	else {
186 		log_info("removed job %s", cfname);
187 		dprintf(ofd, "%s dequeued\n", cfname);
188 	}
189 
190 	/* Then unlink all data files. */
191 	while ((len = getline(&line, &linesz, fp)) != -1) {
192 		if (line[len-1] == '\n')
193 			line[len - 1] = '\0';
194 		if (line[0] != 'U')
195 			continue;
196 		if (strchr(line+1, '/') || strncmp(line+1, "df", 2))
197 			continue;
198 		if (lp->lp_type == PRN_LPR)
199 			dprintf(ofd, "%s: ", lpd_hostname);
200 		if (lp_unlink(lp, line + 1) == -1) {
201 			log_warn("cannot unlink %s", line + 1);
202 			dprintf(ofd, "cannot dequeue %s\n", line + 1);
203 		}
204 		else
205 			dprintf(ofd, "%s dequeued\n", line + 1);
206 	}
207 
208 	fclose(fp);
209 }
210