]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/socket.c
Fix up IPv6 support, and remove remaining dirent checks.
[thirdparty/cups.git] / backend / socket.c
1 /*
2 * "$Id$"
3 *
4 * AppSocket backend for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2005 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/cups.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #include <cups/http-private.h>
40 #include <cups/string.h>
41 #include <errno.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <signal.h>
45
46 #ifdef WIN32
47 # include <winsock.h>
48 #else
49 # include <unistd.h>
50 # include <fcntl.h>
51 # include <sys/socket.h>
52 # include <netinet/in.h>
53 # include <arpa/inet.h>
54 # include <netdb.h>
55 #endif /* WIN32 */
56
57
58 /*
59 * Local functions...
60 */
61
62 void print_backchannel(const unsigned char *buffer, int nbytes);
63
64
65 /*
66 * 'main()' - Send a file to the printer or server.
67 *
68 * Usage:
69 *
70 * printer-uri job-id user title copies options [file]
71 */
72
73 int /* O - Exit status */
74 main(int argc, /* I - Number of command-line arguments (6 or 7) */
75 char *argv[]) /* I - Command-line arguments */
76 {
77 int i; /* Looping var */
78 char method[255], /* Method in URI */
79 hostname[1024], /* Hostname */
80 username[255], /* Username info (not used) */
81 resource[1024]; /* Resource info (not used) */
82 int fp; /* Print file */
83 int copies; /* Number of copies to print */
84 int port; /* Port number */
85 int delay; /* Delay for retries... */
86 int fd; /* AppSocket */
87 int error; /* Error code (if any) */
88 http_addr_t addr; /* Socket address */
89 struct hostent *hostaddr; /* Host address */
90 int wbytes; /* Number of bytes written */
91 int nbytes; /* Number of bytes read */
92 size_t tbytes; /* Total number of bytes written */
93 char buffer[8192], /* Output buffer */
94 *bufptr; /* Pointer into buffer */
95 struct timeval timeout; /* Timeout for select() */
96 fd_set input; /* Input 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 (0);
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 (1);
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 (1);
158 }
159
160 copies = atoi(argv[4]);
161 }
162
163 /*
164 * Extract the hostname and port number from the URI...
165 */
166
167 httpSeparate(argv[0], method, username, hostname, &port, resource);
168
169 if (port == 0)
170 port = 9100; /* Default to HP JetDirect/Tektronix PhaserShare */
171
172 /*
173 * Then try to connect to the remote host...
174 */
175
176 if ((hostaddr = httpGetHostByName(hostname)) == NULL)
177 {
178 fprintf(stderr, "ERROR: Unable to locate printer \'%s\' - %s\n",
179 hostname, hstrerror(h_errno));
180 return (1);
181 }
182
183 fprintf(stderr, "INFO: Attempting to connect to host %s on port %d\n",
184 hostname, port);
185
186 wbytes = 0;
187
188 while (copies > 0)
189 {
190 for (delay = 5;;)
191 {
192 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
193 {
194 perror("ERROR: Unable to create socket");
195 return (1);
196 }
197
198 for (i = 0; hostaddr->h_addr_list[i]; i ++)
199 {
200 httpAddrLoad(hostaddr, port, i, &addr);
201
202 if (!connect(fd, (struct sockaddr *)&addr, sizeof(addr)))
203 break;
204 }
205
206 if (!hostaddr->h_addr_list[i])
207 {
208 error = errno;
209 close(fd);
210 fd = -1;
211
212 if (getenv("CLASS") != NULL)
213 {
214 /*
215 * If the CLASS environment variable is set, the job was submitted
216 * to a class and not to a specific queue. In this case, we want
217 * to abort immediately so that the job can be requeued on the next
218 * available printer in the class.
219 */
220
221 fprintf(stderr, "INFO: Unable to connect to \"%s\", queuing on next printer in class...\n",
222 hostname);
223
224 /*
225 * Sleep 5 seconds to keep the job from requeuing too rapidly...
226 */
227
228 sleep(5);
229
230 return (1);
231 }
232
233 if (error == ECONNREFUSED || error == EHOSTDOWN ||
234 error == EHOSTUNREACH)
235 {
236 fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in %d seconds...\n",
237 hostname, delay);
238 sleep(delay);
239
240 if (delay < 30)
241 delay += 5;
242 }
243 else
244 {
245 perror("ERROR: Unable to connect to printer (retrying in 30 seconds)");
246 sleep(30);
247 }
248 }
249 else
250 break;
251 }
252
253 /*
254 * Now that we are "connected" to the port, ignore SIGTERM so that we
255 * can finish out any page data the driver sends (e.g. to eject the
256 * current page... Only ignore SIGTERM if we are printing data from
257 * stdin (otherwise you can't cancel raw jobs...)
258 */
259
260 if (argc < 7)
261 {
262 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
263 sigset(SIGTERM, SIG_IGN);
264 #elif defined(HAVE_SIGACTION)
265 memset(&action, 0, sizeof(action));
266
267 sigemptyset(&action.sa_mask);
268 action.sa_handler = SIG_IGN;
269 sigaction(SIGTERM, &action, NULL);
270 #else
271 signal(SIGTERM, SIG_IGN);
272 #endif /* HAVE_SIGSET */
273 }
274
275 /*
276 * Finally, send the print file...
277 */
278
279 copies --;
280
281 if (fp != 0)
282 {
283 fputs("PAGE: 1 1\n", stderr);
284 lseek(fp, 0, SEEK_SET);
285 }
286
287 fputs("INFO: Connected to host, sending print job...\n", stderr);
288
289 tbytes = 0;
290 while ((nbytes = read(fp, buffer, sizeof(buffer))) > 0)
291 {
292 /*
293 * Write the print data to the printer...
294 */
295
296 tbytes += nbytes;
297 bufptr = buffer;
298
299 while (nbytes > 0)
300 {
301 if ((wbytes = send(fd, bufptr, nbytes, 0)) < 0)
302 {
303 perror("ERROR: Unable to send print file to printer");
304 break;
305 }
306
307 nbytes -= wbytes;
308 bufptr += wbytes;
309 }
310
311 if (wbytes < 0)
312 break;
313
314 /*
315 * Check for possible data coming back from the printer...
316 */
317
318 timeout.tv_sec = 0;
319 timeout.tv_usec = 0;
320
321 FD_ZERO(&input);
322 FD_SET(fd, &input);
323 if (select(fd + 1, &input, NULL, NULL, &timeout) > 0)
324 {
325 /*
326 * Grab the data coming back and spit it out to stderr...
327 */
328
329 if ((nbytes = recv(fd, buffer, sizeof(buffer), 0)) > 0)
330 {
331 fprintf(stderr, "INFO: Received %d bytes of back-channel data!\n",
332 nbytes);
333 print_backchannel((unsigned char *)buffer, nbytes);
334 }
335 }
336 else if (argc > 6)
337 fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
338 (unsigned long)tbytes);
339 }
340
341 /*
342 * Shutdown the socket and wait for the other end to finish...
343 */
344
345 fputs("INFO: Print file sent, waiting for printer to finish...\n", stderr);
346
347 shutdown(fd, 1);
348
349 for (;;)
350 {
351 /*
352 * Wait a maximum of 90 seconds for backchannel data or a closed
353 * connection...
354 */
355
356 timeout.tv_sec = 90;
357 timeout.tv_usec = 0;
358
359 FD_ZERO(&input);
360 FD_SET(fd, &input);
361
362 #ifdef __hpux
363 if (select(fd + 1, (int *)&input, NULL, NULL, &timeout) > 0)
364 #else
365 if (select(fd + 1, &input, NULL, NULL, &timeout) > 0)
366 #endif /* __hpux */
367 {
368 /*
369 * Grab the data coming back and spit it out to stderr...
370 */
371
372 if ((nbytes = recv(fd, buffer, sizeof(buffer), 0)) > 0)
373 {
374 fprintf(stderr, "INFO: Received %d bytes of back-channel data!\n",
375 nbytes);
376 print_backchannel((unsigned char *)buffer, nbytes);
377 }
378 else
379 break;
380 }
381 else
382 break;
383 }
384
385 /*
386 * Close the socket connection...
387 */
388
389 close(fd);
390 }
391
392 /*
393 * Close the input file and return...
394 */
395
396 if (fp != 0)
397 close(fp);
398
399 return (wbytes < 0);
400 }
401
402
403 /*
404 * 'print_backchannel()' - Print the contents of a back-channel buffer.
405 */
406
407 void
408 print_backchannel(const unsigned char *buffer, /* I - Data buffer */
409 int nbytes) /* I - Number of bytes */
410 {
411 char line[255], /* Formatted line */
412 *lineptr; /* Pointer into line */
413
414
415 for (lineptr = line; nbytes > 0; buffer ++, nbytes --)
416 {
417 if (*buffer < 0x20 || *buffer >= 0x7f)
418 {
419 snprintf(lineptr, sizeof(line) - (lineptr - line), "<%02X>", *buffer);
420 lineptr += strlen(lineptr);
421 }
422 else
423 *lineptr++ = *buffer;
424
425 if ((lineptr - line) > 72)
426 {
427 *lineptr = '\0';
428 fprintf(stderr, "DEBUG: DATA: %s\n", line);
429 lineptr = line;
430 }
431 }
432
433 if (lineptr > line)
434 {
435 *lineptr = '\0';
436 fprintf(stderr, "DEBUG: DATA: %s\n", line);
437 }
438 }
439
440
441 /*
442 * End of "$Id$".
443 */