]>
git.ipfire.org Git - thirdparty/cups.git/blob - backend/lpd.c
2 * "$Id: lpd.c,v 1.54 2003/10/09 19:13:27 mike Exp $"
4 * Line Printer Daemon backend for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2003 by Easy Software Products, all rights reserved.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
24 * This file is subject to the Apple OS-Developed Software exception.
28 * main() - Send a file to the printer or server.
29 * lpd_command() - Send an LPR command sequence and wait for a reply.
30 * lpd_queue() - Queue a file using the Line Printer Daemon protocol.
31 * lpd_timeout() - Handle timeout alarms...
32 * lpd_write() - Write a buffer of data to an LPD server.
33 * rresvport() - A simple implementation of rresvport().
34 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
38 * Include necessary headers.
41 #include <cups/cups.h>
46 #include <cups/http-private.h>
47 #include <cups/string.h>
49 #include <sys/types.h>
56 # include <sys/socket.h>
57 # include <netinet/in.h>
58 # include <arpa/inet.h>
67 static char tmpfilename
[1024] = ""; /* Temporary spool file name */
71 * The order for control and data files in LPD requests...
74 #define ORDER_CONTROL_DATA 0
75 #define ORDER_DATA_CONTROL 1
79 * It appears that rresvport() is never declared on most systems...
82 extern int rresvport(int *port
);
89 static int lpd_command(int lpd_fd
, int timeout
, char *format
, ...);
90 static int lpd_queue(const char *hostname
, int port
, const char *printer
,
92 const char *user
, const char *title
, int copies
,
93 int banner
, int format
, int order
, int reserve
,
94 int manual_copies
, int timeout
);
95 static void lpd_timeout(int sig
);
96 static int lpd_write(int lpd_fd
, char *buffer
, int length
);
97 static void sigterm_handler(int sig
);
101 * 'main()' - Send a file to the printer or server.
105 * printer-uri job-id user title copies options [file]
108 int /* O - Exit status */
109 main(int argc
, /* I - Number of command-line arguments (6 or 7) */
110 char *argv
[]) /* I - Command-line arguments */
112 char method
[255], /* Method in URI */
113 hostname
[1024], /* Hostname */
114 username
[255], /* Username info (not used) */
115 resource
[1024], /* Resource info (printer name) */
116 *options
, /* Pointer to options */
117 name
[255], /* Name of option */
118 value
[255], /* Value of option */
119 *ptr
, /* Pointer into name or value */
120 *filename
, /* File to print */
121 title
[256]; /* Title string */
122 int port
; /* Port number (not used) */
123 int status
; /* Status of LPD job */
124 int banner
; /* Print banner page? */
125 int format
; /* Print format */
126 int order
; /* Order of control/data files */
127 int reserve
; /* Reserve priviledged port? */
128 int sanitize_title
; /* Sanitize title string? */
129 int manual_copies
, /* Do manual copies? */
130 timeout
, /* Timeout */
131 copies
; /* Number of copies */
132 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
133 struct sigaction action
; /* Actions for POSIX signals */
134 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
138 * Make sure status messages are not buffered...
141 setbuf(stderr
, NULL
);
144 * Ignore SIGPIPE and catch SIGTERM signals...
148 sigset(SIGPIPE
, SIG_IGN
);
149 sigset(SIGTERM
, sigterm_handler
);
150 #elif defined(HAVE_SIGACTION)
151 memset(&action
, 0, sizeof(action
));
152 action
.sa_handler
= SIG_IGN
;
153 sigaction(SIGPIPE
, &action
, NULL
);
155 sigemptyset(&action
.sa_mask
);
156 sigaddset(&action
.sa_mask
, SIGTERM
);
157 action
.sa_handler
= sigterm_handler
;
158 sigaction(SIGTERM
, &action
, NULL
);
160 signal(SIGPIPE
, SIG_IGN
);
161 signal(SIGTERM
, sigterm_handler
);
162 #endif /* HAVE_SIGSET */
165 * Check command-line...
170 puts("network lpd \"Unknown\" \"LPD/LPR Host or Printer\"");
173 else if (argc
< 6 || argc
> 7)
175 fprintf(stderr
, "Usage: %s job-id user title copies options [file]\n",
181 * If we have 7 arguments, print the file named on the command-line.
182 * Otherwise, copy stdin to a temporary file and print the temporary
189 * Copy stdin to a temporary file...
192 int fd
; /* Temporary file */
193 char buffer
[8192]; /* Buffer for copying */
194 int bytes
; /* Number of bytes read */
197 if ((fd
= cupsTempFd(tmpfilename
, sizeof(tmpfilename
))) < 0)
199 perror("ERROR: unable to create temporary file");
203 while ((bytes
= fread(buffer
, 1, sizeof(buffer
), stdin
)) > 0)
204 if (write(fd
, buffer
, bytes
) < bytes
)
206 perror("ERROR: unable to write to temporary file");
213 filename
= tmpfilename
;
219 * Extract the hostname and printer name from the URI...
222 httpSeparate(argv
[0], method
, username
, hostname
, &port
, resource
);
225 * See if there are any options...
230 order
= ORDER_CONTROL_DATA
;
236 if ((options
= strchr(resource
, '?')) != NULL
)
239 * Yup, terminate the device name string and move to the first
240 * character of the options...
255 for (ptr
= name
; *options
&& *options
!= '=';)
267 for (ptr
= value
; *options
&& *options
!= '+';)
278 * Process the option...
281 if (strcasecmp(name
, "banner") == 0)
287 banner
= !value
[0] ||
288 strcasecmp(value
, "on") == 0 ||
289 strcasecmp(value
, "yes") == 0 ||
290 strcasecmp(value
, "true") == 0;
292 else if (strcasecmp(name
, "format") == 0 && value
[0])
295 * Set output format...
298 if (strchr("cdfglnoprtv", value
[0]) != NULL
)
301 fprintf(stderr
, "ERROR: Unknown format character \"%c\"\n", value
[0]);
303 else if (strcasecmp(name
, "order") == 0 && value
[0])
306 * Set control/data order...
309 if (strcasecmp(value
, "control,data") == 0)
310 order
= ORDER_CONTROL_DATA
;
311 else if (strcasecmp(value
, "data,control") == 0)
312 order
= ORDER_DATA_CONTROL
;
314 fprintf(stderr
, "ERROR: Unknown file order \"%s\"\n", value
);
316 else if (strcasecmp(name
, "reserve") == 0)
319 * Set port reservation mode...
322 reserve
= !value
[0] ||
323 strcasecmp(value
, "on") == 0 ||
324 strcasecmp(value
, "yes") == 0 ||
325 strcasecmp(value
, "true") == 0;
327 else if (strcasecmp(name
, "manual_copies") == 0)
330 * Set manual copies...
333 manual_copies
= !value
[0] ||
334 strcasecmp(value
, "on") == 0 ||
335 strcasecmp(value
, "yes") == 0 ||
336 strcasecmp(value
, "true") == 0;
338 else if (strcasecmp(name
, "sanitize_title") == 0)
341 * Set sanitize title...
344 sanitize_title
= !value
[0] ||
345 strcasecmp(value
, "on") == 0 ||
346 strcasecmp(value
, "yes") == 0 ||
347 strcasecmp(value
, "true") == 0;
349 else if (strcasecmp(name
, "timeout") == 0)
356 timeout
= atoi(value
);
362 * Sanitize the document title...
365 strlcpy(title
, argv
[3], sizeof(title
));
370 * Sanitize the title string so that we don't cause problems on
374 for (ptr
= title
; *ptr
; ptr
++)
375 if (!isalnum(*ptr
) && !isspace(*ptr
))
387 manual_copies
= atoi(argv
[4]);
393 copies
= atoi(argv
[4]);
396 status
= lpd_queue(hostname
, port
, resource
+ 1, filename
,
397 argv
[2] /* user */, title
, copies
,
398 banner
, format
, order
, reserve
, manual_copies
, timeout
);
401 fprintf(stderr
, "PAGE: 1 %d\n", atoi(argv
[4]));
404 status
= lpd_queue(hostname
, port
, resource
+ 1, filename
,
405 argv
[2] /* user */, title
, 1,
406 banner
, format
, order
, reserve
, 1, timeout
);
409 * Remove the temporary file if necessary...
416 * Return the queue status...
424 * 'lpd_command()' - Send an LPR command sequence and wait for a reply.
427 static int /* O - Status of command */
428 lpd_command(int fd
, /* I - Socket connection to LPD host */
429 int timeout
, /* I - Seconds to wait for a response */
430 char *format
, /* I - printf()-style format string */
431 ...) /* I - Additional args as necessary */
433 va_list ap
; /* Argument pointer */
434 char buf
[1024]; /* Output buffer */
435 int bytes
; /* Number of bytes to output */
436 char status
; /* Status from command */
440 * Format the string...
443 va_start(ap
, format
);
444 bytes
= vsnprintf(buf
, sizeof(buf
), format
, ap
);
447 fprintf(stderr
, "DEBUG: lpd_command %2.2x %s", buf
[0], buf
+ 1);
450 * Send the command...
453 fprintf(stderr
, "DEBUG: Sending command string (%d bytes)...\n", bytes
);
455 if (lpd_write(fd
, buf
, bytes
) < bytes
)
457 perror("ERROR: Unable to send LPD command");
462 * Read back the status from the command and return it...
465 fprintf(stderr
, "DEBUG: Reading command status...\n");
469 if (recv(fd
, &status
, 1, 0) < 1)
471 fprintf(stderr
, "WARNING: Remote host did not respond with command "
472 "status byte after %d seconds!\n", timeout
);
478 fprintf(stderr
, "DEBUG: lpd_command returning %d\n", status
);
485 * 'lpd_queue()' - Queue a file using the Line Printer Daemon protocol.
488 static int /* O - Zero on success, non-zero on failure */
489 lpd_queue(const char *hostname
, /* I - Host to connect to */
490 int port
, /* I - Port to connect on */
491 const char *printer
, /* I - Printer/queue name */
492 const char *filename
, /* I - File to print */
493 const char *user
, /* I - Requesting user */
494 const char *title
, /* I - Job title */
495 int copies
, /* I - Number of copies */
496 int banner
, /* I - Print LPD banner? */
497 int format
, /* I - Format specifier */
498 int order
, /* I - Order of data/control files */
499 int reserve
, /* I - Reserve ports? */
500 int manual_copies
, /* I - Do copies by hand... */
501 int timeout
) /* I - Timeout... */
503 FILE *fp
; /* Job file */
504 char localhost
[255]; /* Local host name */
505 int error
; /* Error number */
506 struct stat filestats
; /* File statistics */
507 int lport
; /* LPD connection local port */
508 int fd
; /* LPD socket */
509 char control
[10240], /* LPD control 'file' */
510 *cptr
; /* Pointer into control file string */
511 char status
; /* Status byte from command */
512 struct sockaddr_in addr
; /* Socket address */
513 struct hostent
*hostaddr
; /* Host address */
514 int copy
; /* Copies written */
515 size_t nbytes
, /* Number of bytes written */
516 tbytes
; /* Total bytes written */
517 char buffer
[8192]; /* Output buffer */
518 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
519 struct sigaction action
; /* Actions for POSIX signals */
520 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
524 * Setup an alarm handler for timeouts...
527 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
528 sigset(SIGALRM
, lpd_timeout
);
529 #elif defined(HAVE_SIGACTION)
530 memset(&action
, 0, sizeof(action
));
532 sigemptyset(&action
.sa_mask
);
533 action
.sa_handler
= lpd_timeout
;
534 sigaction(SIGALRM
, &action
, NULL
);
536 signal(SIGALRM
, lpd_timeout
);
537 #endif /* HAVE_SIGSET */
540 * Loop forever trying to print the file...
543 for (;;) /* FOREVER */
546 * First try to reserve a port for this connection...
549 if ((hostaddr
= httpGetHostByName(hostname
)) == NULL
)
551 fprintf(stderr
, "ERROR: Unable to locate printer \'%s\' - %s\n",
552 hostname
, hstrerror(h_errno
));
556 fprintf(stderr
, "INFO: Attempting to connect to host %s for printer %s\n",
559 memset(&addr
, 0, sizeof(addr
));
560 memcpy(&(addr
.sin_addr
), hostaddr
->h_addr
, hostaddr
->h_length
);
561 addr
.sin_family
= hostaddr
->h_addrtype
;
562 addr
.sin_port
= htons(port
);
566 if (getuid() || !reserve
)
569 * Just create a regular socket...
572 if ((fd
= socket(AF_INET
, SOCK_STREAM
, 0)) < 0)
574 perror("ERROR: Unable to create socket");
583 * We're running as root and want to comply with RFC 1179. Reserve a
584 * priviledged lport between 721 and 732...
587 if ((fd
= rresvport(&lport
)) < 0)
589 perror("ERROR: Unable to reserve port");
595 if (connect(fd
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0)
601 if (error
== ECONNREFUSED
|| error
== EHOSTDOWN
||
602 error
== EHOSTUNREACH
)
604 fprintf(stderr
, "WARNING: Network host \'%s\' is busy, down, or unreachable; will retry in 30 seconds...\n",
608 else if (error
== EADDRINUSE
)
616 perror("ERROR: Unable to connect to printer");
624 fprintf(stderr
, "INFO: Connected to %s...\n", hostname
);
625 fprintf(stderr
, "DEBUG: Connected on ports %d (local %d)...\n", port
,
629 * Next, open the print file and figure out its size...
632 if (stat(filename
, &filestats
))
634 perror("ERROR: unable to stat print file");
638 filestats
.st_size
*= manual_copies
;
640 if ((fp
= fopen(filename
, "rb")) == NULL
)
642 perror("ERROR: unable to open print file for reading");
647 * Send a job header to the printer, specifying no banner page and
651 if (lpd_command(fd
, timeout
, "\002%s\n",
652 printer
)) /* Receive print job(s) */
655 gethostname(localhost
, sizeof(localhost
));
656 localhost
[31] = '\0'; /* RFC 1179, Section 7.2 - host name < 32 chars */
658 snprintf(control
, sizeof(control
), "H%s\nP%s\nJ%s\n", localhost
, user
,
660 cptr
= control
+ strlen(control
);
664 snprintf(cptr
, sizeof(control
) - (cptr
- control
), "L%s\nC%s\n", user
,
666 cptr
+= strlen(cptr
);
671 snprintf(cptr
, sizeof(control
) - (cptr
- control
), "%cdfA%03d%s\n", format
,
672 getpid() % 1000, localhost
);
673 cptr
+= strlen(cptr
);
677 snprintf(cptr
, sizeof(control
) - (cptr
- control
),
679 getpid() % 1000, localhost
, title
);
681 fprintf(stderr
, "DEBUG: Control file is:\n%s", control
);
683 if (order
== ORDER_CONTROL_DATA
)
685 if (lpd_command(fd
, timeout
, "\002%d cfA%03.3d%s\n", strlen(control
),
686 getpid() % 1000, localhost
))
689 fprintf(stderr
, "INFO: Sending control file (%lu bytes)\n",
690 (unsigned long)strlen(control
));
692 if (lpd_write(fd
, control
, strlen(control
) + 1) < (strlen(control
) + 1))
695 perror("ERROR: Unable to write control file");
701 if (read(fd
, &status
, 1) < 1)
703 fprintf(stderr
, "WARNING: Remote host did not respond with control "
704 "status byte after %d seconds!\n", timeout
);
712 fprintf(stderr
, "ERROR: Remote host did not accept control file (%d)\n",
715 fputs("INFO: Control file sent successfully\n", stderr
);
723 * Send the print file...
726 if (lpd_command(fd
, timeout
, "\003%u dfA%03.3d%s\n",
727 (unsigned)filestats
.st_size
, getpid() % 1000,
731 fprintf(stderr
, "INFO: Sending data file (%u bytes)\n",
732 (unsigned)filestats
.st_size
);
735 for (copy
= 0; copy
< manual_copies
; copy
++)
739 while ((nbytes
= fread(buffer
, 1, sizeof(buffer
), fp
)) > 0)
741 fprintf(stderr
, "INFO: Spooling LPR job, %u%% complete...\n",
742 (unsigned)(100.0f
* tbytes
/ filestats
.st_size
));
744 if (lpd_write(fd
, buffer
, nbytes
) < nbytes
)
746 perror("ERROR: Unable to send print file to printer");
754 if (tbytes
< filestats
.st_size
)
756 else if (lpd_write(fd
, "", 1) < 1)
758 perror("ERROR: Unable to send trailing nul to printer");
764 * Read the status byte from the printer; if we can't read the byte
765 * back now, we should set status to "errno", however at this point
766 * we know the printer got the whole file and we don't necessarily
767 * want to requeue it over and over...
772 if (recv(fd
, &status
, 1, 0) < 1)
774 fprintf(stderr
, "WARNING: Remote host did not respond with data "
775 "status byte after %d seconds!\n", timeout
);
783 fprintf(stderr
, "ERROR: Remote host did not accept data file (%d)\n",
786 fputs("INFO: Data file sent successfully\n", stderr
);
789 if (status
== 0 && order
== ORDER_DATA_CONTROL
)
791 if (lpd_command(fd
, timeout
, "\002%d cfA%03.3d%s\n", strlen(control
),
792 getpid() % 1000, localhost
))
795 fprintf(stderr
, "INFO: Sending control file (%lu bytes)\n",
796 (unsigned long)strlen(control
));
798 if (lpd_write(fd
, control
, strlen(control
) + 1) < (strlen(control
) + 1))
801 perror("ERROR: Unable to write control file");
807 if (read(fd
, &status
, 1) < 1)
809 fprintf(stderr
, "WARNING: Remote host did not respond with control "
810 "status byte after %d seconds!\n", timeout
);
818 fprintf(stderr
, "ERROR: Remote host did not accept control file (%d)\n",
821 fputs("INFO: Control file sent successfully\n", stderr
);
825 * Close the socket connection and input file...
835 * Waiting for a retry...
844 * 'lpd_timeout()' - Handle timeout alarms...
848 lpd_timeout(int sig
) /* I - Signal number */
852 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
853 signal(SIGALRM
, lpd_timeout
);
854 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
859 * 'lpd_write()' - Write a buffer of data to an LPD server.
862 static int /* O - Number of bytes written or -1 on error */
863 lpd_write(int lpd_fd
, /* I - LPD socket */
864 char *buffer
, /* I - Buffer to write */
865 int length
) /* I - Number of bytes to write */
867 int bytes
, /* Number of bytes written */
868 total
; /* Total number of bytes written */
872 while ((bytes
= send(lpd_fd
, buffer
, length
- total
, 0)) >= 0)
888 #ifndef HAVE_RRESVPORT
890 * 'rresvport()' - A simple implementation of rresvport().
893 int /* O - Socket or -1 on error */
894 rresvport(int *port
) /* IO - Port number to bind to */
896 struct sockaddr_in addr
; /* Socket address */
897 int fd
; /* Socket file descriptor */
901 * Try to create an IPv4 socket...
904 if ((fd
= socket(AF_INET
, SOCK_STREAM
, 0)) < 0)
908 * Initialize the address buffer...
911 memset(&addr
, 0, sizeof(addr
));
913 addr
.sin_family
= AF_INET
;
914 addr
.sin_addr
.s_addr
= INADDR_ANY
;
917 * Try to bind the socket to a reserved port; unlike the standard
918 * BSD rresvport(), we limit the port number to 721 through 732
919 * (instead of 512 to 1023) since RFC 1179 defines the local port
920 * number between 721 and 732...
926 * Set the port number...
929 addr
.sin_port
= htons(*port
);
932 * Try binding the port to the socket; return if all is OK...
935 if (!bind(fd
, (struct sockaddr
*)&addr
, sizeof(addr
)))
939 * Stop if we have any error other than "address already in use"...
942 if (errno
!= EADDRINUSE
)
954 * Try the next port...
961 * Wasn't able to bind to a reserved port, so close the socket and return
973 #endif /* !HAVE_RRESVPORT */
977 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
981 sigterm_handler(int sig
) /* I - Signal */
983 (void)sig
; /* remove compiler warnings... */
986 * Remove the temporary file if necessary...
997 * End of "$Id: lpd.c,v 1.54 2003/10/09 19:13:27 mike Exp $".