xref: /openbsd-src/usr.sbin/lpd/lpd.c (revision 3a50f0a93a2072911d0ba6ababa815fb04bf9a71)
1*3a50f0a9Sjmc /*	$OpenBSD: lpd.c,v 1.3 2022/12/28 21:30:17 jmc Exp $	*/
23b188dabSeric 
33b188dabSeric /*
43b188dabSeric  * Copyright (c) 2017 Eric Faurot <eric@openbsd.org>
53b188dabSeric  *
63b188dabSeric  * Permission to use, copy, modify, and distribute this software for any
73b188dabSeric  * purpose with or without fee is hereby granted, provided that the above
83b188dabSeric  * copyright notice and this permission notice appear in all copies.
93b188dabSeric  *
103b188dabSeric  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113b188dabSeric  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123b188dabSeric  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133b188dabSeric  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143b188dabSeric  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153b188dabSeric  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163b188dabSeric  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173b188dabSeric  */
183b188dabSeric 
193b188dabSeric #include <sys/types.h>
203b188dabSeric #include <sys/stat.h>
213b188dabSeric #include <sys/un.h>
223b188dabSeric #include <sys/wait.h>
233b188dabSeric 
243b188dabSeric #include <errno.h>
253b188dabSeric #include <getopt.h>
263b188dabSeric #include <pwd.h>
273b188dabSeric #include <stdlib.h>
283b188dabSeric #include <stdio.h>
293b188dabSeric #include <signal.h>
303b188dabSeric #include <string.h>
313b188dabSeric #include <syslog.h>
323b188dabSeric #include <unistd.h>
333b188dabSeric 
343b188dabSeric #include "lpd.h"
353b188dabSeric 
363b188dabSeric #include "log.h"
373b188dabSeric #include "proc.h"
383b188dabSeric 
393b188dabSeric struct lpd_conf *env;
403b188dabSeric struct imsgproc *p_control;
413b188dabSeric struct imsgproc *p_engine;
423b188dabSeric struct imsgproc *p_frontend;
433b188dabSeric struct imsgproc *p_priv;
443b188dabSeric 
453b188dabSeric static void priv_dispatch_control(struct imsgproc *, struct imsg *, void *);
463b188dabSeric static void priv_dispatch_engine(struct imsgproc *, struct imsg *, void *);
473b188dabSeric static void priv_dispatch_frontend(struct imsgproc *, struct imsg *, void *);
483b188dabSeric static void priv_dispatch_printer(struct imsgproc *, struct imsg *, void *);
493b188dabSeric static void priv_open_listener(struct listener *);
503b188dabSeric static void priv_send_config(void);
513b188dabSeric static void priv_sighandler(int, short, void *);
523b188dabSeric static void priv_shutdown(void);
533b188dabSeric static void priv_run_printer(const char *);
543b188dabSeric 
553b188dabSeric static char **saved_argv;
563b188dabSeric static int saved_argc;
573b188dabSeric 
583b188dabSeric static void
usage(void)593b188dabSeric usage(void)
603b188dabSeric {
613b188dabSeric 	extern char *__progname;
623b188dabSeric 
633b188dabSeric 	fprintf(stderr, "usage: %s [-dnv] [-D macro=value]  [-f file]\n",
643b188dabSeric 	    __progname);
653b188dabSeric 	exit(1);
663b188dabSeric }
673b188dabSeric 
683b188dabSeric int
main(int argc,char ** argv)693b188dabSeric main(int argc, char **argv)
703b188dabSeric {
713b188dabSeric 	struct listener *l;
723b188dabSeric 	struct event evt_sigchld, evt_sigint, evt_sigterm, evt_sighup;
733b188dabSeric 	const char *conffile = LPD_CONFIG, *reexec = NULL;
743b188dabSeric 	int sp[2], ch, debug = 0, nflag = 0, verbose = 1;
753b188dabSeric 
763b188dabSeric 	saved_argv = argv;
773b188dabSeric 	saved_argc = argc;
783b188dabSeric 
793b188dabSeric 	log_init(1, LOG_LPR);
803b188dabSeric 	log_setverbose(0);
813b188dabSeric 
823b188dabSeric 	while ((ch = getopt(argc, argv, "D:df:nvX:")) != -1) {
833b188dabSeric 		switch (ch) {
843b188dabSeric 		case 'D':
853b188dabSeric 			if (cmdline_symset(optarg) < 0)
863b188dabSeric 				log_warnx("could not parse macro definition %s",
873b188dabSeric 				    optarg);
883b188dabSeric 			break;
893b188dabSeric 		case 'd':
903b188dabSeric 			debug = 1;
913b188dabSeric 			break;
923b188dabSeric 		case 'f':
933b188dabSeric 			conffile = optarg;
943b188dabSeric 			break;
953b188dabSeric 		case 'n':
963b188dabSeric 			nflag = 1;
973b188dabSeric 			break;
983b188dabSeric 		case 'v':
993b188dabSeric 			verbose++;
1003b188dabSeric 			break;
1013b188dabSeric 		case 'X':
1023b188dabSeric 			reexec = optarg;
1033b188dabSeric 			break;
1043b188dabSeric 		default:
1053b188dabSeric 			usage();
1063b188dabSeric 		}
1073b188dabSeric 	}
1083b188dabSeric 
1093b188dabSeric 	argv += optind;
1103b188dabSeric 	argc -= optind;
1113b188dabSeric 
1123b188dabSeric 	if (argc || *argv)
1133b188dabSeric 		usage();
1143b188dabSeric 
1153b188dabSeric 	if (reexec) {
1163b188dabSeric 		if (!strcmp(reexec, "control"))
1173b188dabSeric 			control(debug, verbose);
1183b188dabSeric 		if (!strcmp(reexec, "engine"))
1193b188dabSeric 			engine(debug, verbose);
1203b188dabSeric 		if (!strcmp(reexec, "frontend"))
1213b188dabSeric 			frontend(debug, verbose);
1223b188dabSeric 		if (!strncmp(reexec, "printer:", 8))
1233b188dabSeric 			printer(debug, verbose, strchr(reexec, ':') + 1);
1243b188dabSeric 		fatalx("unknown process %s", reexec);
1253b188dabSeric 	}
1263b188dabSeric 
1273b188dabSeric 	/* Parse config file. */
1283b188dabSeric 	env = parse_config(conffile, verbose);
1293b188dabSeric 	if (env == NULL)
1303b188dabSeric 		exit(1);
1313b188dabSeric 
1323b188dabSeric 	if (nflag) {
1333b188dabSeric 		fprintf(stderr, "configuration OK\n");
1343b188dabSeric 		exit(0);
1353b188dabSeric 	}
1363b188dabSeric 
1373b188dabSeric 	/* Check for root privileges. */
1383b188dabSeric 	if (geteuid())
1393b188dabSeric 		fatalx("need root privileges");
1403b188dabSeric 
1413b188dabSeric 	/* Check for assigned daemon user. */
1423b188dabSeric 	if (getpwnam(LPD_USER) == NULL)
1433b188dabSeric 		fatalx("unknown user %s", LPD_USER);
1443b188dabSeric 
1453b188dabSeric 	log_init(debug, LOG_LPR);
1463b188dabSeric 	log_setverbose(verbose);
1473b188dabSeric 	log_procinit("priv");
1483b188dabSeric 	setproctitle("priv");
1493b188dabSeric 
1503b188dabSeric 	if (!debug)
1513b188dabSeric 		if (daemon(1, 0) == -1)
1523b188dabSeric 			fatal("daemon");
1533b188dabSeric 
1543b188dabSeric         log_info("startup");
1553b188dabSeric 
1563b188dabSeric 	TAILQ_FOREACH(l, &env->listeners, entry)
1573b188dabSeric 		priv_open_listener(l);
1583b188dabSeric 
1593b188dabSeric 	event_init();
1603b188dabSeric 
1613b188dabSeric 	signal_set(&evt_sigint, SIGINT, priv_sighandler, NULL);
1623b188dabSeric 	signal_add(&evt_sigint, NULL);
1633b188dabSeric 	signal_set(&evt_sigterm, SIGTERM, priv_sighandler, NULL);
1643b188dabSeric 	signal_add(&evt_sigterm, NULL);
1653b188dabSeric 	signal_set(&evt_sigchld, SIGCHLD, priv_sighandler, NULL);
1663b188dabSeric 	signal_add(&evt_sigchld, NULL);
1673b188dabSeric 	signal_set(&evt_sighup, SIGHUP, priv_sighandler, NULL);
1683b188dabSeric 	signal_add(&evt_sighup, NULL);
1693b188dabSeric 	signal(SIGPIPE, SIG_IGN);
1703b188dabSeric 
171*3a50f0a9Sjmc 	/* Fork and exec unprivileged processes. */
1723b188dabSeric 	argv = calloc(saved_argc + 3, sizeof(*argv));
1733b188dabSeric 	if (argv == NULL)
1743b188dabSeric 		fatal("calloc");
1753b188dabSeric 	for (argc = 0; argc < saved_argc; argc++)
1763b188dabSeric 		argv[argc] = saved_argv[argc];
1773b188dabSeric 	argv[argc++] = "-X";
1783b188dabSeric 	argv[argc++] = "";
1793b188dabSeric 	argv[argc++] = NULL;
1803b188dabSeric 
1813b188dabSeric 	argv[argc - 2] = "control";
1823b188dabSeric 	p_control = proc_exec(PROC_CONTROL, argv);
1833b188dabSeric 	if (p_control == NULL)
1843b188dabSeric 		fatalx("cannot exec control process");
1853b188dabSeric 	proc_setcallback(p_control, priv_dispatch_control, NULL);
1863b188dabSeric 	proc_enable(p_control);
1873b188dabSeric 
1883b188dabSeric 	argv[argc - 2] = "engine";
1893b188dabSeric 	p_engine = proc_exec(PROC_ENGINE, argv);
1903b188dabSeric 	if (p_engine == NULL)
1913b188dabSeric 		fatalx("cannot exec engine process");
1923b188dabSeric 	proc_setcallback(p_engine, priv_dispatch_engine, NULL);
1933b188dabSeric 	proc_enable(p_engine);
1943b188dabSeric 
1953b188dabSeric 	argv[argc - 2] = "frontend";
1963b188dabSeric 	p_frontend = proc_exec(PROC_FRONTEND, argv);
1973b188dabSeric 	if (p_frontend == NULL)
1983b188dabSeric 		fatalx("cannot exec frontend process");
1993b188dabSeric 	proc_setcallback(p_frontend, priv_dispatch_frontend, NULL);
2003b188dabSeric 	proc_enable(p_frontend);
2013b188dabSeric 
2023b188dabSeric 	/* Connect processes. */
2033b188dabSeric 	if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, PF_UNSPEC, sp) == -1)
2043b188dabSeric 		fatal("socketpair");
2053b188dabSeric 	m_compose(p_engine, IMSG_SOCK_FRONTEND, 0, 0, sp[1], NULL, 0);
2063b188dabSeric 	m_compose(p_frontend, IMSG_SOCK_ENGINE, 0, 0, sp[0], NULL, 0);
2073b188dabSeric 
2083b188dabSeric 	priv_send_config();
2093b188dabSeric 
2103b188dabSeric 	if (pledge("stdio sendfd proc exec", NULL) == -1)
2113b188dabSeric 		fatal("pledge");
2123b188dabSeric 
2133b188dabSeric 	event_dispatch();
2143b188dabSeric 
2153b188dabSeric 	priv_shutdown();
2163b188dabSeric 
2173b188dabSeric 	return (0);
2183b188dabSeric }
2193b188dabSeric 
2203b188dabSeric static void
priv_sighandler(int sig,short ev,void * arg)2213b188dabSeric priv_sighandler(int sig, short ev, void *arg)
2223b188dabSeric {
2233b188dabSeric 	pid_t pid;
2243b188dabSeric 	int status;
2253b188dabSeric 
2263b188dabSeric 	switch (sig) {
2273b188dabSeric 	case SIGTERM:
2283b188dabSeric 	case SIGINT:
2293b188dabSeric 		event_loopbreak();
2303b188dabSeric 		break;
2313b188dabSeric 	case SIGCHLD:
2323b188dabSeric 		do {
2333b188dabSeric 			pid = waitpid(-1, &status, WNOHANG);
2343b188dabSeric 			if (pid <= 0)
2353b188dabSeric 				continue;
2363b188dabSeric 			if (WIFSIGNALED(status))
2373b188dabSeric 				log_warnx("process %d terminated by signal %d",
2383b188dabSeric 				    (int)pid, WTERMSIG(status));
2393b188dabSeric 			else if (WIFEXITED(status) && WEXITSTATUS(status))
2403b188dabSeric 				log_warnx("process %d exited with status %d",
2413b188dabSeric 				    (int)pid, WEXITSTATUS(status));
2423b188dabSeric 			else if (WIFEXITED(status))
2433b188dabSeric 				log_debug("process %d exited normally",
2443b188dabSeric 				    (int)pid);
2453b188dabSeric 			else
2463b188dabSeric 				/* WIFSTOPPED or WIFCONTINUED */
2473b188dabSeric 				continue;
2483b188dabSeric 		} while (pid > 0 || (pid == -1 && errno == EINTR));
2493b188dabSeric 		break;
2503b188dabSeric 	default:
2513b188dabSeric 		fatalx("signal %d", sig);
2523b188dabSeric 	}
2533b188dabSeric }
2543b188dabSeric 
2553b188dabSeric static void
priv_shutdown(void)2563b188dabSeric priv_shutdown(void)
2573b188dabSeric {
2583b188dabSeric 	pid_t pid;
2593b188dabSeric 
2603b188dabSeric 	proc_free(p_control);
2613b188dabSeric 	proc_free(p_engine);
2623b188dabSeric 	proc_free(p_frontend);
2633b188dabSeric 
2643b188dabSeric 	do {
2653b188dabSeric 		pid = waitpid(WAIT_MYPGRP, NULL, 0);
2663b188dabSeric 	} while (pid != -1 || (pid == -1 && errno == EINTR));
2673b188dabSeric 
2683b188dabSeric 	log_info("exiting");
2693b188dabSeric 
2703b188dabSeric 	exit(0);
2713b188dabSeric }
2723b188dabSeric 
2733b188dabSeric static void
priv_open_listener(struct listener * l)2743b188dabSeric priv_open_listener(struct listener *l)
2753b188dabSeric {
2763b188dabSeric 	struct sockaddr_un *su;
2773b188dabSeric 	struct sockaddr *sa;
2783b188dabSeric 	const char *path;
2793b188dabSeric 	mode_t old_umask;
2803b188dabSeric 	int opt, sock, r;
2813b188dabSeric 
2823b188dabSeric 	sa = (struct sockaddr *)&l->ss;
2833b188dabSeric 
2843b188dabSeric 	sock = socket(sa->sa_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
2853b188dabSeric 	if (sock == -1) {
2863b188dabSeric 		if (errno == EAFNOSUPPORT) {
2873b188dabSeric 			log_warn("%s: socket", __func__);
2883b188dabSeric 			return;
2893b188dabSeric 		}
2903b188dabSeric 		fatal("%s: socket", __func__);
2913b188dabSeric 	}
2923b188dabSeric 
2933b188dabSeric 	switch (sa->sa_family) {
2943b188dabSeric 	case AF_LOCAL:
2953b188dabSeric 		su = (struct sockaddr_un *)sa;
2963b188dabSeric 		path = su->sun_path;
2973b188dabSeric 		if (connect(sock, sa, sa->sa_len) == 0)
2983b188dabSeric 			fatalx("%s already in use", path);
2993b188dabSeric 
3003b188dabSeric 		if (unlink(path) == -1)
3013b188dabSeric 			if (errno != ENOENT)
3023b188dabSeric 				fatal("unlink: %s", path);
3033b188dabSeric 
3043b188dabSeric 		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
3053b188dabSeric 		r = bind(sock, sa, sizeof(*su));
3063b188dabSeric 		(void)umask(old_umask);
3073b188dabSeric 
3083b188dabSeric 		if (r == -1)
3093b188dabSeric 			fatal("bind: %s", path);
3103b188dabSeric 		break;
3113b188dabSeric 
3123b188dabSeric 	case AF_INET:
3133b188dabSeric 	case AF_INET6:
3143b188dabSeric 		opt = 1;
3153b188dabSeric 		if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt,
316df69c215Sderaadt 		    sizeof(opt)) == -1)
3173b188dabSeric 			fatal("setsockopt: %s", log_fmt_sockaddr(sa));
3183b188dabSeric 
3193b188dabSeric 		if (bind(sock, sa, sa->sa_len) == -1)
3203b188dabSeric 			fatal("bind: %s", log_fmt_sockaddr(sa));
3213b188dabSeric 		break;
3223b188dabSeric 
3233b188dabSeric 	default:
3243b188dabSeric 		fatalx("bad address family %d", sa->sa_family);
3253b188dabSeric 	}
3263b188dabSeric 
3273b188dabSeric 	l->sock = sock;
3283b188dabSeric }
3293b188dabSeric 
3303b188dabSeric static void
priv_send_config(void)3313b188dabSeric priv_send_config(void)
3323b188dabSeric {
3333b188dabSeric 	struct listener *l;
3343b188dabSeric 
3353b188dabSeric 	m_compose(p_control, IMSG_CONF_START, 0, 0, -1, NULL, 0);
3363b188dabSeric 	m_compose(p_control, IMSG_CONF_END, 0, 0, -1, NULL, 0);
3373b188dabSeric 
3383b188dabSeric 	m_compose(p_engine, IMSG_CONF_START, 0, 0, -1, NULL, 0);
3393b188dabSeric 	m_compose(p_engine, IMSG_CONF_END, 0, 0, -1, NULL, 0);
3403b188dabSeric 
3413b188dabSeric 	m_compose(p_frontend, IMSG_CONF_START, 0, 0, -1, NULL, 0);
3423b188dabSeric 	TAILQ_FOREACH(l, &env->listeners, entry) {
3433b188dabSeric 		m_create(p_frontend, IMSG_CONF_LISTENER, 0, 0, l->sock);
3443b188dabSeric 		m_add_int(p_frontend, l->proto);
3453b188dabSeric 		m_add_sockaddr(p_frontend, (struct sockaddr *)(&l->ss));
3463b188dabSeric 		m_close(p_frontend);
3473b188dabSeric 	}
3483b188dabSeric 	m_compose(p_frontend, IMSG_CONF_END, 0, 0, -1, NULL, 0);
3493b188dabSeric }
3503b188dabSeric 
3513b188dabSeric static void
priv_dispatch_control(struct imsgproc * proc,struct imsg * imsg,void * arg)3523b188dabSeric priv_dispatch_control(struct imsgproc *proc, struct imsg *imsg, void *arg)
3533b188dabSeric {
3543b188dabSeric 	if (imsg == NULL)
3553b188dabSeric 		fatalx("%s: imsg connection lost", __func__);
3563b188dabSeric 
3573b188dabSeric 	if (log_getverbose() > LOGLEVEL_IMSG)
3583b188dabSeric 		log_imsg(proc, imsg);
3593b188dabSeric 
3603b188dabSeric 	switch (imsg->hdr.type) {
3613b188dabSeric 	default:
3623b188dabSeric 		fatalx("%s: unexpected imsg %s", __func__,
3633b188dabSeric 		    log_fmt_imsgtype(imsg->hdr.type));
3643b188dabSeric 	}
3653b188dabSeric }
3663b188dabSeric 
3673b188dabSeric static void
priv_dispatch_engine(struct imsgproc * proc,struct imsg * imsg,void * arg)3683b188dabSeric priv_dispatch_engine(struct imsgproc *proc, struct imsg *imsg, void *arg)
3693b188dabSeric {
3703b188dabSeric 	const char *prn;
3713b188dabSeric 
3723b188dabSeric 	if (imsg == NULL)
3733b188dabSeric 		fatalx("%s: imsg connection lost", __func__);
3743b188dabSeric 
3753b188dabSeric 	if (log_getverbose() > LOGLEVEL_IMSG)
3763b188dabSeric 		log_imsg(proc, imsg);
3773b188dabSeric 
3783b188dabSeric 	switch (imsg->hdr.type) {
3793b188dabSeric 	case IMSG_LPR_PRINTJOB:
3803b188dabSeric 		m_get_string(proc, &prn);
3813b188dabSeric 		m_end(proc);
3823b188dabSeric 		priv_run_printer(prn);
3833b188dabSeric 		break;
3843b188dabSeric 	default:
3853b188dabSeric 		fatalx("%s: unexpected imsg %s", __func__,
3863b188dabSeric 		    log_fmt_imsgtype(imsg->hdr.type));
3873b188dabSeric 	}
3883b188dabSeric }
3893b188dabSeric 
3903b188dabSeric static void
priv_dispatch_frontend(struct imsgproc * proc,struct imsg * imsg,void * arg)3913b188dabSeric priv_dispatch_frontend(struct imsgproc *proc, struct imsg *imsg, void *arg)
3923b188dabSeric {
3933b188dabSeric 	if (imsg == NULL)
3943b188dabSeric 		fatalx("%s: imsg connection lost", __func__);
3953b188dabSeric 
3963b188dabSeric 	if (log_getverbose() > LOGLEVEL_IMSG)
3973b188dabSeric 		log_imsg(proc, imsg);
3983b188dabSeric 
3993b188dabSeric 	switch (imsg->hdr.type) {
4003b188dabSeric 	default:
4013b188dabSeric 		fatalx("%s: unexpected imsg %s", __func__,
4023b188dabSeric 		    log_fmt_imsgtype(imsg->hdr.type));
4033b188dabSeric 	}
4043b188dabSeric }
4053b188dabSeric 
4063b188dabSeric static void
priv_dispatch_printer(struct imsgproc * proc,struct imsg * imsg,void * arg)4073b188dabSeric priv_dispatch_printer(struct imsgproc *proc, struct imsg *imsg, void *arg)
4083b188dabSeric {
4093b188dabSeric 	if (imsg == NULL) {
4103b188dabSeric 		log_debug("printer process ended, pid=%d, printer=%s",
4113b188dabSeric 		     proc_getpid(proc), proc_gettitle(proc));
4123b188dabSeric 		proc_free(proc);
4133b188dabSeric 		return;
4143b188dabSeric 	}
4153b188dabSeric 
4163b188dabSeric 	if (log_getverbose() > LOGLEVEL_IMSG)
4173b188dabSeric 		log_imsg(proc, imsg);
4183b188dabSeric 
4193b188dabSeric 	switch (imsg->hdr.type) {
4203b188dabSeric 	default:
4213b188dabSeric 		fatalx("%s: unexpected imsg %s", __func__,
4223b188dabSeric 		    log_fmt_imsgtype(imsg->hdr.type));
4233b188dabSeric 	}
4243b188dabSeric }
4253b188dabSeric 
4263b188dabSeric static void
priv_run_printer(const char * prn)4273b188dabSeric priv_run_printer(const char *prn)
4283b188dabSeric {
4293b188dabSeric 	struct imsgproc *p;
4303b188dabSeric 	char **argv, *buf;
4313b188dabSeric 	int argc;
4323b188dabSeric 
4333b188dabSeric 	if (asprintf(&buf, "printer:%s", prn) == -1) {
4343b188dabSeric 		log_warn("%s: asprintf", __func__);
4353b188dabSeric 		return;
4363b188dabSeric 	}
4373b188dabSeric 
4383b188dabSeric 	argv = calloc(saved_argc + 4, sizeof(*argv));
4393b188dabSeric 	if (argv == NULL) {
4403b188dabSeric 		log_warn("%s: calloc", __func__);
4413b188dabSeric 		free(buf);
4423b188dabSeric 		return;
4433b188dabSeric 	}
4443b188dabSeric 	for (argc = 0; argc < saved_argc; argc++)
4453b188dabSeric 		argv[argc] = saved_argv[argc];
4463b188dabSeric 	argv[argc++] = "-X";
4473b188dabSeric 	argv[argc++] = buf;
4483b188dabSeric 	argv[argc++] = NULL;
4493b188dabSeric 
4503b188dabSeric 	p = proc_exec(PROC_PRINTER, argv);
4513b188dabSeric 	if (p == NULL)
4523b188dabSeric 		log_warnx("%s: cannot exec printer process", __func__);
4533b188dabSeric 	else {
4543b188dabSeric 		proc_settitle(p, prn);
4553b188dabSeric 		proc_setcallback(p, priv_dispatch_printer, p);
4563b188dabSeric 		proc_enable(p);
4573b188dabSeric 	}
4583b188dabSeric 
4593b188dabSeric 	free(argv);
4603b188dabSeric 	free(buf);
4613b188dabSeric }
462