]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/socket.c
Load cups into easysw/current.
[thirdparty/cups.git] / backend / socket.c
CommitLineData
ef416fc2 1/*
2 * "$Id: socket.c 4906 2006-01-10 20:53:28Z mike $"
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) */
74 resource[1024]; /* Resource info (not used) */
75 int fp; /* Print file */
76 int copies; /* Number of copies to print */
77 int port; /* Port number */
78 char portname[255]; /* Port name */
79 int delay; /* Delay for retries... */
80 int fd; /* AppSocket */
81 int error; /* Error code (if any) */
82 http_addrlist_t *addrlist; /* Address list */
83 int rbytes; /* Number of bytes read */
84 int wbytes; /* Number of bytes written */
85 int nbytes; /* Number of bytes read */
86 size_t tbytes; /* Total number of bytes written */
87 char buffer[8192], /* Output buffer */
88 *bufptr; /* Pointer into buffer */
89 struct timeval timeout; /* Timeout for select() */
90 fd_set input, /* Input set for select() */
91 output; /* Output set for select() */
92#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
93 struct sigaction action; /* Actions for POSIX signals */
94#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
95
96
97 /*
98 * Make sure status messages are not buffered...
99 */
100
101 setbuf(stderr, NULL);
102
103 /*
104 * Ignore SIGPIPE signals...
105 */
106
107#ifdef HAVE_SIGSET
108 sigset(SIGPIPE, SIG_IGN);
109#elif defined(HAVE_SIGACTION)
110 memset(&action, 0, sizeof(action));
111 action.sa_handler = SIG_IGN;
112 sigaction(SIGPIPE, &action, NULL);
113#else
114 signal(SIGPIPE, SIG_IGN);
115#endif /* HAVE_SIGSET */
116
117 /*
118 * Check command-line...
119 */
120
121 if (argc == 1)
122 {
123 puts("network socket \"Unknown\" \"AppSocket/HP JetDirect\"");
124 return (CUPS_BACKEND_OK);
125 }
126 else if (argc < 6 || argc > 7)
127 {
128 fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
129 argv[0]);
130 return (CUPS_BACKEND_FAILED);
131 }
132
133 /*
134 * If we have 7 arguments, print the file named on the command-line.
135 * Otherwise, send stdin instead...
136 */
137
138 if (argc == 6)
139 {
140 fp = 0;
141 copies = 1;
142 }
143 else
144 {
145 /*
146 * Try to open the print file...
147 */
148
149 if ((fp = open(argv[6], O_RDONLY)) < 0)
150 {
151 perror("ERROR: unable to open print file");
152 return (CUPS_BACKEND_FAILED);
153 }
154
155 copies = atoi(argv[4]);
156 }
157
158 /*
159 * Extract the hostname and port number from the URI...
160 */
161
162 httpSeparateURI(argv[0], method, sizeof(method), username, sizeof(username),
163 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 * Then try to connect to the remote host...
171 */
172
173 sprintf(portname, "%d", port);
174
175 if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
176 {
177 fprintf(stderr, "ERROR: Unable to locate printer \'%s\'!\n", hostname);
178 return (CUPS_BACKEND_STOP);
179 }
180
181 fprintf(stderr, "INFO: Attempting to connect to host %s on port %d\n",
182 hostname, port);
183
184 wbytes = 0;
185
186 while (copies > 0)
187 {
188 for (delay = 5;;)
189 {
190 if (!httpAddrConnect(addrlist, &fd))
191 {
192 error = errno;
193 fd = -1;
194
195 if (getenv("CLASS") != NULL)
196 {
197 /*
198 * If the CLASS environment variable is set, the job was submitted
199 * to a class and not to a specific queue. In this case, we want
200 * to abort immediately so that the job can be requeued on the next
201 * available printer in the class.
202 */
203
204 fprintf(stderr, "INFO: Unable to connect to \"%s\", queuing on next printer in class...\n",
205 hostname);
206
207 /*
208 * Sleep 5 seconds to keep the job from requeuing too rapidly...
209 */
210
211 sleep(5);
212
213 return (CUPS_BACKEND_FAILED);
214 }
215
216 if (error == ECONNREFUSED || error == EHOSTDOWN ||
217 error == EHOSTUNREACH)
218 {
219 fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in %d seconds...\n",
220 hostname, delay);
221 sleep(delay);
222
223 if (delay < 30)
224 delay += 5;
225 }
226 else
227 {
228 perror("ERROR: Unable to connect to printer (retrying in 30 seconds)");
229 sleep(30);
230 }
231 }
232 else
233 break;
234 }
235
236 /*
237 * Now that we are "connected" to the port, ignore SIGTERM so that we
238 * can finish out any page data the driver sends (e.g. to eject the
239 * current page... Only ignore SIGTERM if we are printing data from
240 * stdin (otherwise you can't cancel raw jobs...)
241 */
242
243 if (argc < 7)
244 {
245#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
246 sigset(SIGTERM, SIG_IGN);
247#elif defined(HAVE_SIGACTION)
248 memset(&action, 0, sizeof(action));
249
250 sigemptyset(&action.sa_mask);
251 action.sa_handler = SIG_IGN;
252 sigaction(SIGTERM, &action, NULL);
253#else
254 signal(SIGTERM, SIG_IGN);
255#endif /* HAVE_SIGSET */
256 }
257
258 /*
259 * Finally, send the print file...
260 */
261
262 copies --;
263
264 if (fp != 0)
265 {
266 fputs("PAGE: 1 1\n", stderr);
267 lseek(fp, 0, SEEK_SET);
268 }
269
270 fputs("INFO: Connected to host, sending print job...\n", stderr);
271
272 tbytes = 0;
273 while ((nbytes = read(fp, buffer, sizeof(buffer))) > 0)
274 {
275 /*
276 * Write the print data to the printer...
277 */
278
279 tbytes += nbytes;
280 bufptr = buffer;
281
282 while (nbytes > 0)
283 {
284 /*
285 * See if we are ready to read or write...
286 */
287
288 do
289 {
290 FD_ZERO(&input);
291 FD_SET(fd, &input);
292 FD_ZERO(&output);
293 FD_SET(fd, &output);
294 }
295 while (select(fd + 1, &input, &output, NULL, NULL) < 0);
296
297 if (FD_ISSET(fd, &input))
298 {
299 /*
300 * Read backchannel data...
301 */
302
303 if ((rbytes = recv(fd, resource, sizeof(resource), 0)) > 0)
304 {
305 fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
306 rbytes);
307 cupsBackchannelWrite(resource, rbytes, 1.0);
308 }
309 }
310
311 if (FD_ISSET(fd, &output))
312 {
313 /*
314 * Write print data...
315 */
316
317 if ((wbytes = send(fd, bufptr, nbytes, 0)) < 0)
318 {
319 /*
320 * Check for retryable errors...
321 */
322
323 if (errno != EAGAIN && errno != EINTR)
324 {
325 perror("ERROR: Unable to send print file to printer");
326 break;
327 }
328 }
329 else
330 {
331 /*
332 * Update count and pointer...
333 */
334
335 nbytes -= wbytes;
336 bufptr += wbytes;
337 }
338 }
339 }
340
341 if (wbytes < 0)
342 break;
343
344 if (argc > 6)
345 fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
346 (unsigned long)tbytes);
347 }
348
349 /*
350 * Shutdown the socket and wait for the other end to finish...
351 */
352
353 fputs("INFO: Print file sent, waiting for printer to finish...\n", stderr);
354
355 shutdown(fd, 1);
356
357 for (;;)
358 {
359 /*
360 * Wait a maximum of 90 seconds for backchannel data or a closed
361 * connection...
362 */
363
364 timeout.tv_sec = 90;
365 timeout.tv_usec = 0;
366
367 FD_ZERO(&input);
368 FD_SET(fd, &input);
369
370#ifdef __hpux
371 if (select(fd + 1, (int *)&input, NULL, NULL, &timeout) > 0)
372#else
373 if (select(fd + 1, &input, NULL, NULL, &timeout) > 0)
374#endif /* __hpux */
375 {
376 /*
377 * Grab the data coming back and spit it out to stderr...
378 */
379
380 if ((rbytes = recv(fd, resource, sizeof(resource), 0)) > 0)
381 {
382 fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n",
383 rbytes);
384 cupsBackchannelWrite(resource, rbytes, 1.0);
385 }
386 else
387 break;
388 }
389 else
390 break;
391 }
392
393 /*
394 * Close the socket connection...
395 */
396
397 close(fd);
398 }
399
400 httpAddrFreeList(addrlist);
401
402 /*
403 * Close the input file and return...
404 */
405
406 if (fp != 0)
407 close(fp);
408
409 if (wbytes >= 0)
410 fputs("INFO: Ready to print.\n", stderr);
411
412 return (wbytes < 0 ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
413}
414
415
416/*
417 * End of "$Id: socket.c 4906 2006-01-10 20:53:28Z mike $".
418 */