]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/socket.c
Update ipp documentation to reflect the behavior of configuring WiFi on IPP USB printers.
[thirdparty/cups.git] / backend / socket.c
CommitLineData
ef416fc2 1/*
581dae2d 2 * AppSocket backend for CUPS.
ef416fc2 3 *
72a21348 4 * Copyright © 2007-2018 by Apple Inc.
53f8d64f 5 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
ef416fc2 6 *
53f8d64f
MS
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
ef416fc2 9 */
10
11/*
12 * Include necessary headers.
13 */
14
ef416fc2 15#include <cups/http-private.h>
ed486911 16#include "backend-private.h"
ef416fc2 17#include <stdarg.h>
ef416fc2 18#include <sys/types.h>
19#include <sys/stat.h>
ef416fc2 20
24a06ed3 21#ifdef _WIN32
ef416fc2 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>
24a06ed3 30#endif /* _WIN32 */
ef416fc2 31
32
f7deaa1a 33/*
34 * Local functions...
35 */
36
7e86f2f6 37static ssize_t wait_bc(int device_fd, int secs);
f7deaa1a 38
39
ef416fc2 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
48int /* O - Exit status */
49main(int argc, /* I - Number of command-line arguments (6 or 7) */
50 char *argv[]) /* I - Command-line arguments */
51{
1f0275e3 52 const char *device_uri; /* Device URI */
acb056cb 53 char scheme[255], /* Scheme in URI */
ef416fc2 54 hostname[1024], /* Hostname */
55 username[255], /* Username info (not used) */
fa73b229 56 resource[1024], /* Resource info (not used) */
57 *options, /* Pointer to options */
db1f069b
MS
58 *name, /* Name of option */
59 *value, /* Value of option */
60 sep; /* Option separator */
ed486911 61 int print_fd; /* Print file */
ef416fc2 62 int copies; /* Number of copies to print */
f8b3a85b 63 time_t start_time; /* Time of first connect */
c0e1af83 64 int contimeout; /* Connection timeout */
fa73b229 65 int waiteof; /* Wait for end-of-file? */
ef416fc2 66 int port; /* Port number */
ef416fc2 67 int delay; /* Delay for retries... */
ed486911 68 int device_fd; /* AppSocket */
ef416fc2 69 int error; /* Error code (if any) */
26d47ec6 70 http_addrlist_t *addrlist, /* Address list */
db1f069b 71 *addr; /* Connected address */
26d47ec6 72 char addrname[256]; /* Address name */
5a9febac 73 int snmp_enabled = 1; /* Is SNMP enabled? */
568fa3fa
MS
74 int snmp_fd, /* SNMP socket */
75 start_count, /* Page count via SNMP at start */
426c6a59
MS
76 page_count, /* Page count via SNMP */
77 have_supplies; /* Printer supports supply levels? */
eac3a0a0
MS
78 ssize_t bytes = 0, /* Initial bytes read */
79 tbytes; /* Total number of bytes written */
80 char buffer[1024]; /* Initial print buffer */
ef416fc2 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 {
8b450588
MS
112 printf("network socket \"Unknown\" \"%s\"\n",
113 _cupsLangString(cupsLangDefault(), _("AppSocket/HP JetDirect")));
ef416fc2 114 return (CUPS_BACKEND_OK);
115 }
116 else if (argc < 6 || argc > 7)
117 {
db1f069b 118 _cupsLangPrintf(stderr,
0837b7e8 119 _("Usage: %s job-id user title copies options [file]"),
db1f069b 120 argv[0]);
ef416fc2 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 {
ed486911 131 print_fd = 0;
132 copies = 1;
ef416fc2 133 }
134 else
135 {
136 /*
137 * Try to open the print file...
138 */
139
ed486911 140 if ((print_fd = open(argv[6], O_RDONLY)) < 0)
ef416fc2 141 {
0837b7e8 142 _cupsLangPrintError("ERROR", _("Unable to open print file"));
ef416fc2 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
0268488e
MS
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 }
1f0275e3 161
acb056cb
MS
162 httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
163 username, sizeof(username), hostname, sizeof(hostname), &port,
ef416fc2 164 resource, sizeof(resource));
165
166 if (port == 0)
167 port = 9100; /* Default to HP JetDirect/Tektronix PhaserShare */
168
fa73b229 169 /*
170 * Get options, if any...
171 */
172
c0e1af83 173 waiteof = 1;
174 contimeout = 7 * 24 * 60 * 60;
fa73b229 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
db1f069b 195 name = options;
fa73b229 196
db1f069b
MS
197 while (*options && *options != '=' && *options != '+' && *options != '&')
198 options ++;
199
200 if ((sep = *options) != '\0')
201 *options++ = '\0';
202
203 if (sep == '=')
fa73b229 204 {
205 /*
206 * Get the value...
207 */
208
db1f069b 209 value = options;
fa73b229 210
db1f069b 211 while (*options && *options != '+' && *options != '&')
fa73b229 212 options ++;
db1f069b
MS
213
214 if (*options)
215 *options++ = '\0';
fa73b229 216 }
217 else
db1f069b 218 value = (char *)"";
fa73b229 219
220 /*
221 * Process the option...
222 */
223
88f9aafc 224 if (!_cups_strcasecmp(name, "waiteof"))
fa73b229 225 {
226 /*
227 * Set the wait-for-eof value...
228 */
229
88f9aafc
MS
230 waiteof = !value[0] || !_cups_strcasecmp(value, "on") ||
231 !_cups_strcasecmp(value, "yes") || !_cups_strcasecmp(value, "true");
fa73b229 232 }
5a9febac
MS
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") ||
5a582409
MS
240 !_cups_strcasecmp(value, "yes") ||
241 !_cups_strcasecmp(value, "true");
5a9febac 242 }
88f9aafc 243 else if (!_cups_strcasecmp(name, "contimeout"))
c0e1af83 244 {
245 /*
246 * Set the connection timeout...
247 */
248
249 if (atoi(value) > 0)
250 contimeout = atoi(value);
251 }
fa73b229 252 }
253 }
254
ef416fc2 255 /*
c8fef167 256 * Then try finding the remote host...
ef416fc2 257 */
258
4d301e69 259 start_time = time(NULL);
c0e1af83 260
f5cffc18 261 addrlist = backendLookup(hostname, port, NULL);
c8fef167
MS
262
263 /*
264 * See if the printer supports SNMP...
265 */
266
5a9febac
MS
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)
f14324a7
MS
273 have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr),
274 &start_count, NULL);
c8fef167
MS
275 else
276 have_supplies = start_count = 0;
277
278 /*
279 * Wait for data from the filter...
280 */
281
282 if (print_fd == 0)
eac3a0a0 283 {
f14324a7 284 if (!backendWaitLoop(snmp_fd, &(addrlist->addr), 1, backendNetworkSideCB))
c8fef167 285 return (CUPS_BACKEND_OK);
eac3a0a0
MS
286 else if ((bytes = read(0, buffer, sizeof(buffer))) <= 0)
287 return (CUPS_BACKEND_OK);
288 }
c8fef167
MS
289
290 /*
291 * Connect to the printer...
292 */
ef416fc2 293
acb056cb 294 fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
0837b7e8 295 _cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer."));
ef416fc2 296
ed486911 297 for (delay = 5;;)
ef416fc2 298 {
26d47ec6 299 if ((addr = httpAddrConnect(addrlist, &device_fd)) == NULL)
ef416fc2 300 {
ed486911 301 error = errno;
302 device_fd = -1;
ef416fc2 303
ed486911 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 */
ef416fc2 312
0837b7e8
MS
313 _cupsLangPrintFilter(stderr, "INFO",
314 _("Unable to contact printer, queuing on next "
315 "printer in class."));
ef416fc2 316
ed486911 317 /*
318 * Sleep 5 seconds to keep the job from requeuing too rapidly...
319 */
ef416fc2 320
ed486911 321 sleep(5);
ef416fc2 322
ed486911 323 return (CUPS_BACKEND_FAILED);
324 }
ef416fc2 325
c7017ecc
MS
326 fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(error));
327
4cf66fef 328 if (errno == ECONNREFUSED || errno == EHOSTDOWN || errno == EHOSTUNREACH || errno == ETIMEDOUT || errno == ENOTCONN)
ed486911 329 {
c0e1af83 330 if (contimeout && (time(NULL) - start_time) > contimeout)
331 {
0837b7e8
MS
332 _cupsLangPrintFilter(stderr, "ERROR",
333 _("The printer is not responding."));
c0e1af83 334 return (CUPS_BACKEND_FAILED);
335 }
336
c7017ecc
MS
337 switch (error)
338 {
339 case EHOSTDOWN :
0837b7e8 340 _cupsLangPrintFilter(stderr, "WARNING",
22c9029b
MS
341 _("The printer may not exist or "
342 "is unavailable at this time."));
c7017ecc
MS
343 break;
344
345 case EHOSTUNREACH :
4cf66fef 346 default :
0837b7e8 347 _cupsLangPrintFilter(stderr, "WARNING",
22c9029b
MS
348 _("The printer is unreachable at this "
349 "time."));
c7017ecc
MS
350 break;
351
352 case ECONNREFUSED :
0837b7e8 353 _cupsLangPrintFilter(stderr, "WARNING",
f3c17241 354 _("The printer is in use."));
c7017ecc
MS
355 break;
356 }
c0e1af83 357
7e86f2f6 358 sleep((unsigned)delay);
ef416fc2 359
ed486911 360 if (delay < 30)
361 delay += 5;
ef416fc2 362 }
363 else
ed486911 364 {
0837b7e8 365 _cupsLangPrintFilter(stderr, "ERROR",
22c9029b 366 _("The printer is not responding."));
ed486911 367 sleep(30);
368 }
ef416fc2 369 }
ed486911 370 else
371 break;
372 }
ef416fc2 373
ed486911 374 fputs("STATE: -connecting-to-device\n", stderr);
0837b7e8 375 _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer."));
26d47ec6 376
22c9029b
MS
377 fprintf(stderr, "DEBUG: Connected to %s:%d...\n",
378 httpAddrString(&(addr->addr), addrname, sizeof(addrname)),
a469f8a5 379 httpAddrPort(&(addr->addr)));
ef416fc2 380
ed486911 381 /*
382 * Print everything...
383 */
ef416fc2 384
ed486911 385 tbytes = 0;
ef416fc2 386
eac3a0a0 387 if (bytes > 0)
7e86f2f6 388 tbytes += write(device_fd, buffer, (size_t)bytes);
eac3a0a0 389
ed486911 390 while (copies > 0 && tbytes >= 0)
391 {
ef416fc2 392 copies --;
393
ed486911 394 if (print_fd != 0)
ef416fc2 395 {
396 fputs("PAGE: 1 1\n", stderr);
ed486911 397 lseek(print_fd, 0, SEEK_SET);
ef416fc2 398 }
399
72a21348
MS
400 if ((bytes = backendRunLoop(print_fd, device_fd, snmp_fd, &(addrlist->addr), 1, 0, backendNetworkSideCB)) < 0)
401 tbytes = -1;
402 else
403 tbytes = bytes;
ef416fc2 404
ed486911 405 if (print_fd != 0 && tbytes >= 0)
0837b7e8 406 _cupsLangPrintFilter(stderr, "INFO", _("Print file sent."));
ed486911 407 }
ef416fc2 408
1e3e80bb 409 fputs("STATE: +cups-waiting-for-job-completed\n", stderr);
581dae2d 410
72a21348 411 if (waiteof && tbytes >= 0)
ed486911 412 {
413 /*
414 * Shutdown the socket and wait for the other end to finish...
415 */
ef416fc2 416
0837b7e8 417 _cupsLangPrintFilter(stderr, "INFO", _("Waiting for printer to finish."));
ef416fc2 418
ed486911 419 shutdown(device_fd, 1);
ef416fc2 420
323c5de1 421 while (wait_bc(device_fd, 90) > 0);
ed486911 422 }
ef416fc2 423
568fa3fa
MS
424 /*
425 * Collect the final page count as needed...
426 */
427
f14324a7
MS
428 if (have_supplies &&
429 !backendSNMPSupplies(snmp_fd, &(addrlist->addr), &page_count, NULL) &&
568fa3fa
MS
430 page_count > start_count)
431 fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
432
ed486911 433 /*
434 * Close the socket connection...
435 */
ef416fc2 436
ed486911 437 close(device_fd);
ef416fc2 438
439 httpAddrFreeList(addrlist);
440
441 /*
442 * Close the input file and return...
443 */
444
ed486911 445 if (print_fd != 0)
446 close(print_fd);
ef416fc2 447
72a21348 448 return (tbytes >= 0 ? CUPS_BACKEND_OK : CUPS_BACKEND_FAILED);
ef416fc2 449}
450
451
f7deaa1a 452/*
323c5de1 453 * 'wait_bc()' - Wait for back-channel data...
454 */
455
7e86f2f6 456static ssize_t /* O - # bytes read or -1 on error */
323c5de1 457wait_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 {
4d301e69 484 fprintf(stderr, "DEBUG: Received %d bytes of back-channel data\n",
323c5de1 485 (int)bytes);
7e86f2f6 486 cupsBackChannelWrite(buffer, (size_t)bytes, 1.0);
323c5de1 487 }
488
489 return (bytes);
490 }
491 else
492 return (-1);
493}