]>
Commit | Line | Data |
---|---|---|
43e2fc22 | 1 | /* |
2d417cb3 | 2 | * "$Id: lpd.c,v 1.28.2.25 2003/10/09 19:13:48 mike Exp $" |
43e2fc22 | 3 | * |
e73c6c0a | 4 | * Line Printer Daemon backend for the Common UNIX Printing System (CUPS). |
43e2fc22 | 5 | * |
1d9595ab | 6 | * Copyright 1997-2003 by Easy Software Products, all rights reserved. |
43e2fc22 | 7 | * |
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 | |
13 | * at: | |
14 | * | |
15 | * Attn: CUPS Licensing Information | |
16 | * Easy Software Products | |
8784b6a6 | 17 | * 44141 Airport View Drive, Suite 204 |
43e2fc22 | 18 | * Hollywood, Maryland 20636-3111 USA |
19 | * | |
20 | * Voice: (301) 373-9603 | |
21 | * EMail: cups-info@cups.org | |
22 | * WWW: http://www.cups.org | |
23 | * | |
dab1a4d8 | 24 | * This file is subject to the Apple OS-Developed Software exception. |
25 | * | |
43e2fc22 | 26 | * Contents: |
27 | * | |
55737cf0 | 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. | |
e73c6c0a | 35 | */ |
36 | ||
37 | /* | |
38 | * Include necessary headers. | |
39 | */ | |
40 | ||
41 | #include <cups/cups.h> | |
42 | #include <stdio.h> | |
43 | #include <stdlib.h> | |
44 | #include <stdarg.h> | |
8056639c | 45 | #include <ctype.h> |
2d417cb3 | 46 | #include <cups/http-private.h> |
e73c6c0a | 47 | #include <cups/string.h> |
48 | #include <errno.h> | |
49 | #include <sys/types.h> | |
50 | #include <sys/stat.h> | |
4ff40357 | 51 | #include <signal.h> |
e73c6c0a | 52 | |
c3026ddc | 53 | #ifdef WIN32 |
e73c6c0a | 54 | # include <winsock.h> |
55 | #else | |
56 | # include <sys/socket.h> | |
57 | # include <netinet/in.h> | |
58 | # include <arpa/inet.h> | |
59 | # include <netdb.h> | |
c3026ddc | 60 | #endif /* WIN32 */ |
e73c6c0a | 61 | |
55d0f521 | 62 | |
55737cf0 | 63 | /* |
64 | * Globals... | |
65 | */ | |
66 | ||
67 | static char tmpfilename[1024] = ""; /* Temporary spool file name */ | |
68 | ||
69 | ||
f8529e1a | 70 | /* |
71 | * The order for control and data files in LPD requests... | |
72 | */ | |
73 | ||
74 | #define ORDER_CONTROL_DATA 0 | |
75 | #define ORDER_DATA_CONTROL 1 | |
76 | ||
77 | ||
55d0f521 | 78 | /* |
79 | * It appears that rresvport() is never declared on most systems... | |
80 | */ | |
81 | ||
82 | extern int rresvport(int *port); | |
e73c6c0a | 83 | |
84 | ||
85 | /* | |
86 | * Local functions... | |
87 | */ | |
88 | ||
55737cf0 | 89 | static int lpd_command(int lpd_fd, int timeout, char *format, ...); |
c50b7b12 | 90 | static int lpd_queue(const char *hostname, int port, const char *printer, |
91 | const char *filename, | |
92 | const char *user, const char *title, int copies, | |
04e01993 | 93 | int banner, int format, int order, int reserve, |
55737cf0 | 94 | int manual_copies, int timeout); |
25037b2b | 95 | static void lpd_timeout(int sig); |
f8529e1a | 96 | static int lpd_write(int lpd_fd, char *buffer, int length); |
55737cf0 | 97 | static void sigterm_handler(int sig); |
e73c6c0a | 98 | |
99 | ||
100 | /* | |
101 | * 'main()' - Send a file to the printer or server. | |
43e2fc22 | 102 | * |
e73c6c0a | 103 | * Usage: |
43e2fc22 | 104 | * |
e73c6c0a | 105 | * printer-uri job-id user title copies options [file] |
43e2fc22 | 106 | */ |
107 | ||
55737cf0 | 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 */ | |
e73c6c0a | 111 | { |
55737cf0 | 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 */ | |
73c53f5d | 132 | #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) |
55737cf0 | 133 | struct sigaction action; /* Actions for POSIX signals */ |
73c53f5d | 134 | #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ |
e73c6c0a | 135 | |
136 | ||
4b23f3b3 | 137 | /* |
138 | * Make sure status messages are not buffered... | |
139 | */ | |
140 | ||
141 | setbuf(stderr, NULL); | |
142 | ||
98904cd6 | 143 | /* |
55737cf0 | 144 | * Ignore SIGPIPE and catch SIGTERM signals... |
98904cd6 | 145 | */ |
146 | ||
147 | #ifdef HAVE_SIGSET | |
148 | sigset(SIGPIPE, SIG_IGN); | |
55737cf0 | 149 | sigset(SIGTERM, sigterm_handler); |
98904cd6 | 150 | #elif defined(HAVE_SIGACTION) |
151 | memset(&action, 0, sizeof(action)); | |
152 | action.sa_handler = SIG_IGN; | |
153 | sigaction(SIGPIPE, &action, NULL); | |
55737cf0 | 154 | |
155 | sigemptyset(&action.sa_mask); | |
156 | sigaddset(&action.sa_mask, SIGTERM); | |
157 | action.sa_handler = sigterm_handler; | |
158 | sigaction(SIGTERM, &action, NULL); | |
98904cd6 | 159 | #else |
160 | signal(SIGPIPE, SIG_IGN); | |
55737cf0 | 161 | signal(SIGTERM, sigterm_handler); |
98904cd6 | 162 | #endif /* HAVE_SIGSET */ |
163 | ||
4b23f3b3 | 164 | /* |
165 | * Check command-line... | |
166 | */ | |
167 | ||
68edc300 | 168 | if (argc == 1) |
169 | { | |
d4c438d4 | 170 | puts("network lpd \"Unknown\" \"LPD/LPR Host or Printer\""); |
68edc300 | 171 | return (0); |
172 | } | |
173 | else if (argc < 6 || argc > 7) | |
e73c6c0a | 174 | { |
175 | fprintf(stderr, "Usage: %s job-id user title copies options [file]\n", | |
176 | argv[0]); | |
177 | return (1); | |
178 | } | |
179 | ||
180 | /* | |
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 | |
183 | * file. | |
184 | */ | |
185 | ||
186 | if (argc == 6) | |
187 | { | |
188 | /* | |
189 | * Copy stdin to a temporary file... | |
190 | */ | |
191 | ||
1b5bf964 | 192 | int fd; /* Temporary file */ |
e73c6c0a | 193 | char buffer[8192]; /* Buffer for copying */ |
194 | int bytes; /* Number of bytes read */ | |
195 | ||
196 | ||
55737cf0 | 197 | if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0) |
e73c6c0a | 198 | { |
c8f9565c | 199 | perror("ERROR: unable to create temporary file"); |
e73c6c0a | 200 | return (1); |
201 | } | |
202 | ||
203 | while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0) | |
1b5bf964 | 204 | if (write(fd, buffer, bytes) < bytes) |
e73c6c0a | 205 | { |
c8f9565c | 206 | perror("ERROR: unable to write to temporary file"); |
1b5bf964 | 207 | close(fd); |
55737cf0 | 208 | unlink(tmpfilename); |
e73c6c0a | 209 | return (1); |
210 | } | |
211 | ||
1b5bf964 | 212 | close(fd); |
55737cf0 | 213 | filename = tmpfilename; |
e73c6c0a | 214 | } |
215 | else | |
55737cf0 | 216 | filename = argv[6]; |
e73c6c0a | 217 | |
218 | /* | |
219 | * Extract the hostname and printer name from the URI... | |
220 | */ | |
221 | ||
222 | httpSeparate(argv[0], method, username, hostname, &port, resource); | |
223 | ||
72a039d0 | 224 | /* |
225 | * See if there are any options... | |
226 | */ | |
227 | ||
55737cf0 | 228 | banner = 0; |
229 | format = 'l'; | |
230 | order = ORDER_CONTROL_DATA; | |
231 | reserve = 0; | |
232 | manual_copies = 1; | |
233 | timeout = 300; | |
234 | sanitize_title = 1; | |
72a039d0 | 235 | |
236 | if ((options = strchr(resource, '?')) != NULL) | |
237 | { | |
238 | /* | |
239 | * Yup, terminate the device name string and move to the first | |
240 | * character of the options... | |
241 | */ | |
242 | ||
243 | *options++ = '\0'; | |
244 | ||
245 | /* | |
246 | * Parse options... | |
247 | */ | |
248 | ||
249 | while (*options) | |
250 | { | |
251 | /* | |
252 | * Get the name... | |
253 | */ | |
254 | ||
255 | for (ptr = name; *options && *options != '=';) | |
256 | *ptr++ = *options++; | |
257 | *ptr = '\0'; | |
258 | ||
259 | if (*options == '=') | |
260 | { | |
261 | /* | |
262 | * Get the value... | |
263 | */ | |
264 | ||
265 | options ++; | |
266 | ||
267 | for (ptr = value; *options && *options != '+';) | |
268 | *ptr++ = *options++; | |
269 | *ptr = '\0'; | |
270 | ||
271 | if (*options == '+') | |
272 | options ++; | |
273 | } | |
274 | else | |
275 | value[0] = '\0'; | |
276 | ||
277 | /* | |
278 | * Process the option... | |
279 | */ | |
280 | ||
281 | if (strcasecmp(name, "banner") == 0) | |
282 | { | |
283 | /* | |
284 | * Set the banner... | |
285 | */ | |
286 | ||
287 | banner = !value[0] || | |
288 | strcasecmp(value, "on") == 0 || | |
289 | strcasecmp(value, "yes") == 0 || | |
290 | strcasecmp(value, "true") == 0; | |
291 | } | |
292 | else if (strcasecmp(name, "format") == 0 && value[0]) | |
293 | { | |
294 | /* | |
295 | * Set output format... | |
296 | */ | |
297 | ||
298 | if (strchr("cdfglnoprtv", value[0]) != NULL) | |
299 | format = value[0]; | |
300 | else | |
301 | fprintf(stderr, "ERROR: Unknown format character \"%c\"\n", value[0]); | |
302 | } | |
f8529e1a | 303 | else if (strcasecmp(name, "order") == 0 && value[0]) |
304 | { | |
305 | /* | |
306 | * Set control/data order... | |
307 | */ | |
308 | ||
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; | |
313 | else | |
314 | fprintf(stderr, "ERROR: Unknown file order \"%s\"\n", value); | |
315 | } | |
9ba7fc54 | 316 | else if (strcasecmp(name, "reserve") == 0) |
317 | { | |
318 | /* | |
319 | * Set port reservation mode... | |
320 | */ | |
321 | ||
322 | reserve = !value[0] || | |
323 | strcasecmp(value, "on") == 0 || | |
324 | strcasecmp(value, "yes") == 0 || | |
325 | strcasecmp(value, "true") == 0; | |
326 | } | |
04e01993 | 327 | else if (strcasecmp(name, "manual_copies") == 0) |
328 | { | |
329 | /* | |
55737cf0 | 330 | * Set manual copies... |
04e01993 | 331 | */ |
332 | ||
333 | manual_copies = !value[0] || | |
334 | strcasecmp(value, "on") == 0 || | |
335 | strcasecmp(value, "yes") == 0 || | |
336 | strcasecmp(value, "true") == 0; | |
337 | } | |
55737cf0 | 338 | else if (strcasecmp(name, "sanitize_title") == 0) |
339 | { | |
340 | /* | |
341 | * Set sanitize title... | |
342 | */ | |
343 | ||
344 | sanitize_title = !value[0] || | |
345 | strcasecmp(value, "on") == 0 || | |
346 | strcasecmp(value, "yes") == 0 || | |
347 | strcasecmp(value, "true") == 0; | |
348 | } | |
349 | else if (strcasecmp(name, "timeout") == 0) | |
350 | { | |
351 | /* | |
352 | * Set the timeout... | |
353 | */ | |
354 | ||
355 | if (atoi(value) > 0) | |
356 | timeout = atoi(value); | |
357 | } | |
72a039d0 | 358 | } |
359 | } | |
360 | ||
8056639c | 361 | /* |
362 | * Sanitize the document title... | |
363 | */ | |
364 | ||
def978d5 | 365 | strlcpy(title, argv[3], sizeof(title)); |
8056639c | 366 | |
55737cf0 | 367 | if (sanitize_title) |
368 | { | |
369 | /* | |
370 | * Sanitize the title string so that we don't cause problems on | |
371 | * the remote end... | |
372 | */ | |
373 | ||
374 | for (ptr = title; *ptr; ptr ++) | |
375 | if (!isalnum(*ptr) && !isspace(*ptr)) | |
376 | *ptr = '_'; | |
377 | } | |
8056639c | 378 | |
e73c6c0a | 379 | /* |
380 | * Queue the job... | |
381 | */ | |
382 | ||
3f9cb6c6 | 383 | if (argc > 6) |
384 | { | |
04e01993 | 385 | if (manual_copies) |
386 | { | |
387 | manual_copies = atoi(argv[4]); | |
388 | copies = 1; | |
389 | } | |
390 | else | |
391 | { | |
392 | manual_copies = 1; | |
393 | copies = atoi(argv[4]); | |
394 | } | |
395 | ||
c50b7b12 | 396 | status = lpd_queue(hostname, port, resource + 1, filename, |
04e01993 | 397 | argv[2] /* user */, title, copies, |
55737cf0 | 398 | banner, format, order, reserve, manual_copies, timeout); |
3f9cb6c6 | 399 | |
400 | if (!status) | |
401 | fprintf(stderr, "PAGE: 1 %d\n", atoi(argv[4])); | |
402 | } | |
403 | else | |
c50b7b12 | 404 | status = lpd_queue(hostname, port, resource + 1, filename, |
9ba7fc54 | 405 | argv[2] /* user */, title, 1, |
55737cf0 | 406 | banner, format, order, reserve, 1, timeout); |
e73c6c0a | 407 | |
408 | /* | |
409 | * Remove the temporary file if necessary... | |
410 | */ | |
411 | ||
55737cf0 | 412 | if (tmpfilename[0]) |
413 | unlink(tmpfilename); | |
e73c6c0a | 414 | |
415 | /* | |
416 | * Return the queue status... | |
417 | */ | |
418 | ||
419 | return (status); | |
420 | } | |
421 | ||
422 | ||
43e2fc22 | 423 | /* |
e73c6c0a | 424 | * 'lpd_command()' - Send an LPR command sequence and wait for a reply. |
43e2fc22 | 425 | */ |
426 | ||
e73c6c0a | 427 | static int /* O - Status of command */ |
428 | lpd_command(int fd, /* I - Socket connection to LPD host */ | |
55737cf0 | 429 | int timeout, /* I - Seconds to wait for a response */ |
e73c6c0a | 430 | char *format, /* I - printf()-style format string */ |
431 | ...) /* I - Additional args as necessary */ | |
432 | { | |
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 */ | |
437 | ||
438 | ||
439 | /* | |
440 | * Format the string... | |
441 | */ | |
442 | ||
443 | va_start(ap, format); | |
6a536282 | 444 | bytes = vsnprintf(buf, sizeof(buf), format, ap); |
e73c6c0a | 445 | va_end(ap); |
446 | ||
d21a7597 | 447 | fprintf(stderr, "DEBUG: lpd_command %2.2x %s", buf[0], buf + 1); |
e73c6c0a | 448 | |
449 | /* | |
450 | * Send the command... | |
451 | */ | |
452 | ||
85e96ec0 | 453 | fprintf(stderr, "DEBUG: Sending command string (%d bytes)...\n", bytes); |
454 | ||
f8529e1a | 455 | if (lpd_write(fd, buf, bytes) < bytes) |
55737cf0 | 456 | { |
457 | perror("ERROR: Unable to send LPD command"); | |
e73c6c0a | 458 | return (-1); |
55737cf0 | 459 | } |
e73c6c0a | 460 | |
461 | /* | |
462 | * Read back the status from the command and return it... | |
463 | */ | |
464 | ||
85e96ec0 | 465 | fprintf(stderr, "DEBUG: Reading command status...\n"); |
466 | ||
55737cf0 | 467 | alarm(timeout); |
25037b2b | 468 | |
e73c6c0a | 469 | if (recv(fd, &status, 1, 0) < 1) |
55737cf0 | 470 | { |
471 | fprintf(stderr, "WARNING: Remote host did not respond with command " | |
472 | "status byte after %d seconds!\n", timeout); | |
1aef57ed | 473 | status = errno; |
55737cf0 | 474 | } |
e73c6c0a | 475 | |
25037b2b | 476 | alarm(0); |
477 | ||
c8f9565c | 478 | fprintf(stderr, "DEBUG: lpd_command returning %d\n", status); |
e73c6c0a | 479 | |
480 | return (status); | |
481 | } | |
482 | ||
483 | ||
484 | /* | |
485 | * 'lpd_queue()' - Queue a file using the Line Printer Daemon protocol. | |
486 | */ | |
487 | ||
55737cf0 | 488 | static int /* O - Zero on success, non-zero on failure */ |
c50b7b12 | 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... */ | |
e73c6c0a | 502 | { |
503 | FILE *fp; /* Job file */ | |
504 | char localhost[255]; /* Local host name */ | |
505 | int error; /* Error number */ | |
506 | struct stat filestats; /* File statistics */ | |
c50b7b12 | 507 | int lport; /* LPD connection local port */ |
e73c6c0a | 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 */ | |
38dc9124 | 514 | int copy; /* Copies written */ |
e73c6c0a | 515 | size_t nbytes, /* Number of bytes written */ |
516 | tbytes; /* Total bytes written */ | |
517 | char buffer[8192]; /* Output buffer */ | |
4ff40357 | 518 | #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) |
519 | struct sigaction action; /* Actions for POSIX signals */ | |
520 | #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ | |
e73c6c0a | 521 | |
522 | ||
1aef57ed | 523 | /* |
524 | * Setup an alarm handler for timeouts... | |
525 | */ | |
526 | ||
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)); | |
e73c6c0a | 531 | |
1aef57ed | 532 | sigemptyset(&action.sa_mask); |
533 | action.sa_handler = lpd_timeout; | |
534 | sigaction(SIGALRM, &action, NULL); | |
535 | #else | |
536 | signal(SIGALRM, lpd_timeout); | |
537 | #endif /* HAVE_SIGSET */ | |
538 | ||
539 | /* | |
540 | * Loop forever trying to print the file... | |
541 | */ | |
542 | ||
543 | for (;;) /* FOREVER */ | |
e73c6c0a | 544 | { |
25037b2b | 545 | /* |
546 | * First try to reserve a port for this connection... | |
547 | */ | |
e73c6c0a | 548 | |
25037b2b | 549 | if ((hostaddr = httpGetHostByName(hostname)) == NULL) |
550 | { | |
8314b40b | 551 | fprintf(stderr, "ERROR: Unable to locate printer \'%s\' - %s\n", |
7e3ba0af | 552 | hostname, hstrerror(h_errno)); |
25037b2b | 553 | return (1); |
554 | } | |
e73c6c0a | 555 | |
25037b2b | 556 | fprintf(stderr, "INFO: Attempting to connect to host %s for printer %s\n", |
557 | hostname, printer); | |
e73c6c0a | 558 | |
25037b2b | 559 | memset(&addr, 0, sizeof(addr)); |
560 | memcpy(&(addr.sin_addr), hostaddr->h_addr, hostaddr->h_length); | |
561 | addr.sin_family = hostaddr->h_addrtype; | |
c50b7b12 | 562 | addr.sin_port = htons(port); |
25037b2b | 563 | |
c50b7b12 | 564 | for (lport = 732;;) |
55d0f521 | 565 | { |
25037b2b | 566 | if (getuid() || !reserve) |
567 | { | |
568 | /* | |
569 | * Just create a regular socket... | |
570 | */ | |
55d0f521 | 571 | |
25037b2b | 572 | if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
573 | { | |
574 | perror("ERROR: Unable to create socket"); | |
575 | return (1); | |
576 | } | |
1aef57ed | 577 | |
c50b7b12 | 578 | lport = 0; |
25037b2b | 579 | } |
580 | else | |
55d0f521 | 581 | { |
25037b2b | 582 | /* |
583 | * We're running as root and want to comply with RFC 1179. Reserve a | |
c50b7b12 | 584 | * priviledged lport between 721 and 732... |
25037b2b | 585 | */ |
586 | ||
c50b7b12 | 587 | if ((fd = rresvport(&lport)) < 0) |
25037b2b | 588 | { |
589 | perror("ERROR: Unable to reserve port"); | |
590 | sleep(30); | |
591 | continue; | |
592 | } | |
55d0f521 | 593 | } |
55d0f521 | 594 | |
25037b2b | 595 | if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) |
55d0f521 | 596 | { |
25037b2b | 597 | error = errno; |
598 | close(fd); | |
599 | fd = -1; | |
600 | ||
601 | if (error == ECONNREFUSED || error == EHOSTDOWN || | |
602 | error == EHOSTUNREACH) | |
603 | { | |
c46f2cfe | 604 | fprintf(stderr, "WARNING: Network host \'%s\' is busy, down, or unreachable; will retry in 30 seconds...\n", |
25037b2b | 605 | hostname); |
606 | sleep(30); | |
607 | } | |
608 | else if (error == EADDRINUSE) | |
609 | { | |
610 | port --; | |
611 | if (port < 721) | |
612 | port = 732; | |
613 | } | |
614 | else | |
615 | { | |
616 | perror("ERROR: Unable to connect to printer"); | |
617 | sleep(30); | |
618 | } | |
55d0f521 | 619 | } |
25037b2b | 620 | else |
621 | break; | |
e73c6c0a | 622 | } |
623 | ||
c50b7b12 | 624 | fprintf(stderr, "INFO: Connected to %s...\n", hostname); |
625 | fprintf(stderr, "DEBUG: Connected on ports %d (local %d)...\n", port, | |
626 | lport); | |
25037b2b | 627 | |
25037b2b | 628 | /* |
629 | * Next, open the print file and figure out its size... | |
630 | */ | |
e73c6c0a | 631 | |
25037b2b | 632 | if (stat(filename, &filestats)) |
633 | { | |
634 | perror("ERROR: unable to stat print file"); | |
635 | return (1); | |
636 | } | |
e73c6c0a | 637 | |
25037b2b | 638 | filestats.st_size *= manual_copies; |
04e01993 | 639 | |
25037b2b | 640 | if ((fp = fopen(filename, "rb")) == NULL) |
641 | { | |
642 | perror("ERROR: unable to open print file for reading"); | |
643 | return (1); | |
644 | } | |
e73c6c0a | 645 | |
25037b2b | 646 | /* |
647 | * Send a job header to the printer, specifying no banner page and | |
648 | * literal output... | |
649 | */ | |
e73c6c0a | 650 | |
55737cf0 | 651 | if (lpd_command(fd, timeout, "\002%s\n", |
652 | printer)) /* Receive print job(s) */ | |
653 | return (1); | |
e73c6c0a | 654 | |
25037b2b | 655 | gethostname(localhost, sizeof(localhost)); |
656 | localhost[31] = '\0'; /* RFC 1179, Section 7.2 - host name < 32 chars */ | |
e73c6c0a | 657 | |
82ba0702 | 658 | snprintf(control, sizeof(control), "H%s\nP%s\nJ%s\n", localhost, user, |
659 | title); | |
25037b2b | 660 | cptr = control + strlen(control); |
e73c6c0a | 661 | |
25037b2b | 662 | if (banner) |
663 | { | |
82ba0702 | 664 | snprintf(cptr, sizeof(control) - (cptr - control), "L%s\nC%s\n", user, |
665 | localhost); | |
25037b2b | 666 | cptr += strlen(cptr); |
667 | } | |
72a039d0 | 668 | |
25037b2b | 669 | while (copies > 0) |
670 | { | |
671 | snprintf(cptr, sizeof(control) - (cptr - control), "%cdfA%03d%s\n", format, | |
672 | getpid() % 1000, localhost); | |
673 | cptr += strlen(cptr); | |
674 | copies --; | |
675 | } | |
e73c6c0a | 676 | |
25037b2b | 677 | snprintf(cptr, sizeof(control) - (cptr - control), |
854d3bb2 | 678 | "UdfA%03d%s\nN%s\n", |
679 | getpid() % 1000, localhost, title); | |
e73c6c0a | 680 | |
25037b2b | 681 | fprintf(stderr, "DEBUG: Control file is:\n%s", control); |
e73c6c0a | 682 | |
25037b2b | 683 | if (order == ORDER_CONTROL_DATA) |
684 | { | |
55737cf0 | 685 | if (lpd_command(fd, timeout, "\002%d cfA%03.3d%s\n", strlen(control), |
686 | getpid() % 1000, localhost)) | |
687 | return (1); | |
f8529e1a | 688 | |
1aef57ed | 689 | fprintf(stderr, "INFO: Sending control file (%lu bytes)\n", |
690 | (unsigned long)strlen(control)); | |
e73c6c0a | 691 | |
25037b2b | 692 | if (lpd_write(fd, control, strlen(control) + 1) < (strlen(control) + 1)) |
693 | { | |
694 | status = errno; | |
695 | perror("ERROR: Unable to write control file"); | |
696 | } | |
697 | else | |
698 | { | |
55737cf0 | 699 | alarm(timeout); |
f8529e1a | 700 | |
25037b2b | 701 | if (read(fd, &status, 1) < 1) |
55737cf0 | 702 | { |
703 | fprintf(stderr, "WARNING: Remote host did not respond with control " | |
704 | "status byte after %d seconds!\n", timeout); | |
25037b2b | 705 | status = errno; |
55737cf0 | 706 | } |
e73c6c0a | 707 | |
25037b2b | 708 | alarm(0); |
709 | } | |
e73c6c0a | 710 | |
25037b2b | 711 | if (status != 0) |
712 | fprintf(stderr, "ERROR: Remote host did not accept control file (%d)\n", | |
713 | status); | |
714 | else | |
715 | fputs("INFO: Control file sent successfully\n", stderr); | |
716 | } | |
717 | else | |
718 | status = 0; | |
e73c6c0a | 719 | |
25037b2b | 720 | if (status == 0) |
e73c6c0a | 721 | { |
25037b2b | 722 | /* |
723 | * Send the print file... | |
724 | */ | |
e73c6c0a | 725 | |
55737cf0 | 726 | if (lpd_command(fd, timeout, "\003%u dfA%03.3d%s\n", |
727 | (unsigned)filestats.st_size, getpid() % 1000, | |
728 | localhost)) | |
729 | return (1); | |
25037b2b | 730 | |
731 | fprintf(stderr, "INFO: Sending data file (%u bytes)\n", | |
732 | (unsigned)filestats.st_size); | |
733 | ||
734 | tbytes = 0; | |
38dc9124 | 735 | for (copy = 0; copy < manual_copies; copy ++) |
e73c6c0a | 736 | { |
25037b2b | 737 | rewind(fp); |
04e01993 | 738 | |
25037b2b | 739 | while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) |
04e01993 | 740 | { |
25037b2b | 741 | fprintf(stderr, "INFO: Spooling LPR job, %u%% complete...\n", |
742 | (unsigned)(100.0f * tbytes / filestats.st_size)); | |
743 | ||
744 | if (lpd_write(fd, buffer, nbytes) < nbytes) | |
745 | { | |
746 | perror("ERROR: Unable to send print file to printer"); | |
747 | break; | |
748 | } | |
749 | else | |
750 | tbytes += nbytes; | |
04e01993 | 751 | } |
e73c6c0a | 752 | } |
04e01993 | 753 | |
25037b2b | 754 | if (tbytes < filestats.st_size) |
755 | status = errno; | |
756 | else if (lpd_write(fd, "", 1) < 1) | |
55737cf0 | 757 | { |
758 | perror("ERROR: Unable to send trailing nul to printer"); | |
25037b2b | 759 | status = errno; |
55737cf0 | 760 | } |
25037b2b | 761 | else |
762 | { | |
55737cf0 | 763 | /* |
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... | |
768 | */ | |
769 | ||
770 | alarm(timeout); | |
25037b2b | 771 | |
772 | if (recv(fd, &status, 1, 0) < 1) | |
55737cf0 | 773 | { |
774 | fprintf(stderr, "WARNING: Remote host did not respond with data " | |
775 | "status byte after %d seconds!\n", timeout); | |
776 | status = 0; | |
777 | } | |
25037b2b | 778 | |
779 | alarm(0); | |
780 | } | |
781 | ||
782 | if (status != 0) | |
783 | fprintf(stderr, "ERROR: Remote host did not accept data file (%d)\n", | |
784 | status); | |
785 | else | |
786 | fputs("INFO: Data file sent successfully\n", stderr); | |
e73c6c0a | 787 | } |
788 | ||
25037b2b | 789 | if (status == 0 && order == ORDER_DATA_CONTROL) |
790 | { | |
55737cf0 | 791 | if (lpd_command(fd, timeout, "\002%d cfA%03.3d%s\n", strlen(control), |
792 | getpid() % 1000, localhost)) | |
793 | return (1); | |
f8529e1a | 794 | |
1aef57ed | 795 | fprintf(stderr, "INFO: Sending control file (%lu bytes)\n", |
796 | (unsigned long)strlen(control)); | |
e73c6c0a | 797 | |
25037b2b | 798 | if (lpd_write(fd, control, strlen(control) + 1) < (strlen(control) + 1)) |
799 | { | |
800 | status = errno; | |
801 | perror("ERROR: Unable to write control file"); | |
802 | } | |
803 | else | |
804 | { | |
55737cf0 | 805 | alarm(timeout); |
f8529e1a | 806 | |
55737cf0 | 807 | if (read(fd, &status, 1) < 1) |
808 | { | |
809 | fprintf(stderr, "WARNING: Remote host did not respond with control " | |
810 | "status byte after %d seconds!\n", timeout); | |
25037b2b | 811 | status = errno; |
55737cf0 | 812 | } |
25037b2b | 813 | |
814 | alarm(0); | |
815 | } | |
816 | ||
817 | if (status != 0) | |
818 | fprintf(stderr, "ERROR: Remote host did not accept control file (%d)\n", | |
819 | status); | |
820 | else | |
821 | fputs("INFO: Control file sent successfully\n", stderr); | |
f8529e1a | 822 | } |
f8529e1a | 823 | |
25037b2b | 824 | /* |
825 | * Close the socket connection and input file... | |
826 | */ | |
827 | ||
828 | close(fd); | |
829 | fclose(fp); | |
830 | ||
831 | if (status == 0) | |
1aef57ed | 832 | return (0); |
f8529e1a | 833 | |
1aef57ed | 834 | /* |
55737cf0 | 835 | * Waiting for a retry... |
1aef57ed | 836 | */ |
e73c6c0a | 837 | |
1aef57ed | 838 | sleep(30); |
839 | } | |
25037b2b | 840 | } |
841 | ||
842 | ||
843 | /* | |
844 | * 'lpd_timeout()' - Handle timeout alarms... | |
845 | */ | |
846 | ||
847 | static void | |
55737cf0 | 848 | lpd_timeout(int sig) /* I - Signal number */ |
25037b2b | 849 | { |
850 | (void)sig; | |
851 | ||
852 | #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION) | |
853 | signal(SIGALRM, lpd_timeout); | |
854 | #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */ | |
e73c6c0a | 855 | } |
43e2fc22 | 856 | |
857 | ||
858 | /* | |
f8529e1a | 859 | * 'lpd_write()' - Write a buffer of data to an LPD server. |
860 | */ | |
861 | ||
55737cf0 | 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 */ | |
f8529e1a | 866 | { |
55737cf0 | 867 | int bytes, /* Number of bytes written */ |
868 | total; /* Total number of bytes written */ | |
f8529e1a | 869 | |
870 | ||
871 | total = 0; | |
872 | while ((bytes = send(lpd_fd, buffer, length - total, 0)) >= 0) | |
873 | { | |
874 | total += bytes; | |
875 | buffer += bytes; | |
876 | ||
877 | if (total == length) | |
878 | break; | |
879 | } | |
880 | ||
881 | if (bytes < 0) | |
882 | return (-1); | |
883 | else | |
884 | return (length); | |
885 | } | |
886 | ||
887 | ||
c3026ddc | 888 | #ifndef HAVE_RRESVPORT |
f8529e1a | 889 | /* |
c3026ddc | 890 | * 'rresvport()' - A simple implementation of rresvport(). |
891 | */ | |
892 | ||
55737cf0 | 893 | int /* O - Socket or -1 on error */ |
894 | rresvport(int *port) /* IO - Port number to bind to */ | |
c3026ddc | 895 | { |
55737cf0 | 896 | struct sockaddr_in addr; /* Socket address */ |
897 | int fd; /* Socket file descriptor */ | |
c3026ddc | 898 | |
899 | ||
900 | /* | |
901 | * Try to create an IPv4 socket... | |
902 | */ | |
903 | ||
904 | if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) | |
905 | return (-1); | |
906 | ||
907 | /* | |
908 | * Initialize the address buffer... | |
909 | */ | |
910 | ||
911 | memset(&addr, 0, sizeof(addr)); | |
912 | ||
913 | addr.sin_family = AF_INET; | |
914 | addr.sin_addr.s_addr = INADDR_ANY; | |
915 | ||
916 | /* | |
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... | |
921 | */ | |
922 | ||
923 | while (*port > 720) | |
924 | { | |
925 | /* | |
926 | * Set the port number... | |
927 | */ | |
928 | ||
929 | addr.sin_port = htons(*port); | |
930 | ||
931 | /* | |
932 | * Try binding the port to the socket; return if all is OK... | |
933 | */ | |
934 | ||
935 | if (!bind(fd, (struct sockaddr *)&addr, sizeof(addr))) | |
936 | return (fd); | |
937 | ||
938 | /* | |
939 | * Stop if we have any error other than "address already in use"... | |
940 | */ | |
941 | ||
942 | if (errno != EADDRINUSE) | |
943 | { | |
944 | # ifdef WIN32 | |
945 | closesocket(fd); | |
946 | # else | |
947 | close(fd); | |
948 | # endif /* WIN32 */ | |
949 | ||
950 | return (-1); | |
951 | } | |
952 | ||
953 | /* | |
954 | * Try the next port... | |
955 | */ | |
956 | ||
957 | (*port)--; | |
958 | } | |
959 | ||
960 | /* | |
961 | * Wasn't able to bind to a reserved port, so close the socket and return | |
962 | * -1... | |
963 | */ | |
964 | ||
965 | # ifdef WIN32 | |
966 | closesocket(fd); | |
967 | # else | |
968 | close(fd); | |
969 | # endif /* WIN32 */ | |
970 | ||
971 | return (-1); | |
972 | } | |
973 | #endif /* !HAVE_RRESVPORT */ | |
974 | ||
55737cf0 | 975 | |
976 | /* | |
977 | * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend. | |
978 | */ | |
979 | ||
980 | static void | |
981 | sigterm_handler(int sig) /* I - Signal */ | |
982 | { | |
983 | (void)sig; /* remove compiler warnings... */ | |
984 | ||
985 | /* | |
986 | * Remove the temporary file if necessary... | |
987 | */ | |
988 | ||
989 | if (tmpfilename[0]) | |
990 | unlink(tmpfilename); | |
991 | ||
992 | exit(1); | |
993 | } | |
994 | ||
995 | ||
c3026ddc | 996 | /* |
2d417cb3 | 997 | * End of "$Id: lpd.c,v 1.28.2.25 2003/10/09 19:13:48 mike Exp $". |
43e2fc22 | 998 | */ |