source: MondoRescue/branches/3.3/mindi-busybox/printutils/lpd.c@ 3791

Last change on this file since 3791 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 8.8 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * micro lpd
4 *
5 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
6 *
7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */
9
10/*
11 * A typical usage of BB lpd looks as follows:
12 * # tcpsvd -E 0 515 lpd [SPOOLDIR] [HELPER-PROG [ARGS...]]
13 *
14 * This starts TCP listener on port 515 (default for LP protocol).
15 * When a client connection is made (via lpr) lpd first changes its
16 * working directory to SPOOLDIR (current dir is the default).
17 *
18 * SPOOLDIR is the spool directory which contains printing queues
19 * and should have the following structure:
20 *
21 * SPOOLDIR/
22 * <queue1>
23 * ...
24 * <queueN>
25 *
26 * <queueX> can be of two types:
27 * A. a printer character device, an ordinary file or a link to such;
28 * B. a directory.
29 *
30 * In case A lpd just dumps the data it receives from client (lpr) to the
31 * end of queue file/device. This is non-spooling mode.
32 *
33 * In case B lpd enters spooling mode. It reliably saves client data along
34 * with control info in two unique files under the queue directory. These
35 * files are named dfAXXXHHHH and cfAXXXHHHH, where XXX is the job number
36 * and HHHH is the client hostname. Unless a printing helper application
37 * is specified lpd is done at this point.
38 *
39 * NB: file names are produced by peer! They actually may be anything at all.
40 * lpd only sanitizes them (by removing most non-alphanumerics).
41 *
42 * If HELPER-PROG (with optional arguments) is specified then lpd continues
43 * to process client data:
44 * 1. it reads and parses control file (cfA...). The parse process
45 * results in setting environment variables whose values were passed
46 * in control file; when parsing is complete, lpd deletes control file.
47 * 2. it spawns specified helper application. It is then
48 * the helper application who is responsible for both actual printing
49 * and deleting of processed data file.
50 *
51 * A good lpr passes control files which when parsed provides the following
52 * variables:
53 * $H = host which issues the job
54 * $P = user who prints
55 * $C = class of printing (what is printed on banner page)
56 * $J = the name of the job
57 * $L = print banner page
58 * $M = the user to whom a mail should be sent if a problem occurs
59 *
60 * We specifically filter out and NOT provide:
61 * $l = name of datafile ("dfAxxx") - file whose content are to be printed
62 *
63 * lpd provides $DATAFILE instead - the ACTUAL name
64 * of the datafile under which it was saved.
65 * $l would be not reliable (you would be at mercy of remote peer).
66 *
67 * Thus, a typical helper can be something like this:
68 * #!/bin/sh
69 * cat ./"$DATAFILE" >/dev/lp0
70 * mv -f ./"$DATAFILE" save/
71 */
72//config:config LPD
73//config: bool "lpd"
74//config: default y
75//config: help
76//config: lpd is a print spooling daemon.
77
78//applet:IF_LPD(APPLET(lpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
79
80//kbuild:lib-$(CONFIG_LPD) += lpd.o
81
82//usage:#define lpd_trivial_usage
83//usage: "SPOOLDIR [HELPER [ARGS]]"
84//usage:#define lpd_full_usage "\n\n"
85//usage: "SPOOLDIR must contain (symlinks to) device nodes or directories"
86//usage: "\nwith names matching print queue names. In the first case, jobs are"
87//usage: "\nsent directly to the device. Otherwise each job is stored in queue"
88//usage: "\ndirectory and HELPER program is called. Name of file to print"
89//usage: "\nis passed in $DATAFILE variable."
90//usage: "\nExample:"
91//usage: "\n tcpsvd -E 0 515 softlimit -m 999999 lpd /var/spool ./print"
92
93#include "libbb.h"
94
95// strip argument of bad chars
96static char *sane(char *str)
97{
98 char *s = str;
99 char *p = s;
100 while (*s) {
101 if (isalnum(*s) || '-' == *s || '_' == *s) {
102 *p++ = *s;
103 }
104 s++;
105 }
106 *p = '\0';
107 return str;
108}
109
110static char *xmalloc_read_stdin(void)
111{
112 // SECURITY:
113 size_t max = 4 * 1024; // more than enough for commands!
114 return xmalloc_reads(STDIN_FILENO, &max);
115}
116
117int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
118int lpd_main(int argc UNUSED_PARAM, char *argv[])
119{
120 int spooling = spooling; // for compiler
121 char *s, *queue;
122 char *filenames[2];
123
124 // goto spool directory
125 if (*++argv)
126 xchdir(*argv++);
127
128 // error messages of xfuncs will be sent over network
129 xdup2(STDOUT_FILENO, STDERR_FILENO);
130
131 // nullify ctrl/data filenames
132 memset(filenames, 0, sizeof(filenames));
133
134 // read command
135 s = queue = xmalloc_read_stdin();
136 // we understand only "receive job" command
137 if (2 != *queue) {
138 unsupported_cmd:
139 printf("Command %02x %s\n",
140 (unsigned char)s[0], "is not supported");
141 goto err_exit;
142 }
143
144 // parse command: "2 | QUEUE_NAME | '\n'"
145 queue++;
146 // protect against "/../" attacks
147 // *strchrnul(queue, '\n') = '\0'; - redundant, sane() will do
148 if (!*sane(queue))
149 return EXIT_FAILURE;
150
151 // queue is a directory -> chdir to it and enter spooling mode
152 spooling = chdir(queue) + 1; // 0: cannot chdir, 1: done
153 // we don't free(s), we might need "queue" var later
154
155 while (1) {
156 char *fname;
157 int fd;
158 // int is easier than ssize_t: can use xatoi_positive,
159 // and can correctly display error returns (-1)
160 int expected_len, real_len;
161
162 // signal OK
163 safe_write(STDOUT_FILENO, "", 1);
164
165 // get subcommand
166 // valid s must be of form: "SUBCMD | LEN | space | FNAME"
167 // N.B. we bail out on any error
168 s = xmalloc_read_stdin();
169 if (!s) { // (probably) EOF
170 char *p, *q, var[2];
171
172 // non-spooling mode or no spool helper specified
173 if (!spooling || !*argv)
174 return EXIT_SUCCESS; // the only non-error exit
175 // spooling mode but we didn't see both ctrlfile & datafile
176 if (spooling != 7)
177 goto err_exit; // reject job
178
179 // spooling mode and spool helper specified -> exec spool helper
180 // (we exit 127 if helper cannot be executed)
181 var[1] = '\0';
182 // read and delete ctrlfile
183 q = xmalloc_xopen_read_close(filenames[0], NULL);
184 unlink(filenames[0]);
185 // provide datafile name
186 // we can use leaky setenv since we are about to exec or exit
187 xsetenv("DATAFILE", filenames[1]);
188 // parse control file by "\n"
189 while ((p = strchr(q, '\n')) != NULL && isalpha(*q)) {
190 *p++ = '\0';
191 // q is a line of <SYM><VALUE>,
192 // we are setting environment string <SYM>=<VALUE>.
193 // Ignoring "l<datafile>", exporting others:
194 if (*q != 'l') {
195 var[0] = *q++;
196 xsetenv(var, q);
197 }
198 q = p; // next line
199 }
200 // helper should not talk over network.
201 // this call reopens stdio fds to "/dev/null"
202 // (no daemonization is done)
203 bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO | DAEMON_ONLY_SANITIZE, NULL);
204 BB_EXECVP_or_die(argv);
205 }
206
207 // validate input.
208 // we understand only "control file" or "data file" cmds
209 if (2 != s[0] && 3 != s[0])
210 goto unsupported_cmd;
211 if (spooling & (1 << (s[0]-1))) {
212 puts("Duplicated subcommand");
213 goto err_exit;
214 }
215 // get filename
216 chomp(s);
217 fname = strchr(s, ' ');
218 if (!fname) {
219// bad_fname:
220 puts("No or bad filename");
221 goto err_exit;
222 }
223 *fname++ = '\0';
224// // s[0]==2: ctrlfile, must start with 'c'
225// // s[0]==3: datafile, must start with 'd'
226// if (fname[0] != s[0] + ('c'-2))
227// goto bad_fname;
228 // get length
229 expected_len = bb_strtou(s + 1, NULL, 10);
230 if (errno || expected_len < 0) {
231 puts("Bad length");
232 goto err_exit;
233 }
234 if (2 == s[0] && expected_len > 16 * 1024) {
235 // SECURITY:
236 // ctrlfile can't be big (we want to read it back later!)
237 puts("File is too big");
238 goto err_exit;
239 }
240
241 // open the file
242 if (spooling) {
243 // spooling mode: dump both files
244 // job in flight has mode 0200 "only writable"
245 sane(fname);
246 fd = open3_or_warn(fname, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0200);
247 if (fd < 0)
248 goto err_exit;
249 filenames[s[0] - 2] = xstrdup(fname);
250 } else {
251 // non-spooling mode:
252 // 2: control file (ignoring), 3: data file
253 fd = -1;
254 if (3 == s[0])
255 fd = xopen(queue, O_RDWR | O_APPEND);
256 }
257
258 // signal OK
259 safe_write(STDOUT_FILENO, "", 1);
260
261 // copy the file
262 real_len = bb_copyfd_size(STDIN_FILENO, fd, expected_len);
263 if (real_len != expected_len) {
264 printf("Expected %d but got %d bytes\n",
265 expected_len, real_len);
266 goto err_exit;
267 }
268 // get EOF indicator, see whether it is NUL (ok)
269 // (and don't trash s[0]!)
270 if (safe_read(STDIN_FILENO, &s[1], 1) != 1 || s[1] != 0) {
271 // don't send error msg to peer - it obviously
272 // doesn't follow the protocol, so probably
273 // it can't understand us either
274 goto err_exit;
275 }
276
277 if (spooling) {
278 // chmod completely downloaded file as "readable+writable"
279 fchmod(fd, 0600);
280 // accumulate dump state
281 // N.B. after all files are dumped spooling should be 1+2+4==7
282 spooling |= (1 << (s[0]-1)); // bit 1: ctrlfile; bit 2: datafile
283 }
284
285 free(s);
286 close(fd); // NB: can do close(-1). Who cares?
287
288 // NB: don't do "signal OK" write here, it will be done
289 // at the top of the loop
290 } // while (1)
291
292 err_exit:
293 // don't keep corrupted files
294 if (spooling) {
295#define i spooling
296 for (i = 2; --i >= 0; )
297 if (filenames[i])
298 unlink(filenames[i]);
299 }
300 return EXIT_FAILURE;
301}
Note: See TracBrowser for help on using the repository browser.