xref: /illumos-gate/usr/src/cmd/print/bsd-sysv-commands/in.lpd.c (revision 24b9abbad58fdd63dad716fd35a99a7944c4e3eb)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  */
27 
28 /* $Id: in.lpd.c 170 2006-05-20 05:58:49Z njacobs $ */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <stdarg.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <errno.h>
40 #include <syslog.h>
41 #include <libintl.h>
42 #include <pwd.h>
43 #include <grp.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <netdb.h>
50 #include <sys/systeminfo.h>
51 
52 #include <papi.h>
53 #include <uri.h>
54 #include "common.h"
55 
56 #define	ACK(fp)		{ (void) fputc('\0', fp); (void) fflush(fp); }
57 #define	NACK(fp)	{ (void) fputc('\1', fp); (void) fflush(fp); }
58 
59 /*
60  * This file contains the front-end of the BSD Print Protocol adaptor.  This
61  * code assumes a BSD Socket interface to the networking side.
62  */
63 
64 static char *
65 remote_host_name(FILE *fp)
66 {
67 	struct hostent *hp;
68 	struct sockaddr_in6 peer;
69 	socklen_t peer_len = sizeof (peer);
70 	int fd = fileno(fp);
71 	int error_num;
72 	char myname[MAXHOSTNAMELEN], tmp_buf[INET6_ADDRSTRLEN];
73 	char *hostname;
74 
75 	/* who is our peer ? */
76 	if (getpeername(fd, (struct sockaddr *)&peer, &peer_len) < 0) {
77 		if ((errno != ENOTSOCK) && (errno != EINVAL))
78 			return (NULL);
79 		else
80 			return (strdup("localhost"));
81 	}
82 
83 	/* get their name or return a string containing their address */
84 	if ((hp = getipnodebyaddr((const char *)&peer.sin6_addr,
85 				sizeof (struct in6_addr), AF_INET6,
86 				&error_num)) == NULL) {
87 		return (strdup(inet_ntop(peer.sin6_family,
88 			&peer.sin6_addr, tmp_buf, sizeof (tmp_buf))));
89 	}
90 
91 	/* is it "localhost" ? */
92 	if (strcasecmp(hp->h_name, "localhost") == 0)
93 		return (strdup("localhost"));
94 
95 	/* duplicate the name because gethostbyXXXX() is not reentrant */
96 	hostname = strdup(hp->h_name);
97 	(void) sysinfo(SI_HOSTNAME, myname, sizeof (myname));
98 
99 	/* is it from one of my addresses ? */
100 	if ((hp = getipnodebyname(myname, AF_INET6, AI_ALL|AI_V4MAPPED,
101 	    &error_num)) != NULL) {
102 		struct in6_addr **tmp = (struct in6_addr **)hp->h_addr_list;
103 		int i = 0;
104 
105 		while (tmp[i] != NULL) {
106 			if (memcmp(tmp[i++], &peer.sin6_addr, hp->h_length)
107 			    == 0) {
108 				free(hostname);
109 				return (strdup("localhost"));
110 			}
111 		}
112 	}
113 
114 	/* It must be someone else */
115 	return (hostname);
116 }
117 
118 static void
119 fatal(FILE *fp, char *fmt, ...)
120 {
121 	va_list ap;
122 
123 	va_start(ap, fmt);
124 	vsyslog(LOG_DEBUG, fmt, ap);
125 	vfprintf(fp, fmt, ap);
126 	va_end(ap);
127 	exit(1);
128 }
129 
130 static void
131 cleanup(char ***files, char **cf)
132 {
133 	if (*files != NULL) {
134 		int i;
135 
136 		for (i = 0; (*files)[i] != NULL; i++) {
137 			(void) unlink((*files)[i]);
138 			free((*files)[i]);
139 		}
140 		free(*files);
141 		*files = NULL;
142 	}
143 
144 	if (*cf != NULL) {
145 		free(*cf);
146 		*cf = NULL;
147 	}
148 }
149 
150 static papi_attribute_t **
151 parse_cf(papi_service_t svc, char *cf, char **files)
152 {
153 	papi_attribute_t **list = NULL;
154 	char	previous = NULL,
155 		*entry,
156 		*s,
157 		text[BUFSIZ];
158 	int	count = 0,
159 		copies_set = 0,
160 		copies = 0;
161 
162 	for (entry = strtok(cf, "\n"); entry != NULL;
163 	    entry = strtok(NULL, "\n")) {
164 		char *format = NULL;
165 
166 		/* count the copies */
167 		if ((entry[0] >= 'a') && (entry[0] <= 'z') &&
168 		    (copies_set == 0) && (previous == entry[0]))
169 			copies++;
170 		else if ((previous >= 'a') && (previous <= 'z'))
171 			copies_set = 1;
172 		previous = entry[0];
173 
174 		/* process the control message */
175 		switch (entry[0]) {
176 		/* RFC-1179 options */
177 		case 'J':	/* RFC-1179 Banner Job Name */
178 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
179 					"job-name", ++entry);
180 			break;
181 		case 'C':	/* RFC-1179 Banner Class Name */
182 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
183 					"rfc-1179-class", ++entry);
184 			break;
185 		case 'L':	/* RFC-1179 Banner toggle  */
186 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
187 					"job-sheets", "standard");
188 			break;
189 		case 'T':	/* RFC-1179 Title (pr)  */
190 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
191 					"pr-title", ++entry);
192 			break;
193 		case 'H':	/* RFC-1179 Host */
194 			/*
195 			 * use the host as known by us, not by them
196 			 *
197 			 * papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
198 			 *		"job-originating-host-name", ++entry);
199 			 */
200 			break;
201 		case 'P':	/* RFC-1179 User */
202 			++entry;
203 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
204 					"requesting-user-name", entry);
205 			papiServiceSetUserName(svc, entry);
206 			break;
207 		case 'M':	/* RFC-1179 Mail to User */
208 			papiAttributeListAddBoolean(&list, PAPI_ATTR_EXCL,
209 				"rfc-1179-mail", 1);
210 			break;
211 		case 'W':	/* RFC-1179 Width (pr) */
212 			papiAttributeListAddInteger(&list, PAPI_ATTR_EXCL,
213 					"pr-width", atoi(++entry));
214 			break;
215 		case 'I':	/* RFC-1179 Indent (pr) */
216 			papiAttributeListAddInteger(&list, PAPI_ATTR_EXCL,
217 					"pr-indent", atoi(++entry));
218 			break;
219 		case 'N':	/* RFC-1179 Filename */
220 			/* could have HPUX extension embedded */
221 			if (entry[1] != ' ') {	/* real pathname */
222 #ifdef DEBUG
223 				papiAttributeListAddString(&list,
224 						PAPI_ATTR_EXCL,
225 						"flist", ++entry);
226 #endif
227 			} else if (entry[2] == 'O') /* HPUX lp -o options */
228 				papiAttributeListFromString(&list,
229 						PAPI_ATTR_APPEND, ++entry);
230 			break;
231 		case 'U':	/* RFC-1179 Unlink */
232 			break;	/* ignored */
233 		case '1':	/* RFC-1179 TROFF Font R */
234 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
235 				"rfc-1179-font-r", ++entry);
236 			break;
237 		case '2':	/* RFC-1179 TROFF Font I */
238 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
239 				"rfc-1179-font-i", ++entry);
240 			break;
241 		case '3':	/* RFC-1179 TROFF Font B */
242 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
243 				"rfc-1179-font-b", ++entry);
244 			break;
245 		case '4':	/* RFC-1179 TROFF Font S */
246 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
247 				"rfc-1179-font-s", ++entry);
248 			break;
249 		case 'f':	/* RFC-1179 ASCII file (print) */
250 			format = "text/plain";
251 			if (is_postscript(files[0]) == 1)
252 				format = "application/postscript";
253 			break;
254 		case 'l':	/* RFC-1179 CATV file (print) */
255 			format = "application/octet-stream";
256 			if (is_postscript(files[0]) == 1)
257 				format = "application/postscript";
258 			break;
259 		case 'o':	/* RFC-1179 Postscript file (print) */
260 			format = "application/postscript";
261 			break;
262 		case 'p':	/* RFC-1179 PR file (print) */
263 			format = "application/x-pr";
264 			papiAttributeListAddBoolean(&list, PAPI_ATTR_EXCL,
265 					"pr-filter", 1);
266 			break;
267 		case 't':	/* RFC-1179 TROFF file (print) */
268 			format = "application/x-troff";
269 			break;
270 		case 'n':	/* RFC-1179 DITROFF file (print) */
271 			format = "application/x-ditroff";
272 			break;
273 		case 'd':	/* RFC-1179 DVI file (print) */
274 			format = "application/x-dvi";
275 			break;
276 		case 'g':	/* RFC-1179 GRAPH file (print) */
277 			format = "application/x-plot";
278 			break;
279 		case 'c':	/* RFC-1179 CIF file (print) */
280 			format = "application/x-cif";
281 			break;
282 		case 'v':	/* RFC-1179 RASTER file (print) */
283 			format = "application/x-raster";
284 			break;
285 		case 'r':	/* RFC-1179 FORTRAN file (print) */
286 			format = "application/x-fortran";
287 			break;
288 		/* Sun Solaris Extensions */
289 		case 'O':
290 			++entry;
291 			do {
292 				if (*entry != '"')
293 					text[count++] = *entry;
294 			} while (*entry++);
295 			papiAttributeListFromString(&list, PAPI_ATTR_APPEND,
296 				text);
297 			break;
298 		case '5':
299 			++entry;
300 			switch (entry[0]) {
301 			case 'f':	/* Solaris form */
302 				papiAttributeListAddString(&list,
303 						PAPI_ATTR_EXCL,
304 						"form", ++entry);
305 				break;
306 			case 'H':	/* Solaris handling */
307 				++entry;
308 				if (strcasecmp(entry, "hold") == 0)
309 					papiAttributeListAddString(&list,
310 						PAPI_ATTR_EXCL,
311 						"job-hold-until", "indefinite");
312 				else if (strcasecmp(entry, "immediate") == 0)
313 					papiAttributeListAddString(&list,
314 						PAPI_ATTR_EXCL,
315 						"job-hold-until", "no-hold");
316 				else
317 					papiAttributeListAddString(&list,
318 						PAPI_ATTR_EXCL,
319 						"job-hold-until", entry);
320 				break;
321 			case 'p':	/* Solaris notification */
322 				papiAttributeListAddBoolean(&list,
323 					PAPI_ATTR_EXCL, "rfc-1179-mail", 1);
324 				break;
325 			case 'P': {	/* Solaris page list */
326 				char buf[BUFSIZ];
327 
328 				snprintf(buf, sizeof (buf), "page-ranges=%s",
329 						++entry);
330 				papiAttributeListFromString(&list,
331 						PAPI_ATTR_EXCL, buf);
332 				}
333 				break;
334 			case 'q': {	/* Solaris priority */
335 				int i = atoi(optarg);
336 
337 				i = 100 - (i * 2.5);
338 				if ((i < 1) || (i > 100))
339 					i = 50;
340 				papiAttributeListAddInteger(&list,
341 					PAPI_ATTR_EXCL, "job-priority", i);
342 				}
343 				break;
344 			case 'S':	/* Solaris character set */
345 				papiAttributeListAddString(&list,
346 					PAPI_ATTR_EXCL, "lp-charset",
347 					++entry);
348 				break;
349 			case 'T':	/* Solaris type */
350 				format = lp_type_to_mime_type(++entry);
351 				break;
352 			case 'y':	/* Solaris mode */
353 				papiAttributeListAddString(&list,
354 					PAPI_ATTR_APPEND, "lp-modes", ++entry);
355 				break;
356 			default:
357 				syslog(LOG_INFO|LOG_DEBUG,
358 					"Warning: cf message (%s) ignored",
359 					entry);
360 				break;
361 			}
362 			break;
363 		/* Undefined Extensions: SCO, Ultrix, AIX, ... */
364 
365 		default:
366 			syslog(LOG_INFO|LOG_DEBUG,
367 				"Warning: cf message (%s) ignored", entry);
368 			break;
369 		}
370 
371 		if (format != NULL)
372 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
373 				"document-format", format);
374 	}
375 
376 	papiAttributeListAddInteger(&list, PAPI_ATTR_EXCL,
377 					"copies", ++copies);
378 	papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
379 					"job-sheets", "none");
380 
381 	return (list);
382 }
383 
384 static papi_status_t
385 submit_job(papi_service_t svc, FILE *ifp, char *printer, int rid, char *cf,
386 		char **files)
387 {
388 	papi_attribute_t **list = NULL;
389 	papi_status_t status;
390 	papi_job_t job = NULL;
391 	char *format = "";
392 
393 	if ((list = parse_cf(svc, cf, files)) != NULL) {
394 		/* use the host as known by us, not by them */
395 		char *host = remote_host_name(ifp);
396 
397 		if (host != NULL) {
398 			papiAttributeListAddString(&list, PAPI_ATTR_REPLACE,
399 					"job-originating-host-name", host);
400 			free(host);
401 		}
402 		if (rid > 0) {
403 			papiAttributeListAddInteger(&list, PAPI_ATTR_EXCL,
404 					"job-id-requested", rid);
405 		}
406 	}
407 
408 	status = papiJobSubmit(svc, printer, list, NULL, files, &job);
409 	syslog(LOG_DEBUG, "submit: %s", papiStatusString(status));
410 	if (status != PAPI_OK) {
411 		char *tmp = papiServiceGetStatusMessage(svc);
412 
413 		syslog(LOG_DEBUG, "submit-detail: %s", tmp ? tmp : "none");
414 	}
415 	papiJobFree(job);
416 
417 	return (status);
418 }
419 
420 static char *
421 receive_control_file(papi_service_t svc, FILE *ifp, FILE *ofp, int size)
422 {
423 	char *ptr, *cf_data;
424 
425 	if ((ptr = cf_data = calloc(1, size + 1)) == NULL) {
426 		NACK(ofp);
427 		return (NULL);
428 	} else
429 		ACK(ofp);
430 
431 	while (size > 0) {
432 		int rc;
433 
434 		if (((rc = fread(ptr, 1, size, ifp)) == 0) &&
435 		    (feof(ifp) != 0)) {
436 			free(cf_data);
437 			return (NULL);
438 		} else {
439 			ptr += rc;
440 			size -= rc;
441 		}
442 	}
443 	syslog(LOG_DEBUG, "cf_data(%s)", cf_data);
444 
445 	if (fgetc(ifp) != 0) {
446 		free(cf_data);
447 		return (NULL);
448 	}
449 	ACK(ofp);
450 
451 	return (cf_data);
452 }
453 
454 static char *
455 receive_data_file(FILE *ifp, FILE *ofp, int size)
456 {
457 	char file[] = "lpdXXXXXX";
458 	char buf[BUFSIZ];
459 	int fd;
460 
461 	if ((fd = mkstemp(file)) < 0) {
462 		NACK(ofp);
463 		return (NULL);
464 	} else
465 		ACK(ofp);
466 
467 	while (size > 0) {
468 		int rc = ((size > BUFSIZ) ? BUFSIZ : size);
469 
470 		if (((rc = fread(buf, 1, rc, ifp)) == 0) &&
471 		    (feof(ifp) != 0)) {
472 			close(fd);
473 			unlink(file);
474 			return (NULL);
475 		} else {
476 			char *ptr = buf;
477 
478 			while (rc > 0) {
479 				int wrc = write(fd, ptr, rc);
480 
481 				if (wrc < 0) {
482 					close(fd);
483 					unlink(file);
484 					return (NULL);
485 				}
486 
487 				ptr += wrc;
488 				size -= wrc;
489 				rc -= wrc;
490 			}
491 		}
492 	}
493 	close(fd);
494 	if (fgetc(ifp) != 0) {
495 		unlink(file);
496 		return (NULL);
497 	}
498 	ACK(ofp);
499 
500 	return (strdup(file));
501 }
502 
503 static papi_status_t
504 berkeley_receive_files(papi_service_t svc, FILE *ifp, FILE *ofp, char *printer)
505 {
506 	papi_status_t status = PAPI_OK;
507 	char *file, **files = NULL;	/* the job data files */
508 	char *cf = NULL;
509 	int rid = 0;
510 	char buf[BUFSIZ];
511 
512 	while (fgets(buf, sizeof (buf), ifp) != NULL) {
513 		int size;
514 
515 		syslog(LOG_DEBUG, "XFER CMD: (%d)%s\n", buf[0], &buf[1]);
516 #ifdef DEBUG	/* translate [1-3]... messages to \[1-3] to run by hand */
517 		if ((buf[0] > '0') && (buf[0] < '4'))
518 			buf[0] -= '0';
519 #endif
520 		switch (buf[0]) {
521 		case 0x01:	/* Abort */
522 			cleanup(&files, &cf);
523 			break;
524 		case 0x02: {	/* Receive control file */
525 			if (((cf = strchr(buf, ' ')) != NULL) &&
526 			    (strlen(cf) > 4)) {
527 				while ((*cf != NULL) && (isdigit(*cf) == 0))
528 					cf++;
529 				rid = atoi(cf);
530 			}
531 			cf = receive_control_file(svc, ifp, ofp, atoi(&buf[1]));
532 			if (cf == NULL) {
533 				cleanup(&files, &cf);
534 				return (PAPI_BAD_REQUEST);
535 			} else if (files != NULL) {
536 				status = submit_job(svc, ifp, printer, rid, cf,
537 							files);
538 				cleanup(&files, &cf);
539 			}
540 			}
541 			break;
542 		case 0x03: {	/* Receive data file */
543 			file = receive_data_file(ifp, ofp, atoi(&buf[1]));
544 			if (file == NULL) {
545 				cleanup(&files, &cf);
546 				return (PAPI_TEMPORARY_ERROR);
547 			}
548 			list_append(&files, file);
549 			}
550 			break;
551 		default:
552 			cleanup(&files, &cf);
553 			fatal(ofp, "protocol screwup");
554 			break;
555 		}
556 	}
557 
558 	if ((cf != NULL) && (files != NULL))
559 		status = submit_job(svc, ifp, printer, rid, cf, files);
560 
561 	cleanup(&files, &cf);
562 
563 	return (status);
564 }
565 
566 static papi_status_t
567 berkeley_transfer_files(papi_service_t svc, FILE *ifp, FILE *ofp,
568 		char *printer)
569 {
570 	papi_status_t status;
571 	papi_printer_t p = NULL;
572 	char *keys[] = { "printer-is-accepting-jobs", NULL };
573 
574 	status = papiPrinterQuery(svc, printer, keys, NULL, &p);
575 	if ((status == PAPI_OK) && (p != NULL)) {
576 		papi_attribute_t **attrs = papiPrinterGetAttributeList(p);
577 		char accepting = PAPI_FALSE;
578 
579 		papiAttributeListGetBoolean(attrs, NULL,
580 				"printer-is-accepting-jobs", &accepting);
581 
582 		if (accepting == PAPI_TRUE) {
583 			ACK(ofp);
584 			status = berkeley_receive_files(svc, ifp, ofp, printer);
585 		} else
586 			NACK(ofp);
587 
588 		papiPrinterFree(p);
589 	} else
590 		NACK(ofp);
591 
592 	return (status);
593 }
594 
595 static int
596 cyclical_service_check(char *svc_name)
597 {
598 	papi_attribute_t **list;
599 	char buf[BUFSIZ];
600 	uri_t *uri = NULL;
601 	char *s = NULL;
602 
603 	/* was there a printer? */
604 	if (svc_name == NULL)
605 		return (0);
606 
607 	if ((list = getprinterbyname(svc_name, NULL)) == NULL)
608 		return (0);	/* if it doesnt' resolve, we will fail later */
609 
610 	papiAttributeListGetString(list, NULL, "printer-uri-supported", &s);
611 	if ((s == NULL) || (strcasecmp(svc_name, s) != 0))
612 		return (0);	/* they don't match */
613 
614 	/* is it in uri form? */
615 	if (uri_from_string(s, &uri) < 0)
616 		return (0);
617 
618 	if ((uri == NULL) || (uri->scheme == NULL) || (uri->host == NULL)) {
619 		uri_free(uri);
620 		return (0);
621 	}
622 
623 	/* is it in lpd form? */
624 	if (strcasecmp(uri->scheme, "lpd") != 0) {
625 		uri_free(uri);
626 		return (0);
627 	}
628 
629 	/* is it the local host? */
630 	sysinfo(SI_HOSTNAME, buf, sizeof (buf));
631 	if ((strcasecmp(uri->host, "localhost") != 0) &&
632 	    (strcasecmp(uri->host, buf) != 0)) {
633 		uri_free(uri);
634 		return (0);
635 	}
636 
637 	uri_free(uri);
638 	return (1);
639 }
640 
641 
642 /*
643  * This is the entry point for this program.  The program takes the
644  * following options:
645  * 	(none)
646  */
647 int
648 main(int ac, char *av[])
649 {
650 	papi_status_t status;
651 	papi_service_t svc = NULL;
652 	papi_encryption_t encryption = PAPI_ENCRYPT_NEVER;
653 	FILE	*ifp = stdin,
654 		*ofp = stdout;
655 	int	c;
656 	char	buf[BUFSIZ],
657 		**args,
658 		*printer,
659 		*run_dir = "/var/run/in.lpd",
660 		*run_user = NULL;
661 	struct passwd *pw = NULL;
662 
663 	(void) chdir("/tmp");		/* run in /tmp by default */
664 	openlog("bsd-gw", LOG_PID, LOG_LPR);
665 
666 	while ((c = getopt(ac, av, "Ed:u:")) != EOF)
667 		switch (c) {
668 		case 'E':
669 			encryption = PAPI_ENCRYPT_ALWAYS;
670 			break;
671 		case 'd':	/* run where they tell you */
672 			run_dir = optarg;
673 			break;
674 		case 'u':	/* run as */
675 			run_user = optarg;
676 			break;
677 		default:
678 			;
679 		}
680 
681 	if (run_user != NULL)	/* get the requested user info */
682 		pw = getpwnam(run_user);
683 
684 	if (run_dir != NULL) {	/* setup the run_dir */
685 		(void) mkdir(run_dir, 0700);
686 		if (pw != NULL)
687 			(void) chown(run_dir, pw->pw_uid, pw->pw_gid);
688 	}
689 
690 	if (pw != NULL) {	/* run as the requested user */
691 		syslog(LOG_DEBUG, "name: %s, uid: %d, gid: %d",
692 				pw->pw_name, pw->pw_uid, pw->pw_gid);
693 		initgroups(pw->pw_name, pw->pw_gid);
694 		setgid(pw->pw_gid);
695 		setuid(pw->pw_uid);
696 	}
697 
698 	if (run_dir != NULL)	/* move to the run_dir */
699 		if (chdir(run_dir) < 0) {
700 			syslog(LOG_DEBUG, "failed to chdir(%s)", run_dir);
701 			exit(1);
702 		}
703 
704 	syslog(LOG_DEBUG, "$CWD = %s", getwd(NULL));
705 
706 	if (fgets(buf, sizeof (buf), ifp) == NULL) {
707 		if (feof(ifp) == 0)
708 			syslog(LOG_ERR, "Error reading from connection: %s",
709 				strerror(errno));
710 		exit(1);
711 	}
712 
713 	syslog(LOG_DEBUG, "CMD: (%d)%s\n", buf[0], &buf[1]);
714 
715 #ifdef DEBUG	/* translate [1-5]... messages to \[1-5] to run by hand */
716 	if ((buf[0] > '0') && (buf[0] < '6'))
717 		buf[0] -= '0';
718 #endif
719 
720 	if ((buf[0] < 1) || (buf[0] > 5)) {
721 		fatal(ofp, "Invalid protocol request (%d): %c%s\n",
722 			buf[0], buf[0], buf);
723 		exit(1);
724 	}
725 
726 	args = strsplit(&buf[1], "\t\n ");
727 	printer = *args++;
728 
729 	if (printer == NULL) {
730 		fatal(ofp, "Can't determine requested printer");
731 		exit(1);
732 	}
733 
734 	if (cyclical_service_check(printer) != 0) {
735 		fatal(ofp, "%s is cyclical\n", printer);
736 		exit(1);
737 	}
738 
739 	status = papiServiceCreate(&svc, printer, NULL, NULL, NULL,
740 					encryption, NULL);
741 	if (status != PAPI_OK) {
742 		fatal(ofp, "Failed to contact service for %s: %s\n", printer,
743 			verbose_papi_message(svc, status));
744 		exit(1);
745 	}
746 
747 	/*
748 	 * Trusted Solaris can't be trusting of intermediaries.  Pass
749 	 * the socket connection to the print service to retrieve the
750 	 * sensativity label off of a multi-level port.
751 	 */
752 	(void) papiServiceSetPeer(svc, fileno(ifp));
753 
754 	switch (buf[0]) {
755 	case '\1':	/* restart printer */
756 		ACK(ofp);	/* there is no equivalent */
757 		break;
758 	case '\2':	/* transfer job(s) */
759 		status = berkeley_transfer_files(svc, ifp, ofp, printer);
760 		break;
761 	case '\3':	/* show queue (short) */
762 	case '\4': {	/* show queue (long) */
763 		int count;
764 
765 		for (count = 0; args[count] != 0; count++);
766 
767 		berkeley_queue_report(svc, ofp, printer, buf[0], count, args);
768 		}
769 		break;
770 	case '\5': {	/* cancel job(s) */
771 		char *user = *args++;
772 		char *host = remote_host_name(ifp);
773 		int count;
774 
775 		if (host != NULL) {
776 			char buf[BUFSIZ];
777 
778 			snprintf(buf, sizeof (buf), "%s@%s", user, host);
779 			status = papiServiceSetUserName(svc, buf);
780 		} else
781 			status = papiServiceSetUserName(svc, user);
782 
783 		for (count = 0; args[count] != 0; count++);
784 
785 		berkeley_cancel_request(svc, ofp, printer, count, args);
786 		}
787 		break;
788 	default:
789 		fatal(ofp, "unsupported protocol request (%c), %s",
790 			buf[0], &buf[1]);
791 	}
792 
793 	(void) fflush(ofp);
794 
795 	syslog(LOG_DEBUG, "protocol request(%d) for %s completed: %s",
796 		buf[0], printer, papiStatusString(status));
797 	if (status != PAPI_OK)
798 		syslog(LOG_DEBUG, "detail: %s",
799 				verbose_papi_message(svc, status));
800 
801 	papiServiceDestroy(svc);
802 
803 	return (0);
804 }
805