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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 /* $Id: in.lpd.c 170 2006-05-20 05:58:49Z njacobs $ */
42 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
48 #include <sys/systeminfo.h>
54 #define ACK(fp) { (void) fputc('\0', fp); (void) fflush(fp); }
55 #define NACK(fp) { (void) fputc('\1', fp); (void) fflush(fp); }
58 * This file contains the front-end of the BSD Print Protocol adaptor. This
59 * code assumes a BSD Socket interface to the networking side.
63 remote_host_name(FILE *fp
)
66 struct sockaddr_in6 peer
;
67 socklen_t peer_len
= sizeof (peer
);
70 char tmp_buf
[INET6_ADDRSTRLEN
];
73 /* who is our peer ? */
74 if (getpeername(fd
, (struct sockaddr
*)&peer
, &peer_len
) < 0) {
75 if ((errno
!= ENOTSOCK
) && (errno
!= EINVAL
))
78 return (strdup("localhost"));
81 /* get their name or return a string containing their address */
82 if ((hp
= getipnodebyaddr((const char *)&peer
.sin6_addr
,
83 sizeof (struct in6_addr
), AF_INET6
,
84 &error_num
)) == NULL
) {
85 return (strdup(inet_ntop(peer
.sin6_family
,
86 &peer
.sin6_addr
, tmp_buf
, sizeof (tmp_buf
))));
89 hostname
= strdup(hp
->h_name
);
90 if (is_localhost(hp
->h_name
) != 0)
91 return (strdup("localhost"));
93 /* It must be someone else */
98 fatal(FILE *fp
, char *fmt
, ...)
103 vsyslog(LOG_DEBUG
, fmt
, ap
);
104 vfprintf(fp
, fmt
, ap
);
110 cleanup(char ***files
, char **cf
)
112 if (*files
!= NULL
) {
115 for (i
= 0; (*files
)[i
] != NULL
; i
++) {
116 (void) unlink((*files
)[i
]);
129 static papi_attribute_t
**
130 parse_cf(papi_service_t svc
, char *cf
, char **files
)
132 papi_attribute_t
**list
= NULL
;
133 char previous
= NULL
;
138 for (entry
= strtok(cf
, "\n"); entry
!= NULL
;
139 entry
= strtok(NULL
, "\n")) {
142 /* count the copies */
143 if ((entry
[0] >= 'a') && (entry
[0] <= 'z') &&
144 (copies_set
== 0) && (previous
== entry
[0]))
146 else if ((previous
>= 'a') && (previous
<= 'z'))
150 /* process the control message */
152 /* RFC-1179 options */
153 case 'J': /* RFC-1179 Banner Job Name */
154 papiAttributeListAddString(&list
, PAPI_ATTR_EXCL
,
155 "job-name", ++entry
);
157 case 'C': /* RFC-1179 Banner Class Name */
158 papiAttributeListAddString(&list
, PAPI_ATTR_EXCL
,
159 "rfc-1179-class", ++entry
);
161 case 'L': /* RFC-1179 Banner toggle */
162 papiAttributeListAddString(&list
, PAPI_ATTR_EXCL
,
163 "job-sheets", "standard");
165 case 'T': /* RFC-1179 Title (pr) */
166 papiAttributeListAddString(&list
, PAPI_ATTR_EXCL
,
167 "pr-title", ++entry
);
169 case 'H': /* RFC-1179 Host */
171 * use the host as known by us, not by them
173 * papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
174 * "job-originating-host-name", ++entry);
177 case 'P': /* RFC-1179 User */
179 papiAttributeListAddString(&list
, PAPI_ATTR_EXCL
,
180 "job-originating-user-name", entry
);
181 papiServiceSetUserName(svc
, entry
);
183 case 'M': /* RFC-1179 Mail to User */
184 papiAttributeListAddBoolean(&list
, PAPI_ATTR_EXCL
,
187 case 'W': /* RFC-1179 Width (pr) */
188 papiAttributeListAddInteger(&list
, PAPI_ATTR_EXCL
,
189 "pr-width", atoi(++entry
));
191 case 'I': /* RFC-1179 Indent (pr) */
192 papiAttributeListAddInteger(&list
, PAPI_ATTR_EXCL
,
193 "pr-indent", atoi(++entry
));
195 case 'N': /* RFC-1179 Filename */
196 /* could have HPUX extension embedded */
197 if (entry
[1] != ' ') { /* real pathname */
199 papiAttributeListAddString(&list
,
203 } else if (entry
[2] == 'O') /* HPUX lp -o options */
204 papiAttributeListFromString(&list
,
205 PAPI_ATTR_APPEND
, ++entry
);
207 case 'U': /* RFC-1179 Unlink */
209 case '1': /* RFC-1179 TROFF Font R */
210 papiAttributeListAddString(&list
, PAPI_ATTR_EXCL
,
211 "rfc-1179-font-r", ++entry
);
213 case '2': /* RFC-1179 TROFF Font I */
214 papiAttributeListAddString(&list
, PAPI_ATTR_EXCL
,
215 "rfc-1179-font-i", ++entry
);
217 case '3': /* RFC-1179 TROFF Font B */
218 papiAttributeListAddString(&list
, PAPI_ATTR_EXCL
,
219 "rfc-1179-font-b", ++entry
);
221 case '4': /* RFC-1179 TROFF Font S */
222 papiAttributeListAddString(&list
, PAPI_ATTR_EXCL
,
223 "rfc-1179-font-s", ++entry
);
225 case 'f': /* RFC-1179 ASCII file (print) */
226 format
= "text/plain";
227 if (is_postscript(files
[0]) == 1)
228 format
= "application/postscript";
230 case 'l': /* RFC-1179 CATV file (print) */
231 format
= "application/octet-stream";
232 if (is_postscript(files
[0]) == 1)
233 format
= "application/postscript";
235 case 'o': /* RFC-1179 Postscript file (print) */
236 format
= "application/postscript";
238 case 'p': /* RFC-1179 PR file (print) */
239 format
= "application/x-pr";
240 papiAttributeListAddBoolean(&list
, PAPI_ATTR_EXCL
,
243 case 't': /* RFC-1179 TROFF file (print) */
244 format
= "application/x-troff";
246 case 'n': /* RFC-1179 DITROFF file (print) */
247 format
= "application/x-ditroff";
249 case 'd': /* RFC-1179 DVI file (print) */
250 format
= "application/x-dvi";
252 case 'g': /* RFC-1179 GRAPH file (print) */
253 format
= "application/x-plot";
255 case 'c': /* RFC-1179 CIF file (print) */
256 format
= "application/x-cif";
258 case 'v': /* RFC-1179 RASTER file (print) */
259 format
= "application/x-raster";
261 case 'r': /* RFC-1179 FORTRAN file (print) */
262 format
= "application/x-fortran";
264 /* Sun Solaris Extensions */
270 for (rd
= wr
= 0; entry
[rd
] != '\0'; rd
++) {
271 if (entry
[rd
] == '"')
274 entry
[wr
] = entry
[rd
];
279 papiAttributeListFromString(&list
,
280 PAPI_ATTR_APPEND
, entry
);
286 case 'f': /* Solaris form */
287 papiAttributeListAddString(&list
,
291 case 'H': /* Solaris handling */
293 if (strcasecmp(entry
, "hold") == 0)
294 papiAttributeListAddString(&list
,
296 "job-hold-until", "indefinite");
297 else if (strcasecmp(entry
, "immediate") == 0)
298 papiAttributeListAddString(&list
,
300 "job-hold-until", "no-hold");
302 papiAttributeListAddString(&list
,
304 "job-hold-until", entry
);
306 case 'p': /* Solaris notification */
307 papiAttributeListAddBoolean(&list
,
308 PAPI_ATTR_EXCL
, "rfc-1179-mail", 1);
310 case 'P': { /* Solaris page list */
313 snprintf(buf
, sizeof (buf
), "page-ranges=%s",
315 papiAttributeListFromString(&list
,
316 PAPI_ATTR_EXCL
, buf
);
319 case 'q': { /* Solaris priority */
320 int i
= atoi(++entry
);
323 if ((i
< 1) || (i
> 100))
325 papiAttributeListAddInteger(&list
,
326 PAPI_ATTR_EXCL
, "job-priority", i
);
329 case 'S': /* Solaris character set */
330 papiAttributeListAddString(&list
,
331 PAPI_ATTR_EXCL
, "lp-charset",
334 case 'T': /* Solaris type */
335 format
= lp_type_to_mime_type(++entry
);
337 case 'y': /* Solaris mode */
338 papiAttributeListAddString(&list
,
339 PAPI_ATTR_APPEND
, "lp-modes", ++entry
);
342 syslog(LOG_INFO
|LOG_DEBUG
,
343 "Warning: cf message (%s) ignored",
348 /* Undefined Extensions: SCO, Ultrix, AIX, ... */
351 syslog(LOG_INFO
|LOG_DEBUG
,
352 "Warning: cf message (%s) ignored", entry
);
357 papiAttributeListAddString(&list
, PAPI_ATTR_EXCL
,
358 "document-format", format
);
361 papiAttributeListAddInteger(&list
, PAPI_ATTR_EXCL
,
363 papiAttributeListAddString(&list
, PAPI_ATTR_EXCL
,
364 "job-sheets", "none");
370 submit_job(papi_service_t svc
, FILE *ifp
, char *printer
, int rid
, char *cf
,
373 papi_attribute_t
**list
= NULL
;
374 papi_status_t status
;
375 papi_job_t job
= NULL
;
378 if ((list
= parse_cf(svc
, cf
, files
)) != NULL
) {
379 /* use the host as known by us, not by them */
380 char *host
= remote_host_name(ifp
);
383 papiAttributeListAddString(&list
, PAPI_ATTR_REPLACE
,
384 "job-originating-host-name", host
);
388 papiAttributeListAddInteger(&list
, PAPI_ATTR_EXCL
,
389 "job-id-requested", rid
);
393 status
= papiJobSubmit(svc
, printer
, list
, NULL
, files
, &job
);
394 syslog(LOG_DEBUG
, "submit: %s", papiStatusString(status
));
395 if (status
!= PAPI_OK
) {
396 char *tmp
= papiServiceGetStatusMessage(svc
);
398 syslog(LOG_DEBUG
, "submit-detail: %s", tmp
? tmp
: "none");
406 receive_control_file(papi_service_t svc
, FILE *ifp
, FILE *ofp
, int size
)
410 if ((ptr
= cf_data
= calloc(1, size
+ 1)) == NULL
) {
419 if (((rc
= fread(ptr
, 1, size
, ifp
)) == 0) &&
428 syslog(LOG_DEBUG
, "cf_data(%s)", cf_data
);
430 if (fgetc(ifp
) != 0) {
440 receive_data_file(FILE *ifp
, FILE *ofp
, int size
)
442 char file
[] = "lpdXXXXXX";
446 if ((fd
= mkstemp(file
)) < 0) {
453 int rc
= ((size
> BUFSIZ
) ? BUFSIZ
: size
);
455 if (((rc
= fread(buf
, 1, rc
, ifp
)) == 0) &&
464 int wrc
= write(fd
, ptr
, rc
);
479 if (fgetc(ifp
) != 0) {
485 return (strdup(file
));
489 berkeley_receive_files(papi_service_t svc
, FILE *ifp
, FILE *ofp
, char *printer
)
491 papi_status_t status
= PAPI_OK
;
492 char *file
, **files
= NULL
; /* the job data files */
497 while (fgets(buf
, sizeof (buf
), ifp
) != NULL
) {
500 syslog(LOG_DEBUG
, "XFER CMD: (%d)%s\n", buf
[0], &buf
[1]);
501 #ifdef DEBUG /* translate [1-3]... messages to \[1-3] to run by hand */
502 if ((buf
[0] > '0') && (buf
[0] < '4'))
506 case 0x01: /* Abort */
507 cleanup(&files
, &cf
);
509 case 0x02: { /* Receive control file */
510 if (((cf
= strchr(buf
, ' ')) != NULL
) &&
512 while ((*cf
!= NULL
) && (isdigit(*cf
) == 0))
516 cf
= receive_control_file(svc
, ifp
, ofp
, atoi(&buf
[1]));
518 cleanup(&files
, &cf
);
519 return (PAPI_BAD_REQUEST
);
520 } else if (files
!= NULL
) {
521 status
= submit_job(svc
, ifp
, printer
, rid
, cf
,
523 cleanup(&files
, &cf
);
527 case 0x03: { /* Receive data file */
528 file
= receive_data_file(ifp
, ofp
, atoi(&buf
[1]));
530 cleanup(&files
, &cf
);
531 return (PAPI_TEMPORARY_ERROR
);
533 list_append(&files
, file
);
537 cleanup(&files
, &cf
);
538 fatal(ofp
, "protocol screwup");
543 if ((cf
!= NULL
) && (files
!= NULL
))
544 status
= submit_job(svc
, ifp
, printer
, rid
, cf
, files
);
546 cleanup(&files
, &cf
);
552 berkeley_transfer_files(papi_service_t svc
, FILE *ifp
, FILE *ofp
,
555 papi_status_t status
;
556 papi_printer_t p
= NULL
;
557 char *keys
[] = { "printer-is-accepting-jobs", NULL
};
559 status
= papiPrinterQuery(svc
, printer
, keys
, NULL
, &p
);
560 if ((status
== PAPI_OK
) && (p
!= NULL
)) {
561 papi_attribute_t
**attrs
= papiPrinterGetAttributeList(p
);
562 char accepting
= PAPI_FALSE
;
564 papiAttributeListGetBoolean(attrs
, NULL
,
565 "printer-is-accepting-jobs", &accepting
);
567 if (accepting
== PAPI_TRUE
) {
569 status
= berkeley_receive_files(svc
, ifp
, ofp
, printer
);
581 cyclical_service_check(char *svc_name
)
583 papi_attribute_t
**list
;
587 /* was there a printer? */
588 if (svc_name
== NULL
)
591 if ((list
= getprinterbyname(svc_name
, NULL
)) == NULL
)
592 return (0); /* if it doesnt' resolve, we will fail later */
594 papiAttributeListGetString(list
, NULL
, "printer-uri-supported", &s
);
595 if ((s
== NULL
) || (strcasecmp(svc_name
, s
) != 0))
596 return (0); /* they don't match */
598 /* is it in uri form? */
599 if (uri_from_string(s
, &uri
) < 0)
602 if ((uri
== NULL
) || (uri
->scheme
== NULL
) || (uri
->host
== NULL
)) {
607 /* is it in lpd form? */
608 if (strcasecmp(uri
->scheme
, "lpd") != 0) {
613 /* is it the local host? */
614 if (is_localhost(uri
->host
) != 0) {
625 * This is the entry point for this program. The program takes the
630 main(int ac
, char *av
[])
632 papi_status_t status
;
633 papi_service_t svc
= NULL
;
634 papi_encryption_t encryption
= PAPI_ENCRYPT_NEVER
;
641 char *run_dir
= "/var/run/in.lpd";
642 char *run_user
= NULL
;
643 struct passwd
*pw
= NULL
;
645 (void) chdir("/tmp"); /* run in /tmp by default */
646 openlog("bsd-gw", LOG_PID
, LOG_LPR
);
648 while ((c
= getopt(ac
, av
, "Ed:u:")) != EOF
)
651 encryption
= PAPI_ENCRYPT_ALWAYS
;
653 case 'd': /* run where they tell you */
656 case 'u': /* run as */
663 if (run_user
!= NULL
) /* get the requested user info */
664 pw
= getpwnam(run_user
);
666 if (run_dir
!= NULL
) { /* setup the run_dir */
667 (void) mkdir(run_dir
, 0700);
669 (void) chown(run_dir
, pw
->pw_uid
, pw
->pw_gid
);
672 if (pw
!= NULL
) { /* run as the requested user */
673 syslog(LOG_DEBUG
, "name: %s, uid: %d, gid: %d",
674 pw
->pw_name
, pw
->pw_uid
, pw
->pw_gid
);
675 initgroups(pw
->pw_name
, pw
->pw_gid
);
680 if (run_dir
!= NULL
) /* move to the run_dir */
681 if (chdir(run_dir
) < 0) {
682 syslog(LOG_DEBUG
, "failed to chdir(%s)", run_dir
);
686 syslog(LOG_DEBUG
, "$CWD = %s", getwd(NULL
));
688 if (fgets(buf
, sizeof (buf
), ifp
) == NULL
) {
690 syslog(LOG_ERR
, "Error reading from connection: %s",
695 syslog(LOG_DEBUG
, "CMD: (%d)%s\n", buf
[0], &buf
[1]);
697 #ifdef DEBUG /* translate [1-5]... messages to \[1-5] to run by hand */
698 if ((buf
[0] > '0') && (buf
[0] < '6'))
702 if ((buf
[0] < 1) || (buf
[0] > 5)) {
703 fatal(ofp
, "Invalid protocol request (%d): %c%s\n",
704 buf
[0], buf
[0], buf
);
708 args
= strsplit(&buf
[1], "\t\n ");
711 if (printer
== NULL
) {
712 fatal(ofp
, "Can't determine requested printer");
716 if (cyclical_service_check(printer
) != 0) {
717 fatal(ofp
, "%s is cyclical\n", printer
);
721 status
= papiServiceCreate(&svc
, printer
, NULL
, NULL
, NULL
,
723 if (status
!= PAPI_OK
) {
724 fatal(ofp
, "Failed to contact service for %s: %s\n", printer
,
725 verbose_papi_message(svc
, status
));
730 * Trusted Solaris can't be trusting of intermediaries. Pass
731 * the socket connection to the print service to retrieve the
732 * sensativity label off of a multi-level port.
734 (void) papiServiceSetPeer(svc
, fileno(ifp
));
737 case '\1': /* restart printer */
738 ACK(ofp
); /* there is no equivalent */
740 case '\2': /* transfer job(s) */
741 status
= berkeley_transfer_files(svc
, ifp
, ofp
, printer
);
743 case '\3': /* show queue (short) */
744 case '\4': { /* show queue (long) */
747 for (count
= 0; args
[count
] != 0; count
++) {}
749 berkeley_queue_report(svc
, ofp
, printer
, buf
[0], count
, args
);
752 case '\5': { /* cancel job(s) */
753 char *user
= *args
++;
754 char *host
= remote_host_name(ifp
);
760 snprintf(buf
, sizeof (buf
), "%s@%s", user
, host
);
761 status
= papiServiceSetUserName(svc
, buf
);
763 status
= papiServiceSetUserName(svc
, user
);
765 for (count
= 0; args
[count
] != 0; count
++) {}
767 berkeley_cancel_request(svc
, ofp
, printer
, count
, args
);
771 fatal(ofp
, "unsupported protocol request (%c), %s",
777 syslog(LOG_DEBUG
, "protocol request(%d) for %s completed: %s",
778 buf
[0], printer
, papiStatusString(status
));
779 if (status
!= PAPI_OK
)
780 syslog(LOG_DEBUG
, "detail: %s",
781 verbose_papi_message(svc
, status
));
783 papiServiceDestroy(svc
);