1 /* $OpenBSD: lpd.c,v 1.3 2022/12/28 21:30:17 jmc 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 <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/un.h>
22 #include <sys/wait.h>
23
24 #include <errno.h>
25 #include <getopt.h>
26 #include <pwd.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <signal.h>
30 #include <string.h>
31 #include <syslog.h>
32 #include <unistd.h>
33
34 #include "lpd.h"
35
36 #include "log.h"
37 #include "proc.h"
38
39 struct lpd_conf *env;
40 struct imsgproc *p_control;
41 struct imsgproc *p_engine;
42 struct imsgproc *p_frontend;
43 struct imsgproc *p_priv;
44
45 static void priv_dispatch_control(struct imsgproc *, struct imsg *, void *);
46 static void priv_dispatch_engine(struct imsgproc *, struct imsg *, void *);
47 static void priv_dispatch_frontend(struct imsgproc *, struct imsg *, void *);
48 static void priv_dispatch_printer(struct imsgproc *, struct imsg *, void *);
49 static void priv_open_listener(struct listener *);
50 static void priv_send_config(void);
51 static void priv_sighandler(int, short, void *);
52 static void priv_shutdown(void);
53 static void priv_run_printer(const char *);
54
55 static char **saved_argv;
56 static int saved_argc;
57
58 static void
usage(void)59 usage(void)
60 {
61 extern char *__progname;
62
63 fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
64 __progname);
65 exit(1);
66 }
67
68 int
main(int argc,char ** argv)69 main(int argc, char **argv)
70 {
71 struct listener *l;
72 struct event evt_sigchld, evt_sigint, evt_sigterm, evt_sighup;
73 const char *conffile = LPD_CONFIG, *reexec = NULL;
74 int sp[2], ch, debug = 0, nflag = 0, verbose = 1;
75
76 saved_argv = argv;
77 saved_argc = argc;
78
79 log_init(1, LOG_LPR);
80 log_setverbose(0);
81
82 while ((ch = getopt(argc, argv, "D:df:nvX:")) != -1) {
83 switch (ch) {
84 case 'D':
85 if (cmdline_symset(optarg) < 0)
86 log_warnx("could not parse macro definition %s",
87 optarg);
88 break;
89 case 'd':
90 debug = 1;
91 break;
92 case 'f':
93 conffile = optarg;
94 break;
95 case 'n':
96 nflag = 1;
97 break;
98 case 'v':
99 verbose++;
100 break;
101 case 'X':
102 reexec = optarg;
103 break;
104 default:
105 usage();
106 }
107 }
108
109 argv += optind;
110 argc -= optind;
111
112 if (argc || *argv)
113 usage();
114
115 if (reexec) {
116 if (!strcmp(reexec, "control"))
117 control(debug, verbose);
118 if (!strcmp(reexec, "engine"))
119 engine(debug, verbose);
120 if (!strcmp(reexec, "frontend"))
121 frontend(debug, verbose);
122 if (!strncmp(reexec, "printer:", 8))
123 printer(debug, verbose, strchr(reexec, ':') + 1);
124 fatalx("unknown process %s", reexec);
125 }
126
127 /* Parse config file. */
128 env = parse_config(conffile, verbose);
129 if (env == NULL)
130 exit(1);
131
132 if (nflag) {
133 fprintf(stderr, "configuration OK\n");
134 exit(0);
135 }
136
137 /* Check for root privileges. */
138 if (geteuid())
139 fatalx("need root privileges");
140
141 /* Check for assigned daemon user. */
142 if (getpwnam(LPD_USER) == NULL)
143 fatalx("unknown user %s", LPD_USER);
144
145 log_init(debug, LOG_LPR);
146 log_setverbose(verbose);
147 log_procinit("priv");
148 setproctitle("priv");
149
150 if (!debug)
151 if (daemon(1, 0) == -1)
152 fatal("daemon");
153
154 log_info("startup");
155
156 TAILQ_FOREACH(l, &env->listeners, entry)
157 priv_open_listener(l);
158
159 event_init();
160
161 signal_set(&evt_sigint, SIGINT, priv_sighandler, NULL);
162 signal_add(&evt_sigint, NULL);
163 signal_set(&evt_sigterm, SIGTERM, priv_sighandler, NULL);
164 signal_add(&evt_sigterm, NULL);
165 signal_set(&evt_sigchld, SIGCHLD, priv_sighandler, NULL);
166 signal_add(&evt_sigchld, NULL);
167 signal_set(&evt_sighup, SIGHUP, priv_sighandler, NULL);
168 signal_add(&evt_sighup, NULL);
169 signal(SIGPIPE, SIG_IGN);
170
171 /* Fork and exec unprivileged processes. */
172 argv = calloc(saved_argc + 3, sizeof(*argv));
173 if (argv == NULL)
174 fatal("calloc");
175 for (argc = 0; argc < saved_argc; argc++)
176 argv[argc] = saved_argv[argc];
177 argv[argc++] = "-X";
178 argv[argc++] = "";
179 argv[argc++] = NULL;
180
181 argv[argc - 2] = "control";
182 p_control = proc_exec(PROC_CONTROL, argv);
183 if (p_control == NULL)
184 fatalx("cannot exec control process");
185 proc_setcallback(p_control, priv_dispatch_control, NULL);
186 proc_enable(p_control);
187
188 argv[argc - 2] = "engine";
189 p_engine = proc_exec(PROC_ENGINE, argv);
190 if (p_engine == NULL)
191 fatalx("cannot exec engine process");
192 proc_setcallback(p_engine, priv_dispatch_engine, NULL);
193 proc_enable(p_engine);
194
195 argv[argc - 2] = "frontend";
196 p_frontend = proc_exec(PROC_FRONTEND, argv);
197 if (p_frontend == NULL)
198 fatalx("cannot exec frontend process");
199 proc_setcallback(p_frontend, priv_dispatch_frontend, NULL);
200 proc_enable(p_frontend);
201
202 /* Connect processes. */
203 if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, PF_UNSPEC, sp) == -1)
204 fatal("socketpair");
205 m_compose(p_engine, IMSG_SOCK_FRONTEND, 0, 0, sp[1], NULL, 0);
206 m_compose(p_frontend, IMSG_SOCK_ENGINE, 0, 0, sp[0], NULL, 0);
207
208 priv_send_config();
209
210 if (pledge("stdio sendfd proc exec", NULL) == -1)
211 fatal("pledge");
212
213 event_dispatch();
214
215 priv_shutdown();
216
217 return (0);
218 }
219
220 static void
priv_sighandler(int sig,short ev,void * arg)221 priv_sighandler(int sig, short ev, void *arg)
222 {
223 pid_t pid;
224 int status;
225
226 switch (sig) {
227 case SIGTERM:
228 case SIGINT:
229 event_loopbreak();
230 break;
231 case SIGCHLD:
232 do {
233 pid = waitpid(-1, &status, WNOHANG);
234 if (pid <= 0)
235 continue;
236 if (WIFSIGNALED(status))
237 log_warnx("process %d terminated by signal %d",
238 (int)pid, WTERMSIG(status));
239 else if (WIFEXITED(status) && WEXITSTATUS(status))
240 log_warnx("process %d exited with status %d",
241 (int)pid, WEXITSTATUS(status));
242 else if (WIFEXITED(status))
243 log_debug("process %d exited normally",
244 (int)pid);
245 else
246 /* WIFSTOPPED or WIFCONTINUED */
247 continue;
248 } while (pid > 0 || (pid == -1 && errno == EINTR));
249 break;
250 default:
251 fatalx("signal %d", sig);
252 }
253 }
254
255 static void
priv_shutdown(void)256 priv_shutdown(void)
257 {
258 pid_t pid;
259
260 proc_free(p_control);
261 proc_free(p_engine);
262 proc_free(p_frontend);
263
264 do {
265 pid = waitpid(WAIT_MYPGRP, NULL, 0);
266 } while (pid != -1 || (pid == -1 && errno == EINTR));
267
268 log_info("exiting");
269
270 exit(0);
271 }
272
273 static void
priv_open_listener(struct listener * l)274 priv_open_listener(struct listener *l)
275 {
276 struct sockaddr_un *su;
277 struct sockaddr *sa;
278 const char *path;
279 mode_t old_umask;
280 int opt, sock, r;
281
282 sa = (struct sockaddr *)&l->ss;
283
284 sock = socket(sa->sa_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
285 if (sock == -1) {
286 if (errno == EAFNOSUPPORT) {
287 log_warn("%s: socket", __func__);
288 return;
289 }
290 fatal("%s: socket", __func__);
291 }
292
293 switch (sa->sa_family) {
294 case AF_LOCAL:
295 su = (struct sockaddr_un *)sa;
296 path = su->sun_path;
297 if (connect(sock, sa, sa->sa_len) == 0)
298 fatalx("%s already in use", path);
299
300 if (unlink(path) == -1)
301 if (errno != ENOENT)
302 fatal("unlink: %s", path);
303
304 old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
305 r = bind(sock, sa, sizeof(*su));
306 (void)umask(old_umask);
307
308 if (r == -1)
309 fatal("bind: %s", path);
310 break;
311
312 case AF_INET:
313 case AF_INET6:
314 opt = 1;
315 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt,
316 sizeof(opt)) == -1)
317 fatal("setsockopt: %s", log_fmt_sockaddr(sa));
318
319 if (bind(sock, sa, sa->sa_len) == -1)
320 fatal("bind: %s", log_fmt_sockaddr(sa));
321 break;
322
323 default:
324 fatalx("bad address family %d", sa->sa_family);
325 }
326
327 l->sock = sock;
328 }
329
330 static void
priv_send_config(void)331 priv_send_config(void)
332 {
333 struct listener *l;
334
335 m_compose(p_control, IMSG_CONF_START, 0, 0, -1, NULL, 0);
336 m_compose(p_control, IMSG_CONF_END, 0, 0, -1, NULL, 0);
337
338 m_compose(p_engine, IMSG_CONF_START, 0, 0, -1, NULL, 0);
339 m_compose(p_engine, IMSG_CONF_END, 0, 0, -1, NULL, 0);
340
341 m_compose(p_frontend, IMSG_CONF_START, 0, 0, -1, NULL, 0);
342 TAILQ_FOREACH(l, &env->listeners, entry) {
343 m_create(p_frontend, IMSG_CONF_LISTENER, 0, 0, l->sock);
344 m_add_int(p_frontend, l->proto);
345 m_add_sockaddr(p_frontend, (struct sockaddr *)(&l->ss));
346 m_close(p_frontend);
347 }
348 m_compose(p_frontend, IMSG_CONF_END, 0, 0, -1, NULL, 0);
349 }
350
351 static void
priv_dispatch_control(struct imsgproc * proc,struct imsg * imsg,void * arg)352 priv_dispatch_control(struct imsgproc *proc, struct imsg *imsg, void *arg)
353 {
354 if (imsg == NULL)
355 fatalx("%s: imsg connection lost", __func__);
356
357 if (log_getverbose() > LOGLEVEL_IMSG)
358 log_imsg(proc, imsg);
359
360 switch (imsg->hdr.type) {
361 default:
362 fatalx("%s: unexpected imsg %s", __func__,
363 log_fmt_imsgtype(imsg->hdr.type));
364 }
365 }
366
367 static void
priv_dispatch_engine(struct imsgproc * proc,struct imsg * imsg,void * arg)368 priv_dispatch_engine(struct imsgproc *proc, struct imsg *imsg, void *arg)
369 {
370 const char *prn;
371
372 if (imsg == NULL)
373 fatalx("%s: imsg connection lost", __func__);
374
375 if (log_getverbose() > LOGLEVEL_IMSG)
376 log_imsg(proc, imsg);
377
378 switch (imsg->hdr.type) {
379 case IMSG_LPR_PRINTJOB:
380 m_get_string(proc, &prn);
381 m_end(proc);
382 priv_run_printer(prn);
383 break;
384 default:
385 fatalx("%s: unexpected imsg %s", __func__,
386 log_fmt_imsgtype(imsg->hdr.type));
387 }
388 }
389
390 static void
priv_dispatch_frontend(struct imsgproc * proc,struct imsg * imsg,void * arg)391 priv_dispatch_frontend(struct imsgproc *proc, struct imsg *imsg, void *arg)
392 {
393 if (imsg == NULL)
394 fatalx("%s: imsg connection lost", __func__);
395
396 if (log_getverbose() > LOGLEVEL_IMSG)
397 log_imsg(proc, imsg);
398
399 switch (imsg->hdr.type) {
400 default:
401 fatalx("%s: unexpected imsg %s", __func__,
402 log_fmt_imsgtype(imsg->hdr.type));
403 }
404 }
405
406 static void
priv_dispatch_printer(struct imsgproc * proc,struct imsg * imsg,void * arg)407 priv_dispatch_printer(struct imsgproc *proc, struct imsg *imsg, void *arg)
408 {
409 if (imsg == NULL) {
410 log_debug("printer process ended, pid=%d, printer=%s",
411 proc_getpid(proc), proc_gettitle(proc));
412 proc_free(proc);
413 return;
414 }
415
416 if (log_getverbose() > LOGLEVEL_IMSG)
417 log_imsg(proc, imsg);
418
419 switch (imsg->hdr.type) {
420 default:
421 fatalx("%s: unexpected imsg %s", __func__,
422 log_fmt_imsgtype(imsg->hdr.type));
423 }
424 }
425
426 static void
priv_run_printer(const char * prn)427 priv_run_printer(const char *prn)
428 {
429 struct imsgproc *p;
430 char **argv, *buf;
431 int argc;
432
433 if (asprintf(&buf, "printer:%s", prn) == -1) {
434 log_warn("%s: asprintf", __func__);
435 return;
436 }
437
438 argv = calloc(saved_argc + 4, sizeof(*argv));
439 if (argv == NULL) {
440 log_warn("%s: calloc", __func__);
441 free(buf);
442 return;
443 }
444 for (argc = 0; argc < saved_argc; argc++)
445 argv[argc] = saved_argv[argc];
446 argv[argc++] = "-X";
447 argv[argc++] = buf;
448 argv[argc++] = NULL;
449
450 p = proc_exec(PROC_PRINTER, argv);
451 if (p == NULL)
452 log_warnx("%s: cannot exec printer process", __func__);
453 else {
454 proc_settitle(p, prn);
455 proc_setcallback(p, priv_dispatch_printer, p);
456 proc_enable(p);
457 }
458
459 free(argv);
460 free(buf);
461 }
462