]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/socket.c
Localize backends.
[thirdparty/cups.git] / backend / socket.c
CommitLineData
43e2fc22 1/*
b2e10895 2 * "$Id$"
43e2fc22 3 *
e73c6c0a 4 * AppSocket backend for the Common UNIX Printing System (CUPS).
43e2fc22 5 *
761a542e 6 * Copyright 1997-2007 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
58ec2a95 17 * 44141 Airport View Drive, Suite 204
b2e10895 18 * Hollywood, Maryland 20636 USA
43e2fc22 19 *
9639c4de 20 * Voice: (301) 373-9600
43e2fc22 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 *
761a542e 28 * main() - Send a file to the printer or server.
29 * side_cb() - Handle side-channel requests...
43e2fc22 30 */
31
32/*
33 * Include necessary headers.
34 */
35
6ca34e4e 36#include <cups/http-private.h>
43b5ad0d 37#include "backend-private.h"
e73c6c0a 38#include <stdarg.h>
e73c6c0a 39#include <sys/types.h>
40#include <sys/stat.h>
41
c3026ddc 42#ifdef WIN32
e73c6c0a 43# include <winsock.h>
44#else
267cf96a 45# include <unistd.h>
46# include <fcntl.h>
e73c6c0a 47# include <sys/socket.h>
48# include <netinet/in.h>
49# include <arpa/inet.h>
50# include <netdb.h>
c3026ddc 51#endif /* WIN32 */
e73c6c0a 52
53
761a542e 54/*
55 * Local functions...
56 */
57
58static void side_cb(int print_fd, int device_fd, int use_bc);
59
60
e73c6c0a 61/*
62 * 'main()' - Send a file to the printer or server.
63 *
64 * Usage:
65 *
66 * printer-uri job-id user title copies options [file]
67 */
68
5baa731f 69int /* O - Exit status */
70main(int argc, /* I - Number of command-line arguments (6 or 7) */
71 char *argv[]) /* I - Command-line arguments */
e73c6c0a 72{
5baa731f 73 char method[255], /* Method in URI */
74 hostname[1024], /* Hostname */
75 username[255], /* Username info (not used) */
41ed0c21 76 resource[1024], /* Resource info (not used) */
77 *options, /* Pointer to options */
78 name[255], /* Name of option */
79 value[255], /* Value of option */
80 *ptr; /* Pointer into name or value */
43b5ad0d 81 int print_fd; /* Print file */
5baa731f 82 int copies; /* Number of copies to print */
41ed0c21 83 int waiteof; /* Wait for end-of-file? */
5baa731f 84 int port; /* Port number */
086c584d 85 char portname[255]; /* Port name */
5baa731f 86 int delay; /* Delay for retries... */
43b5ad0d 87 int device_fd; /* AppSocket */
5baa731f 88 int error; /* Error code (if any) */
a0c0bbe7 89 http_addrlist_t *addrlist, /* Address list */
90 *addr; /* Connected address */
91 char addrname[256]; /* Address name */
43b5ad0d 92 ssize_t tbytes; /* Total number of bytes written */
5baa731f 93 struct timeval timeout; /* Timeout for select() */
43b5ad0d 94 fd_set input; /* Input set for select() */
95 ssize_t bc_bytes; /* Number of back-channel bytes read */
4ff40357 96#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
5baa731f 97 struct sigaction action; /* Actions for POSIX signals */
4ff40357 98#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
e73c6c0a 99
100
4b23f3b3 101 /*
102 * Make sure status messages are not buffered...
103 */
104
105 setbuf(stderr, NULL);
106
98904cd6 107 /*
108 * Ignore SIGPIPE signals...
109 */
110
111#ifdef HAVE_SIGSET
112 sigset(SIGPIPE, SIG_IGN);
113#elif defined(HAVE_SIGACTION)
114 memset(&action, 0, sizeof(action));
115 action.sa_handler = SIG_IGN;
116 sigaction(SIGPIPE, &action, NULL);
117#else
118 signal(SIGPIPE, SIG_IGN);
119#endif /* HAVE_SIGSET */
120
4b23f3b3 121 /*
122 * Check command-line...
123 */
124
68edc300 125 if (argc == 1)
126 {
d4c438d4 127 puts("network socket \"Unknown\" \"AppSocket/HP JetDirect\"");
6248387b 128 return (CUPS_BACKEND_OK);
68edc300 129 }
130 else if (argc < 6 || argc > 7)
e73c6c0a 131 {
34b18bd4 132 _cupsLangPrintf(stderr,
133 _("Usage: %s job-id user title copies options [file]\n"),
134 argv[0]);
6248387b 135 return (CUPS_BACKEND_FAILED);
e73c6c0a 136 }
137
138 /*
139 * If we have 7 arguments, print the file named on the command-line.
140 * Otherwise, send stdin instead...
141 */
142
143 if (argc == 6)
3f9cb6c6 144 {
43b5ad0d 145 print_fd = 0;
146 copies = 1;
3f9cb6c6 147 }
e73c6c0a 148 else
149 {
150 /*
151 * Try to open the print file...
152 */
153
43b5ad0d 154 if ((print_fd = open(argv[6], O_RDONLY)) < 0)
e73c6c0a 155 {
decc1f36 156 perror("ERROR: unable to open print file");
6248387b 157 return (CUPS_BACKEND_FAILED);
e73c6c0a 158 }
3f9cb6c6 159
160 copies = atoi(argv[4]);
e73c6c0a 161 }
162
163 /*
164 * Extract the hostname and port number from the URI...
165 */
166
00a1fad8 167 httpSeparateURI(HTTP_URI_CODING_ALL, cupsBackendDeviceURI(argv),
168 method, sizeof(method), username, sizeof(username),
169 hostname, sizeof(hostname), &port,
0e66904d 170 resource, sizeof(resource));
e73c6c0a 171
172 if (port == 0)
173 port = 9100; /* Default to HP JetDirect/Tektronix PhaserShare */
174
41ed0c21 175 /*
176 * Get options, if any...
177 */
178
179 waiteof = 1;
180
181 if ((options = strchr(resource, '?')) != NULL)
182 {
183 /*
184 * Yup, terminate the device name string and move to the first
185 * character of the options...
186 */
187
188 *options++ = '\0';
189
190 /*
191 * Parse options...
192 */
193
194 while (*options)
195 {
196 /*
197 * Get the name...
198 */
199
200 for (ptr = name; *options && *options != '=';)
201 if (ptr < (name + sizeof(name) - 1))
202 *ptr++ = *options++;
203 *ptr = '\0';
204
205 if (*options == '=')
206 {
207 /*
208 * Get the value...
209 */
210
211 options ++;
212
213 for (ptr = value; *options && *options != '+' && *options != '&';)
214 if (ptr < (value + sizeof(value) - 1))
215 *ptr++ = *options++;
216 *ptr = '\0';
217
218 if (*options == '+' || *options == '&')
219 options ++;
220 }
221 else
222 value[0] = '\0';
223
224 /*
225 * Process the option...
226 */
227
228 if (!strcasecmp(name, "waiteof"))
229 {
230 /*
231 * Set the wait-for-eof value...
232 */
233
234 waiteof = !value[0] || !strcasecmp(value, "on") ||
235 !strcasecmp(value, "yes") || !strcasecmp(value, "true");
236 }
237 }
238 }
239
e73c6c0a 240 /*
241 * Then try to connect to the remote host...
242 */
243
086c584d 244 sprintf(portname, "%d", port);
245
246 if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
e73c6c0a 247 {
34b18bd4 248 _cupsLangPrintf(stderr,
249 _("ERROR: Unable to locate printer \'%s\'!\n"), hostname);
6248387b 250 return (CUPS_BACKEND_STOP);
e73c6c0a 251 }
252
34b18bd4 253 _cupsLangPrintf(stderr,
254 _("INFO: Attempting to connect to host %s on port %d\n"),
255 hostname, port);
e73c6c0a 256
43b5ad0d 257 fputs("STATE: +connecting-to-device\n", stderr);
897922a9 258
43b5ad0d 259 for (delay = 5;;)
e73c6c0a 260 {
a0c0bbe7 261 if ((addr = httpAddrConnect(addrlist, &device_fd)) == NULL)
e73c6c0a 262 {
43b5ad0d 263 error = errno;
264 device_fd = -1;
3f9cb6c6 265
43b5ad0d 266 if (getenv("CLASS") != NULL)
267 {
268 /*
269 * If the CLASS environment variable is set, the job was submitted
270 * to a class and not to a specific queue. In this case, we want
271 * to abort immediately so that the job can be requeued on the next
272 * available printer in the class.
273 */
b7b63780 274
34b18bd4 275 _cupsLangPrintf(stderr,
276 _("INFO: Unable to connect to \"%s\", queuing on "
277 "next printer in class...\n"),
278 hostname);
b7b63780 279
43b5ad0d 280 /*
281 * Sleep 5 seconds to keep the job from requeuing too rapidly...
282 */
b7b63780 283
43b5ad0d 284 sleep(5);
b7b63780 285
43b5ad0d 286 return (CUPS_BACKEND_FAILED);
287 }
b7b63780 288
43b5ad0d 289 if (error == ECONNREFUSED || error == EHOSTDOWN ||
290 error == EHOSTUNREACH)
291 {
34b18bd4 292 _cupsLangPrintf(stderr,
293 _("INFO: Network host \'%s\' is busy; will retry "
294 "in %d seconds...\n"),
295 hostname, delay);
43b5ad0d 296 sleep(delay);
10f725af 297
43b5ad0d 298 if (delay < 30)
299 delay += 5;
e73c6c0a 300 }
3f9cb6c6 301 else
43b5ad0d 302 {
303 perror("ERROR: Unable to connect to printer (retrying in 30 seconds)");
304 sleep(30);
305 }
e73c6c0a 306 }
43b5ad0d 307 else
308 break;
309 }
e73c6c0a 310
43b5ad0d 311 fputs("STATE: -connecting-to-device\n", stderr);
34b18bd4 312 _cupsLangPrintf(stderr, _("INFO: Connected to %s...\n"), hostname);
a0c0bbe7 313
314#ifdef AF_INET6
315 if (addr->addr.addr.sa_family == AF_INET6)
34b18bd4 316 _cupsLangPrintf(stderr, _("DEBUG: Connected to [%s]:%d (IPv6)...\n"),
a0c0bbe7 317 httpAddrString(&addr->addr, addrname, sizeof(addrname)),
318 ntohs(addr->addr.ipv6.sin6_port));
319 else
320#endif /* AF_INET6 */
321 if (addr->addr.addr.sa_family == AF_INET)
34b18bd4 322 _cupsLangPrintf(stderr, _("DEBUG: Connected to %s:%d (IPv4)...\n"),
a0c0bbe7 323 httpAddrString(&addr->addr, addrname, sizeof(addrname)),
324 ntohs(addr->addr.ipv4.sin_port));
4ff40357 325
43b5ad0d 326 /*
327 * Print everything...
328 */
4ff40357 329
43b5ad0d 330 tbytes = 0;
e73c6c0a 331
43b5ad0d 332 while (copies > 0 && tbytes >= 0)
333 {
3f9cb6c6 334 copies --;
7c88bf41 335
43b5ad0d 336 if (print_fd != 0)
e73c6c0a 337 {
3f9cb6c6 338 fputs("PAGE: 1 1\n", stderr);
43b5ad0d 339 lseek(print_fd, 0, SEEK_SET);
e73c6c0a 340 }
e73c6c0a 341
761a542e 342 tbytes = backendRunLoop(print_fd, device_fd, 1, side_cb);
5baa731f 343
43b5ad0d 344 if (print_fd != 0 && tbytes >= 0)
34b18bd4 345 _cupsLangPrintf(stderr,
346 _("INFO: Sent print file, " CUPS_LLFMT " bytes...\n"),
347 CUPS_LLCAST tbytes);
43b5ad0d 348 }
3f9cb6c6 349
43b5ad0d 350 if (waiteof)
351 {
352 /*
353 * Shutdown the socket and wait for the other end to finish...
354 */
3f9cb6c6 355
34b18bd4 356 _cupsLangPuts(stderr,
357 _("INFO: Print file sent, waiting for printer to "
358 "finish...\n"));
0cd4970e 359
43b5ad0d 360 shutdown(device_fd, 1);
3f9cb6c6 361
43b5ad0d 362 for (;;)
b5cb0608 363 {
364 /*
43b5ad0d 365 * Wait a maximum of 90 seconds for backchannel data or a closed
366 * connection...
b5cb0608 367 */
368
43b5ad0d 369 timeout.tv_sec = 90;
370 timeout.tv_usec = 0;
b5cb0608 371
43b5ad0d 372 FD_ZERO(&input);
373 FD_SET(device_fd, &input);
b5cb0608 374
43b5ad0d 375 if (select(device_fd + 1, &input, NULL, NULL, &timeout) > 0)
b5cb0608 376 {
377 /*
43b5ad0d 378 * Grab the data coming back and spit it out to stderr...
b5cb0608 379 */
380
43b5ad0d 381 if ((bc_bytes = read(device_fd, resource, sizeof(resource))) > 0)
b2e10895 382 {
34b18bd4 383 _cupsLangPrintf(stderr,
384 _("DEBUG: Received %d bytes of back-channel data!\n"),
385 (int)bc_bytes);
43b5ad0d 386 cupsBackChannelWrite(resource, bc_bytes, 1.0);
41ed0c21 387 }
b2e10895 388 else
b5cb0608 389 break;
390 }
43b5ad0d 391 else
392 break;
b5cb0608 393 }
43b5ad0d 394 }
b5cb0608 395
43b5ad0d 396 /*
397 * Close the socket connection...
398 */
3f9cb6c6 399
43b5ad0d 400 close(device_fd);
e73c6c0a 401
086c584d 402 httpAddrFreeList(addrlist);
403
e73c6c0a 404 /*
3f9cb6c6 405 * Close the input file and return...
e73c6c0a 406 */
407
43b5ad0d 408 if (print_fd != 0)
409 close(print_fd);
e73c6c0a 410
43b5ad0d 411 if (tbytes >= 0)
34b18bd4 412 _cupsLangPuts(stderr, _("INFO: Ready to print.\n"));
6248387b 413
43b5ad0d 414 return (tbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
e73c6c0a 415}
43e2fc22 416
417
761a542e 418/*
419 * 'side_cb()' - Handle side-channel requests...
420 */
421
422static void
423side_cb(int print_fd, /* I - Print file */
424 int device_fd, /* I - Device file */
425 int use_bc) /* I - Using back-channel? */
426{
427 cups_sc_command_t command; /* Request command */
428 cups_sc_status_t status; /* Request/response status */
429 char data[2048]; /* Request/response data */
430 int datalen; /* Request/response data size */
431
432
433 datalen = sizeof(data);
434
435 if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
436 {
34b18bd4 437 _cupsLangPuts(stderr, _("WARNING: Failed to read side-channel request!\n"));
761a542e 438 return;
439 }
440
441 switch (command)
442 {
443 case CUPS_SC_CMD_DRAIN_OUTPUT :
444 /*
445 * Our sockets disable the Nagle algorithm and data is sent immediately.
446 */
447
448 status = CUPS_SC_STATUS_OK;
449 datalen = 0;
450 break;
451
452 case CUPS_SC_CMD_GET_BIDI :
453 data[0] = use_bc;
454 datalen = 1;
455 break;
456
457 default :
458 status = CUPS_SC_STATUS_NOT_IMPLEMENTED;
459 datalen = 0;
460 break;
461 }
462
463 cupsSideChannelWrite(command, status, data, datalen, 1.0);
464}
465
466
b2e10895 467/*
468 * End of "$Id$".
43e2fc22 469 */