]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/socket.c
The easysw/current branch should have the same properties as trunk...
[thirdparty/cups.git] / backend / socket.c
CommitLineData
ef416fc2 1/*
7a6a01dd 2 * "$Id$"
ef416fc2 3 *
4 * AppSocket backend for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
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
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * This file is subject to the Apple OS-Developed Software exception.
25 *
26 * Contents:
27 *
28 * main() - Send a file to the printer or server.
29 */
30
31/*
32 * Include necessary headers.
33 */
34
35#include <cups/backend.h>
36#include <cups/http-private.h>
37#include <cups/cups.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <stdarg.h>
41#include <cups/string.h>
42#include <errno.h>
43#include <sys/types.h>
44#include <sys/stat.h>
45#include <signal.h>
46
47#ifdef WIN32
48# include <winsock.h>
49#else
50# include <unistd.h>
51# include <fcntl.h>
52# include <sys/socket.h>
53# include <netinet/in.h>
54# include <arpa/inet.h>
55# include <netdb.h>
56#endif /* WIN32 */
57
58
59/*
60 * 'main()' - Send a file to the printer or server.
61 *
62 * Usage:
63 *
64 * printer-uri job-id user title copies options [file]
65 */
66
67int /* O - Exit status */
68main(int argc, /* I - Number of command-line arguments (6 or 7) */
69 char *argv[]) /* I - Command-line arguments */
70{
71 char method[255], /* Method in URI */
72 hostname[1024], /* Hostname */
73 username[255], /* Username info (not used) */
fa73b229 74 resource[1024], /* Resource info (not used) */
75 *options, /* Pointer to options */
76 name[255], /* Name of option */
77 value[255], /* Value of option */
78 *ptr; /* Pointer into name or value */
ef416fc2 79 int fp; /* Print file */
80 int copies; /* Number of copies to print */
fa73b229 81 int waiteof; /* Wait for end-of-file? */
ef416fc2 82 int port; /* Port number */
83 char portname[255]; /* Port name */
84 int delay; /* Delay for retries... */
85 int fd; /* AppSocket */
86 int error; /* Error code (if any) */
87 http_addrlist_t *addrlist; /* Address list */
88 int rbytes; /* Number of bytes read */
89 int wbytes; /* Number of bytes written */
90 int nbytes; /* Number of bytes read */
91 size_t tbytes; /* Total number of bytes written */
92 char buffer[8192], /* Output buffer */
93 *bufptr; /* Pointer into buffer */
94 struct timeval timeout; /* Timeout for select() */
95 fd_set input, /* Input set for select() */
96 output; /* Output set for select() */
97#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
98 struct sigaction action; /* Actions for POSIX signals */
99#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
100
101
102 /*
103 * Make sure status messages are not buffered...
104 */
105
106 setbuf(stderr, NULL);
107
108 /*
109 * Ignore SIGPIPE signals...
110 */
111
112#ifdef HAVE_SIGSET
113 sigset(SIGPIPE, SIG_IGN);
114#elif defined(HAVE_SIGACTION)
115 memset(&action, 0, sizeof(action));
116 action.sa_handler = SIG_IGN;
117 sigaction(SIGPIPE, &action, NULL);
118#else
119 signal(SIGPIPE, SIG_IGN);
120#endif /* HAVE_SIGSET */
121
122 /*
123 * Check command-line...
124 */
125
126 if (argc == 1)
127 {
128 puts("network socket \"Unknown\" \"AppSocket/HP JetDirect\"");
129 return (CUPS_BACKEND_OK);
130 }
131 else if (argc < 6 || argc > 7)
132 {
133 fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
134 argv[0]);
135 return (CUPS_BACKEND_FAILED);
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)
144 {
145 fp = 0;
146 copies = 1;
147 }
148 else
149 {
150 /*
151 * Try to open the print file...
152 */
153
154 if ((fp = open(argv[6], O_RDONLY)) < 0)
155 {
156 perror("ERROR: unable to open print file");
157 return (CUPS_BACKEND_FAILED);
158 }
159
160 copies = atoi(argv[4]);
161 }
162
163 /*
164 * Extract the hostname and port number from the URI...
165 */
166
a4d04587 167 httpSeparateURI(HTTP_URI_CODING_ALL, cupsBackendDeviceURI(argv),
168 method, sizeof(method), username, sizeof(username),
169 hostname, sizeof(hostname), &port,
ef416fc2 170 resource, sizeof(resource));
171
172 if (port == 0)
173 port = 9100; /* Default to HP JetDirect/Tektronix PhaserShare */
174
fa73b229 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
ef416fc2 240 /*
241 * Then try to connect to the remote host...
242 */
243
244 sprintf(portname, "%d", port);
245
246 if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
247 {
248 fprintf(stderr, "ERROR: Unable to locate printer \'%s\'!\n", hostname);
249 return (CUPS_BACKEND_STOP);
250 }
251
252 fprintf(stderr, "INFO: Attempting to connect to host %s on port %d\n",
253 hostname, port);
254
255 wbytes = 0;
256
257 while (copies > 0)
258 {
757d2cad 259 fputs("STATE: +connecting-to-device\n", stderr);
260
ef416fc2 261 for (delay = 5;;)
262 {
263 if (!httpAddrConnect(addrlist, &fd))
264 {
265 error = errno;
266 fd = -1;
267
268 if (getenv("CLASS") != NULL)
269 {
270 /*
271 * If the CLASS environment variable is set, the job was submitted
272 * to a class and not to a specific queue. In this case, we want
273 * to abort immediately so that the job can be requeued on the next
274 * available printer in the class.
275 */
276
277 fprintf(stderr, "INFO: Unable to connect to \"%s\", queuing on next printer in class...\n",
278 hostname);
279
280 /*
281 * Sleep 5 seconds to keep the job from requeuing too rapidly...
282 */
283
284 sleep(5);
285
286 return (CUPS_BACKEND_FAILED);
287 }
288
289 if (error == ECONNREFUSED || error == EHOSTDOWN ||
290 error == EHOSTUNREACH)
291 {
292 fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in %d seconds...\n",
293 hostname, delay);
294 sleep(delay);
295
296 if (delay < 30)
297 delay += 5;
298 }
299 else
300 {
301 perror("ERROR: Unable to connect to printer (retrying in 30 seconds)");
302 sleep(30);
303 }
304 }
305 else
306 break;
307 }
308
757d2cad 309 fputs("STATE: -connecting-to-device\n", stderr);
310
ef416fc2 311 /*
312 * Now that we are "connected" to the port, ignore SIGTERM so that we
313 * can finish out any page data the driver sends (e.g. to eject the
314 * current page... Only ignore SIGTERM if we are printing data from
315 * stdin (otherwise you can't cancel raw jobs...)
316 */
317
318 if (argc < 7)
319 {
320#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
321 sigset(SIGTERM, SIG_IGN);
322#elif defined(HAVE_SIGACTION)
323 memset(&action, 0, sizeof(action));
324
325 sigemptyset(&action.sa_mask);
326 action.sa_handler = SIG_IGN;
327 sigaction(SIGTERM, &action, NULL);
328#else
329 signal(SIGTERM, SIG_IGN);
330#endif /* HAVE_SIGSET */
331 }
332
333 /*
334 * Finally, send the print file...
335 */
336
337 copies --;
338
339 if (fp != 0)
340 {
341 fputs("PAGE: 1 1\n", stderr);
342 lseek(fp, 0, SEEK_SET);
343 }
344
345 fputs("INFO: Connected to host, sending print job...\n", stderr);
346
347 tbytes = 0;
348 while ((nbytes = read(fp, buffer, sizeof(buffer))) > 0)
349 {
350 /*
351 * Write the print data to the printer...
352 */
353
354 tbytes += nbytes;
355 bufptr = buffer;
356
357 while (nbytes > 0)
358 {
359 /*
360 * See if we are ready to read or write...
361 */
362
363 do
364 {
365 FD_ZERO(&input);
366 FD_SET(fd, &input);
367 FD_ZERO(&output);
368 FD_SET(fd, &output);
369 }
370 while (select(fd + 1, &input, &output, NULL, NULL) < 0);
371
372 if (FD_ISSET(fd, &input))
373 {
374 /*
375 * Read backchannel data...
376 */
377
378 if ((rbytes = recv(fd, resource, sizeof(resource), 0)) > 0)
379 {
380 fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
381 rbytes);
bd7854cb 382 cupsBackChannelWrite(resource, rbytes, 1.0);
ef416fc2 383 }
384 }
385
386 if (FD_ISSET(fd, &output))
387 {
388 /*
389 * Write print data...
390 */
391
392 if ((wbytes = send(fd, bufptr, nbytes, 0)) < 0)
393 {
394 /*
395 * Check for retryable errors...
396 */
397
398 if (errno != EAGAIN && errno != EINTR)
399 {
400 perror("ERROR: Unable to send print file to printer");
401 break;
402 }
403 }
404 else
405 {
406 /*
407 * Update count and pointer...
408 */
409
410 nbytes -= wbytes;
411 bufptr += wbytes;
412 }
413 }
414 }
415
416 if (wbytes < 0)
417 break;
418
419 if (argc > 6)
420 fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
421 (unsigned long)tbytes);
422 }
423
fa73b229 424 if (waiteof)
ef416fc2 425 {
426 /*
fa73b229 427 * Shutdown the socket and wait for the other end to finish...
ef416fc2 428 */
429
fa73b229 430 fputs("INFO: Print file sent, waiting for printer to finish...\n", stderr);
ef416fc2 431
fa73b229 432 shutdown(fd, 1);
ef416fc2 433
fa73b229 434 for (;;)
ef416fc2 435 {
436 /*
fa73b229 437 * Wait a maximum of 90 seconds for backchannel data or a closed
438 * connection...
ef416fc2 439 */
440
fa73b229 441 timeout.tv_sec = 90;
442 timeout.tv_usec = 0;
443
444 FD_ZERO(&input);
445 FD_SET(fd, &input);
446
447 #ifdef __hpux
448 if (select(fd + 1, (int *)&input, NULL, NULL, &timeout) > 0)
449 #else
450 if (select(fd + 1, &input, NULL, NULL, &timeout) > 0)
451 #endif /* __hpux */
ef416fc2 452 {
fa73b229 453 /*
454 * Grab the data coming back and spit it out to stderr...
455 */
456
457 if ((rbytes = recv(fd, resource, sizeof(resource), 0)) > 0)
458 {
459 fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
460 rbytes);
bd7854cb 461 cupsBackChannelWrite(resource, rbytes, 1.0);
fa73b229 462 }
463 else
464 break;
465 }
ef416fc2 466 else
467 break;
468 }
ef416fc2 469 }
470
471 /*
472 * Close the socket connection...
473 */
474
475 close(fd);
476 }
477
478 httpAddrFreeList(addrlist);
479
480 /*
481 * Close the input file and return...
482 */
483
484 if (fp != 0)
485 close(fp);
486
487 if (wbytes >= 0)
488 fputs("INFO: Ready to print.\n", stderr);
489
490 return (wbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
491}
492
493
494/*
7a6a01dd 495 * End of "$Id$".
ef416fc2 496 */