]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/socket.c
Revamp child signal handling to remove all processing from the handler
[thirdparty/cups.git] / backend / socket.c
1 /*
2 * "$Id: socket.c,v 1.39 2003/10/09 19:13:28 mike Exp $"
3 *
4 * AppSocket backend for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2003 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-3111 USA
19 *
20 * Voice: (301) 373-9603
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 char method[255], /* Method in URI */
78 hostname[1024], /* Hostname */
79 username[255], /* Username info (not used) */
80 resource[1024]; /* Resource info (not used) */
81 int fp; /* Print file */
82 int copies; /* Number of copies to print */
83 int port; /* Port number */
84 int delay; /* Delay for retries... */
85 int fd; /* AppSocket */
86 int error; /* Error code (if any) */
87 struct sockaddr_in addr; /* Socket address */
88 struct hostent *hostaddr; /* Host address */
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 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
97 struct sigaction action; /* Actions for POSIX signals */
98 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
99
100
101 /*
102 * Make sure status messages are not buffered...
103 */
104
105 setbuf(stderr, NULL);
106
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
121 /*
122 * Check command-line...
123 */
124
125 if (argc == 1)
126 {
127 puts("network socket \"Unknown\" \"AppSocket/HP JetDirect\"");
128 return (0);
129 }
130 else if (argc < 6 || argc > 7)
131 {
132 fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
133 argv[0]);
134 return (1);
135 }
136
137 /*
138 * If we have 7 arguments, print the file named on the command-line.
139 * Otherwise, send stdin instead...
140 */
141
142 if (argc == 6)
143 {
144 fp = 0;
145 copies = 1;
146 }
147 else
148 {
149 /*
150 * Try to open the print file...
151 */
152
153 if ((fp = open(argv[6], O_RDONLY)) < 0)
154 {
155 perror("ERROR: unable to open print file");
156 return (1);
157 }
158
159 copies = atoi(argv[4]);
160 }
161
162 /*
163 * Extract the hostname and port number from the URI...
164 */
165
166 httpSeparate(argv[0], method, username, hostname, &port, resource);
167
168 if (port == 0)
169 port = 9100; /* Default to HP JetDirect/Tektronix PhaserShare */
170
171 /*
172 * Then try to connect to the remote host...
173 */
174
175 if ((hostaddr = httpGetHostByName(hostname)) == NULL)
176 {
177 fprintf(stderr, "ERROR: Unable to locate printer \'%s\' - %s\n",
178 hostname, hstrerror(h_errno));
179 return (1);
180 }
181
182 fprintf(stderr, "INFO: Attempting to connect to host %s on port %d\n",
183 hostname, port);
184
185 memset(&addr, 0, sizeof(addr));
186 memcpy(&(addr.sin_addr), hostaddr->h_addr, hostaddr->h_length);
187 addr.sin_family = hostaddr->h_addrtype;
188 addr.sin_port = htons(port);
189
190 while (copies > 0)
191 {
192 for (delay = 5;;)
193 {
194 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
195 {
196 perror("ERROR: Unable to create socket");
197 return (1);
198 }
199
200 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
201 {
202 error = errno;
203 close(fd);
204 fd = -1;
205
206 if (error == ECONNREFUSED || error == EHOSTDOWN ||
207 error == EHOSTUNREACH)
208 {
209 fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in %d seconds...\n",
210 hostname, delay);
211 sleep(delay);
212
213 if (delay < 30)
214 delay += 5;
215 }
216 else
217 {
218 perror("ERROR: Unable to connect to printer (retrying in 30 seconds)");
219 sleep(30);
220 }
221 }
222 else
223 break;
224 }
225
226 /*
227 * Now that we are "connected" to the port, ignore SIGTERM so that we
228 * can finish out any page data the driver sends (e.g. to eject the
229 * current page... Only ignore SIGTERM if we are printing data from
230 * stdin (otherwise you can't cancel raw jobs...)
231 */
232
233 if (argc < 7)
234 {
235 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
236 sigset(SIGTERM, SIG_IGN);
237 #elif defined(HAVE_SIGACTION)
238 memset(&action, 0, sizeof(action));
239
240 sigemptyset(&action.sa_mask);
241 action.sa_handler = SIG_IGN;
242 sigaction(SIGTERM, &action, NULL);
243 #else
244 signal(SIGTERM, SIG_IGN);
245 #endif /* HAVE_SIGSET */
246 }
247
248 /*
249 * Finally, send the print file...
250 */
251
252 copies --;
253
254 if (fp != 0)
255 {
256 fputs("PAGE: 1 1\n", stderr);
257 lseek(fp, 0, SEEK_SET);
258 }
259
260 fputs("INFO: Connected to host, sending print job...\n", stderr);
261
262 tbytes = 0;
263 while ((nbytes = read(fp, buffer, sizeof(buffer))) > 0)
264 {
265 /*
266 * Write the print data to the printer...
267 */
268
269 tbytes += nbytes;
270 bufptr = buffer;
271
272 while (nbytes > 0)
273 {
274 if ((wbytes = send(fd, bufptr, nbytes, 0)) < 0)
275 {
276 perror("ERROR: Unable to send print file to printer");
277 break;
278 }
279
280 nbytes -= wbytes;
281 bufptr += wbytes;
282 }
283
284 if (wbytes < 0)
285 break;
286
287 /*
288 * Check for possible data coming back from the printer...
289 */
290
291 timeout.tv_sec = 0;
292 timeout.tv_usec = 0;
293
294 FD_ZERO(&input);
295 FD_SET(fd, &input);
296 if (select(fd + 1, &input, NULL, NULL, &timeout) > 0)
297 {
298 /*
299 * Grab the data coming back and spit it out to stderr...
300 */
301
302 if ((nbytes = recv(fd, buffer, sizeof(buffer), 0)) > 0)
303 {
304 fprintf(stderr, "INFO: Received %d bytes of back-channel data!\n",
305 nbytes);
306 print_backchannel((unsigned char *)buffer, nbytes);
307 }
308 }
309 else if (argc > 6)
310 fprintf(stderr, "INFO: Sending print file, %lu bytes...\n",
311 (unsigned long)tbytes);
312 }
313
314 /*
315 * Shutdown the socket and wait for the other end to finish...
316 */
317
318 fputs("INFO: Print file sent, waiting for printer to finish...\n", stderr);
319
320 shutdown(fd, 1);
321
322 for (;;)
323 {
324 /*
325 * Wait a maximum of 90 seconds for backchannel data or a closed
326 * connection...
327 */
328
329 timeout.tv_sec = 90;
330 timeout.tv_usec = 0;
331
332 FD_ZERO(&input);
333 FD_SET(fd, &input);
334
335 #ifdef __hpux
336 if (select(fd + 1, (int *)&input, NULL, NULL, &timeout) > 0)
337 #else
338 if (select(fd + 1, &input, NULL, NULL, &timeout) > 0)
339 #endif /* __hpux */
340 {
341 /*
342 * Grab the data coming back and spit it out to stderr...
343 */
344
345 if ((nbytes = recv(fd, buffer, sizeof(buffer), 0)) > 0)
346 {
347 fprintf(stderr, "INFO: Received %d bytes of back-channel data!\n",
348 nbytes);
349 print_backchannel((unsigned char *)buffer, nbytes);
350 }
351 else
352 break;
353 }
354 else
355 break;
356 }
357
358 /*
359 * Close the socket connection...
360 */
361
362 close(fd);
363 }
364
365 /*
366 * Close the input file and return...
367 */
368
369 if (fp != 0)
370 close(fp);
371
372 fputs("INFO: Ready to print.\n", stderr);
373
374 return (0);
375 }
376
377
378 /*
379 * 'print_backchannel()' - Print the contents of a back-channel buffer.
380 */
381
382 void
383 print_backchannel(const unsigned char *buffer, /* I - Data buffer */
384 int nbytes) /* I - Number of bytes */
385 {
386 char line[255], /* Formatted line */
387 *lineptr; /* Pointer into line */
388
389
390 for (lineptr = line; nbytes > 0; buffer ++, nbytes --)
391 {
392 if (*buffer < 0x20 || *buffer >= 0x7f)
393 {
394 snprintf(lineptr, sizeof(line) - (lineptr - line), "<%02X>", *buffer);
395 lineptr += strlen(lineptr);
396 }
397 else
398 *lineptr++ = *buffer;
399
400 if ((lineptr - line) > 72)
401 {
402 *lineptr = '\0';
403 fprintf(stderr, "DEBUG: DATA: %s\n", line);
404 lineptr = line;
405 }
406 }
407
408 if (lineptr > line)
409 {
410 *lineptr = '\0';
411 fprintf(stderr, "DEBUG: DATA: %s\n", line);
412 }
413 }
414
415
416 /*
417 * End of "$Id: socket.c,v 1.39 2003/10/09 19:13:28 mike Exp $".
418 */