1 /*
2 * AppSocket backend for CUPS.
3 *
4 * Copyright © 2007-2018 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
9 */
10
11 /*
12 * Include necessary headers.
13 */
14
15 #include <cups/http-private.h>
16 #include "backend-private.h"
17 #include <stdarg.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20
21 #ifdef _WIN32
22 # include <winsock.h>
23 #else
24 # include <unistd.h>
25 # include <fcntl.h>
26 # include <sys/socket.h>
27 # include <netinet/in.h>
28 # include <arpa/inet.h>
29 # include <netdb.h>
30 #endif /* _WIN32 */
31
32
33 /*
34 * Local functions...
35 */
36
37 static ssize_t wait_bc(int device_fd, int secs);
38
39
40 /*
41 * 'main()' - Send a file to the printer or server.
42 *
43 * Usage:
44 *
45 * printer-uri job-id user title copies options [file]
46 */
47
48 int /* O - Exit status */
main(int argc,char * argv[])49 main(int argc, /* I - Number of command-line arguments (6 or 7) */
50 char *argv[]) /* I - Command-line arguments */
51 {
52 const char *device_uri; /* Device URI */
53 char scheme[255], /* Scheme in URI */
54 hostname[1024], /* Hostname */
55 username[255], /* Username info (not used) */
56 resource[1024], /* Resource info (not used) */
57 *options, /* Pointer to options */
58 *name, /* Name of option */
59 *value, /* Value of option */
60 sep; /* Option separator */
61 int print_fd; /* Print file */
62 int copies; /* Number of copies to print */
63 time_t start_time; /* Time of first connect */
64 int contimeout; /* Connection timeout */
65 int waiteof; /* Wait for end-of-file? */
66 int port; /* Port number */
67 int delay; /* Delay for retries... */
68 int device_fd; /* AppSocket */
69 int error; /* Error code (if any) */
70 http_addrlist_t *addrlist, /* Address list */
71 *addr; /* Connected address */
72 char addrname[256]; /* Address name */
73 int snmp_enabled = 1; /* Is SNMP enabled? */
74 int snmp_fd, /* SNMP socket */
75 start_count, /* Page count via SNMP at start */
76 page_count, /* Page count via SNMP */
77 have_supplies; /* Printer supports supply levels? */
78 ssize_t bytes = 0, /* Initial bytes read */
79 tbytes; /* Total number of bytes written */
80 char buffer[1024]; /* Initial print buffer */
81 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
82 struct sigaction action; /* Actions for POSIX signals */
83 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
84
85
86 /*
87 * Make sure status messages are not buffered...
88 */
89
90 setbuf(stderr, NULL);
91
92 /*
93 * Ignore SIGPIPE signals...
94 */
95
96 #ifdef HAVE_SIGSET
97 sigset(SIGPIPE, SIG_IGN);
98 #elif defined(HAVE_SIGACTION)
99 memset(&action, 0, sizeof(action));
100 action.sa_handler = SIG_IGN;
101 sigaction(SIGPIPE, &action, NULL);
102 #else
103 signal(SIGPIPE, SIG_IGN);
104 #endif /* HAVE_SIGSET */
105
106 /*
107 * Check command-line...
108 */
109
110 if (argc == 1)
111 {
112 printf("network socket \"Unknown\" \"%s\"\n",
113 _cupsLangString(cupsLangDefault(), _("AppSocket/HP JetDirect")));
114 return (CUPS_BACKEND_OK);
115 }
116 else if (argc < 6 || argc > 7)
117 {
118 _cupsLangPrintf(stderr,
119 _("Usage: %s job-id user title copies options [file]"),
120 argv[0]);
121 return (CUPS_BACKEND_FAILED);
122 }
123
124 /*
125 * If we have 7 arguments, print the file named on the command-line.
126 * Otherwise, send stdin instead...
127 */
128
129 if (argc == 6)
130 {
131 print_fd = 0;
132 copies = 1;
133 }
134 else
135 {
136 /*
137 * Try to open the print file...
138 */
139
140 if ((print_fd = open(argv[6], O_RDONLY)) < 0)
141 {
142 _cupsLangPrintError("ERROR", _("Unable to open print file"));
143 return (CUPS_BACKEND_FAILED);
144 }
145
146 copies = atoi(argv[4]);
147 }
148
149 /*
150 * Extract the hostname and port number from the URI...
151 */
152
153 while ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
154 {
155 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
156 sleep(10);
157
158 if (getenv("CLASS") != NULL)
159 return (CUPS_BACKEND_FAILED);
160 }
161
162 httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
163 username, sizeof(username), hostname, sizeof(hostname), &port,
164 resource, sizeof(resource));
165
166 if (port == 0)
167 port = 9100; /* Default to HP JetDirect/Tektronix PhaserShare */
168
169 /*
170 * Get options, if any...
171 */
172
173 waiteof = 1;
174 contimeout = 7 * 24 * 60 * 60;
175
176 if ((options = strchr(resource, '?')) != NULL)
177 {
178 /*
179 * Yup, terminate the device name string and move to the first
180 * character of the options...
181 */
182
183 *options++ = '\0';
184
185 /*
186 * Parse options...
187 */
188
189 while (*options)
190 {
191 /*
192 * Get the name...
193 */
194
195 name = options;
196
197 while (*options && *options != '=' && *options != '+' && *options != '&')
198 options ++;
199
200 if ((sep = *options) != '\0')
201 *options++ = '\0';
202
203 if (sep == '=')
204 {
205 /*
206 * Get the value...
207 */
208
209 value = options;
210
211 while (*options && *options != '+' && *options != '&')
212 options ++;
213
214 if (*options)
215 *options++ = '\0';
216 }
217 else
218 value = (char *)"";
219
220 /*
221 * Process the option...
222 */
223
224 if (!_cups_strcasecmp(name, "waiteof"))
225 {
226 /*
227 * Set the wait-for-eof value...
228 */
229
230 waiteof = !value[0] || !_cups_strcasecmp(value, "on") ||
231 !_cups_strcasecmp(value, "yes") || !_cups_strcasecmp(value, "true");
232 }
233 else if (!_cups_strcasecmp(name, "snmp"))
234 {
235 /*
236 * Enable/disable SNMP stuff...
237 */
238
239 snmp_enabled = !value[0] || !_cups_strcasecmp(value, "on") ||
240 !_cups_strcasecmp(value, "yes") ||
241 !_cups_strcasecmp(value, "true");
242 }
243 else if (!_cups_strcasecmp(name, "contimeout"))
244 {
245 /*
246 * Set the connection timeout...
247 */
248
249 if (atoi(value) > 0)
250 contimeout = atoi(value);
251 }
252 }
253 }
254
255 /*
256 * Then try finding the remote host...
257 */
258
259 start_time = time(NULL);
260
261 addrlist = backendLookup(hostname, port, NULL);
262
263 /*
264 * See if the printer supports SNMP...
265 */
266
267 if (snmp_enabled)
268 snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family);
269 else
270 snmp_fd = -1;
271
272 if (snmp_fd >= 0)
273 have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr),
274 &start_count, NULL);
275 else
276 have_supplies = start_count = 0;
277
278 /*
279 * Wait for data from the filter...
280 */
281
282 if (print_fd == 0)
283 {
284 if (!backendWaitLoop(snmp_fd, &(addrlist->addr), 1, backendNetworkSideCB))
285 return (CUPS_BACKEND_OK);
286 else if ((bytes = read(0, buffer, sizeof(buffer))) <= 0)
287 return (CUPS_BACKEND_OK);
288 }
289
290 /*
291 * Connect to the printer...
292 */
293
294 fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
295 _cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer."));
296
297 for (delay = 5;;)
298 {
299 if ((addr = httpAddrConnect(addrlist, &device_fd)) == NULL)
300 {
301 error = errno;
302 device_fd = -1;
303
304 if (getenv("CLASS") != NULL)
305 {
306 /*
307 * If the CLASS environment variable is set, the job was submitted
308 * to a class and not to a specific queue. In this case, we want
309 * to abort immediately so that the job can be requeued on the next
310 * available printer in the class.
311 */
312
313 _cupsLangPrintFilter(stderr, "INFO",
314 _("Unable to contact printer, queuing on next "
315 "printer in class."));
316
317 /*
318 * Sleep 5 seconds to keep the job from requeuing too rapidly...
319 */
320
321 sleep(5);
322
323 return (CUPS_BACKEND_FAILED);
324 }
325
326 fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(error));
327
328 if (errno == ECONNREFUSED || errno == EHOSTDOWN || errno == EHOSTUNREACH || errno == ETIMEDOUT || errno == ENOTCONN)
329 {
330 if (contimeout && (time(NULL) - start_time) > contimeout)
331 {
332 _cupsLangPrintFilter(stderr, "ERROR",
333 _("The printer is not responding."));
334 return (CUPS_BACKEND_FAILED);
335 }
336
337 switch (error)
338 {
339 case EHOSTDOWN :
340 _cupsLangPrintFilter(stderr, "WARNING",
341 _("The printer may not exist or "
342 "is unavailable at this time."));
343 break;
344
345 case EHOSTUNREACH :
346 default :
347 _cupsLangPrintFilter(stderr, "WARNING",
348 _("The printer is unreachable at this "
349 "time."));
350 break;
351
352 case ECONNREFUSED :
353 _cupsLangPrintFilter(stderr, "WARNING",
354 _("The printer is in use."));
355 break;
356 }
357
358 sleep((unsigned)delay);
359
360 if (delay < 30)
361 delay += 5;
362 }
363 else
364 {
365 _cupsLangPrintFilter(stderr, "ERROR",
366 _("The printer is not responding."));
367 sleep(30);
368 }
369 }
370 else
371 break;
372 }
373
374 fputs("STATE: -connecting-to-device\n", stderr);
375 _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer."));
376
377 fprintf(stderr, "DEBUG: Connected to %s:%d...\n",
378 httpAddrString(&(addr->addr), addrname, sizeof(addrname)),
379 httpAddrPort(&(addr->addr)));
380
381 /*
382 * Print everything...
383 */
384
385 tbytes = 0;
386
387 if (bytes > 0)
388 tbytes += write(device_fd, buffer, (size_t)bytes);
389
390 while (copies > 0 && tbytes >= 0)
391 {
392 copies --;
393
394 if (print_fd != 0)
395 {
396 fputs("PAGE: 1 1\n", stderr);
397 lseek(print_fd, 0, SEEK_SET);
398 }
399
400 if ((bytes = backendRunLoop(print_fd, device_fd, snmp_fd, &(addrlist->addr), 1, 0, backendNetworkSideCB)) < 0)
401 tbytes = -1;
402 else
403 tbytes = bytes;
404
405 if (print_fd != 0 && tbytes >= 0)
406 _cupsLangPrintFilter(stderr, "INFO", _("Print file sent."));
407 }
408
409 fputs("STATE: +cups-waiting-for-job-completed\n", stderr);
410
411 if (waiteof && tbytes >= 0)
412 {
413 /*
414 * Shutdown the socket and wait for the other end to finish...
415 */
416
417 _cupsLangPrintFilter(stderr, "INFO", _("Waiting for printer to finish."));
418
419 shutdown(device_fd, 1);
420
421 while (wait_bc(device_fd, 90) > 0);
422 }
423
424 /*
425 * Collect the final page count as needed...
426 */
427
428 if (have_supplies &&
429 !backendSNMPSupplies(snmp_fd, &(addrlist->addr), &page_count, NULL) &&
430 page_count > start_count)
431 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
432
433 /*
434 * Close the socket connection...
435 */
436
437 close(device_fd);
438
439 httpAddrFreeList(addrlist);
440
441 /*
442 * Close the input file and return...
443 */
444
445 if (print_fd != 0)
446 close(print_fd);
447
448 return (tbytes >= 0 ? CUPS_BACKEND_OK : CUPS_BACKEND_FAILED);
449 }
450
451
452 /*
453 * 'wait_bc()' - Wait for back-channel data...
454 */
455
456 static ssize_t /* O - # bytes read or -1 on error */
wait_bc(int device_fd,int secs)457 wait_bc(int device_fd, /* I - Socket */
458 int secs) /* I - Seconds to wait */
459 {
460 struct timeval timeout; /* Timeout for select() */
461 fd_set input; /* Input set for select() */
462 ssize_t bytes; /* Number of back-channel bytes read */
463 char buffer[1024]; /* Back-channel buffer */
464
465
466 /*
467 * Wait up to "secs" seconds for backchannel data...
468 */
469
470 timeout.tv_sec = secs;
471 timeout.tv_usec = 0;
472
473 FD_ZERO(&input);
474 FD_SET(device_fd, &input);
475
476 if (select(device_fd + 1, &input, NULL, NULL, &timeout) > 0)
477 {
478 /*
479 * Grab the data coming back and spit it out to stderr...
480 */
481
482 if ((bytes = read(device_fd, buffer, sizeof(buffer))) > 0)
483 {
484 fprintf(stderr, "DEBUG: Received %d bytes of back-channel data\n",
485 (int)bytes);
486 cupsBackChannelWrite(buffer, (size_t)bytes, 1.0);
487 }
488
489 return (bytes);
490 }
491 else
492 return (-1);
493 }
494