1 /*
2 * pdftoippprinter.c
3 *
4 * Function to convert PDF into PWG/Apple Raster and PCLm for printing
5 * on driverless IPP printers.
6 *
7 * Copyright 2007-2011 by Apple Inc.
8 * Copyright 1997-2006 by Easy Software Products.
9 * Copyright 2011-2013 by Till Kamppeter
10 *
11 * Contents:
12 *
13 * apply_filters() - Main function...
14 * cancel_job() - Flag the job as canceled.
15 * filter_present() - Is the requested filter actually installed?
16 * compare_pids() - Compare process IDs for sorting PID list
17 * exec_filter() - Execute a filter process
18 * exec_filters() - Execute a filter chain
19 * open_pipe() - Create a pipe to transfer data from filter to filter
20 * get_option_in_str() - Get an option value from a string like argv[5]
21 * set_option_in_str() - Set an option value in a string like argv[5]
22 */
23
24 /*
25 * Include necessary headers...
26 */
27
28 #include <config.h>
29 #include <cups/cups.h>
30 #include <cups/ppd.h>
31 #include <cups/file.h>
32 #include <signal.h>
33 #include <sys/wait.h>
34 #include <limits.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <cupsfilters/image-private.h>
41
42 #define MAX_CHECK_COMMENT_LINES 20
43
44 /*
45 * Type definitions
46 */
47
48 typedef unsigned output_format_t;
49 enum output_format_e {PDF = 0, POSTSCRIPT = 1, PWGRASTER = 2, PCLXL = 3, PCL = 4, APPLERASTER = 5, PCLM = 6};
50 typedef struct filter_pid_s /* Filter in filter chain */
51 {
52 char *name; /* Filter executable name */
53 int pid; /* PID of filter process */
54 } filter_pid_t;
55
56 /*
57 * Local functions...
58 */
59
60 static void cancel_job(int sig);
61 static int filter_present(const char *filter);
62 static int compare_pids(filter_pid_t *a, filter_pid_t *b);
63 static int exec_filter(const char *filter, char **argv,
64 int infd, int outfd);
65 static int exec_filters(cups_array_t *filters, char **argv);
66 static int open_pipe(int *fds);
67 static char* get_option_in_str(char *buf, const char *option,
68 int return_value);
69
70
71 /*
72 * Local globals...
73 */
74
75 static int job_canceled = 0;
76
77 /*
78 * Set an option in a string of options
79 */
80
81 void /* O - 0 on success, 1 on error */
set_option_in_str(char * buf,int buflen,const char * option,const char * value)82 set_option_in_str(char *buf, /* I - Buffer with option list string */
83 int buflen, /* I - Length of buffer */
84 const char *option, /* I - Option to change/add */
85 const char *value) /* I - New value for option, NULL
86 removes option */
87 {
88 char *p1, *p2, *p3;
89
90 if (!buf || buflen == 0 || !option)
91 return;
92 /* Remove any occurrence of option in the string */
93 p1 = buf;
94 while (*p1 != '\0' && (p2 = strcasestr(p1, option)) != NULL) {
95 if (p2 > buf && *(p2 - 1) != ' ' && *(p2 - 1) != '\t') {
96 p1 = p2 + 1;
97 continue;
98 }
99 p1 = p2 + strlen(option);
100 if(!strcmp(option,"cups-browsed")){
101 fprintf(stderr, "DEBUG: Removing option cups-browsed if it is present\n");
102 }else if (*p1 != '=' && *p1 != ' ' && *p1 != '\t' && *p1 != '\0')
103 continue;
104 if(!strcmp(option,"cups-browsed-dest-printer")){
105 fprintf(stderr, "DEBUG: Removing cups-browsed-dest-printer option from arguments\n");
106 p3 = strchr(p1, '"');
107 p3++;
108 p1 = strchr(p3, '"');
109 }
110 while (*p1 != ' ' && *p1 != '\t' && *p1 != '\0') p1 ++;
111 while ((*p1 == ' ' || *p1 == '\t') && *p1 != '\0') p1 ++;
112 memmove(p2, p1, strlen(buf) - (p1 - buf) + 1);
113 p1 = p2;
114 }
115 /* Add option=value to the end of the string */
116 if (!value)
117 return;
118 p1 = buf + strlen(buf);
119 *p1 = ' ';
120 p1 ++;
121 snprintf(p1, buflen - (p1 - buf), "%s=%s", option, value);
122 buf[buflen - 1] = '\0';
123 }
124
125
126 /*
127 * 'apply_filters()' - Main function...
128 */
129
130 int
apply_filters(int argc,char * argv[])131 apply_filters(int argc, char *argv[])
132 {
133 int i; /* Looping var */
134 output_format_t output_format; /* Output format */
135 int fd = 0; /* Copy file descriptor */
136 char *filename, /* PDF file to convert */
137 tempfile[1024]; /* Temporary file */
138 char buffer[8192]; /* Copy buffer */
139 int bytes; /* Bytes copied */
140 int num_options; /* Number of options */
141 cups_option_t *options; /* Options */
142 const char *val; /* Option value */
143 char *argv_nt[8]; /* NULL-terminated array of the command
144 line arguments */
145 int optbuflen;
146 cups_array_t *filter_chain; /* Filter chain to execute */
147 int exit_status = 0; /* Exit status */
148 int color_printing; /* Do we print in color? */
149 char *filter, *p;
150 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
151 struct sigaction action; /* Actions for POSIX signals */
152 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
153 static const char * const color_mode_option_names[] =
154 { /* Possible names for a color mode option */
155 "pwg-raster-document-type",
156 "PwgRasterDocumentType",
157 "print-color-mode",
158 "PrintColorMode",
159 "color-space",
160 "ColorSpace",
161 "color-model",
162 "ColorModel",
163 NULL
164 };
165
166
167 /*
168 * Make sure status messages are not buffered...
169 */
170
171 setbuf(stderr, NULL);
172
173 /*
174 * Ignore broken pipe signals...
175 */
176
177 signal(SIGPIPE, SIG_IGN);
178
179 /*
180 * Make sure we have the right number of arguments for CUPS!
181 */
182
183 if (argc < 6 || argc > 7)
184 {
185 fprintf(stderr, "Usage: %s job user title copies options [file]\n",
186 argv[0]);
187 return (1);
188 }
189
190 /*
191 * Register a signal handler to cleanly cancel a job.
192 */
193
194 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
195 sigset(SIGTERM, cancel_job);
196 #elif defined(HAVE_SIGACTION)
197 memset(&action, 0, sizeof(action));
198
199 sigemptyset(&action.sa_mask);
200 action.sa_handler = cancel_job;
201 sigaction(SIGTERM, &action, NULL);
202 #else
203 signal(SIGTERM, cancel_job);
204 #endif /* HAVE_SIGSET */
205
206 /*
207 * Copy stdin if needed...
208 */
209
210 if (argc == 6) {
211 /*
212 * Copy stdin to a temp file...
213 */
214
215 if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) {
216 perror("DEBUG: Unable to copy PDF file");
217 return (1);
218 }
219
220 fprintf(stderr,
221 "DEBUG: sys5ippprinter - copying to temp print file \"%s\"\n",
222 tempfile);
223
224 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
225 bytes = write(fd, buffer, bytes);
226
227 close(fd);
228
229 filename = tempfile;
230 } else {
231 /*
232 * Use the filename on the command-line...
233 */
234
235 filename = argv[6];
236 tempfile[0] = '\0';
237 }
238
239 /*
240 * Get the options from the fifth command line argument
241 */
242
243 num_options = cupsParseOptions(argv[5], 0, &options);
244
245 /*
246 * Copy the command line arguments into a NULL-terminated array
247 */
248
249 for (i = 0; i < 5; i++)
250 argv_nt[i] = argv[i];
251 /* We copy the contents of argv[5] into a somewhat larger buffer so that
252 we can manipulate it */
253 optbuflen = strlen(argv[5]) + 256;
254 argv_nt[5] = calloc(optbuflen, sizeof(char));
255 strcpy(argv_nt[5], (const char*)argv[5]);
256 argv_nt[6] = filename;
257 argv_nt[7] = NULL;
258
259 /*
260 * Create filter chain
261 */
262
263 filter_chain = cupsArrayNew(NULL, NULL);
264
265 /*
266 * Add the gziptoany filter if installed
267 */
268
269 if (filter_present("gziptoany"))
270 cupsArrayAdd(filter_chain, "gziptoany");
271
272 /*
273 * Select the output format: PDF, PostScript, PWG Raster, PCL-XL, and
274 * PCL 5c/e
275 * Add the needed filters to the filter chain
276 */
277
278 if ((val = cupsGetOption("output-format", num_options, options)) != NULL) {
279 if (!strcmp(val, "raster")) {
280 output_format = PWGRASTER;
281 /* PWG Raster output */
282 set_option_in_str(argv_nt[5], optbuflen, "MediaClass", NULL);
283 set_option_in_str(argv_nt[5], optbuflen, "media-class", "pwg");
284 /* Page logging into page_log is not done by gstoraster/pdftoraster,
285 so let it be done by pdftopdf */
286 set_option_in_str(argv_nt[5], optbuflen, "page-logging", "on");
287 if (filter_present("gstoraster"))
288 cupsArrayAdd(filter_chain, "gstoraster");
289 else {
290 fprintf(stderr,
291 "DEBUG: Filter gstoraster missing for \"output-format=%s\", using pdftoraster.\n",
292 val);
293 if (filter_present("pdftoraster"))
294 cupsArrayAdd(filter_chain, "pdftoraster");
295 else {
296 fprintf(stderr,
297 "ERROR: Filter pdftoraster missing for \"output-format=%s\"\n",
298 val);
299 exit_status = 1;
300 goto error;
301 }
302 setenv("FINAL_CONTENT_TYPE", "pwg", 1);
303 }
304 } else if (!strcmp(val, "apple-raster")) {
305 output_format = APPLERASTER;
306 /* PWG Raster output */
307 set_option_in_str(argv_nt[5], optbuflen, "MediaClass", NULL);
308 set_option_in_str(argv_nt[5], optbuflen, "media-class", "");
309 /* Page logging into page_log is not done by gstoraster/pdftoraster,
310 so let it be done by pdftopdf */
311 set_option_in_str(argv_nt[5], optbuflen, "page-logging", "on");
312 if (filter_present("gstoraster"))
313 cupsArrayAdd(filter_chain, "gstoraster");
314 else {
315 fprintf(stderr,
316 "DEBUG: Filter gstoraster missing for \"output-format=%s\", using pdftoraster.\n",
317 val);
318 if (filter_present("pdftoraster"))
319 cupsArrayAdd(filter_chain, "pdftoraster");
320 else {
321 fprintf(stderr,
322 "ERROR: Filter pdftoraster missing for \"output-format=%s\"\n",
323 val);
324 exit_status = 1;
325 goto error;
326 }
327 }
328 if (filter_present("rastertopwg"))
329 cupsArrayAdd(filter_chain, "rastertopwg");
330 else {
331 fprintf(stderr,
332 "ERROR: Filter rastertopwg missing for \"output-format=%s\"\n",
333 val);
334 exit_status = 1;
335 goto error;
336 }
337 setenv("FINAL_CONTENT_TYPE", "image/urf", 1);
338 } else if (!strcmp(val, "pdf")) {
339 output_format = PDF;
340 /* Page logging into page_log has to be done by pdftopdf */
341 set_option_in_str(argv_nt[5], optbuflen, "page-logging", "on");
342 } else if (!strcmp(val, "postscript")) {
343 output_format = POSTSCRIPT;
344 /* Page logging into page_log is done by pstops, so no need by
345 pdftopdf */
346 set_option_in_str(argv_nt[5], optbuflen, "page-logging", "off");
347 if (filter_present("pdftops")) {
348 cupsArrayAdd(filter_chain, "pdftops");
349 if (!filter_present("gstoraster")) {
350 fprintf(stderr,
351 "DEBUG: For \"output-format=%s\" using only Poppler's pdftops as we have no Ghostscript support.\n",
352 val);
353 set_option_in_str(argv_nt[5], optbuflen, "pdftops-renderer",
354 "pdftops");
355 } else if (!filter_present("pdftoraster")) {
356 fprintf(stderr,
357 "DEBUG: For \"output-format=%s\", using only Ghostscript as we have no Poppler support.\n",
358 val);
359 set_option_in_str(argv_nt[5], optbuflen, "pdftops-renderer",
360 "gs");
361 } else
362 set_option_in_str(argv_nt[5], optbuflen, "pdftops-renderer",
363 "hybrid");
364 } else {
365 fprintf(stderr,
366 "ERROR: Filter pdftops missing for \"output-format=%s\"\n",
367 val);
368 exit_status = 1;
369 goto error;
370 }
371 } else if ((p = strcasestr(val, "pcl")) != NULL) {
372 if (!strcasecmp(p, "pclxl")) {
373 output_format = PCLXL;
374 if (filter_present("gstopxl")) {
375 cupsArrayAdd(filter_chain, "gstopxl");
376 /* Page logging into page_log is not done by gstopxl,
377 so let it be done by pdftopdf */
378 set_option_in_str(argv_nt[5], optbuflen, "page-logging", "on");
379 } else {
380 fprintf(stderr,
381 "DEBUG: Filter gstopxl missing for \"output-format=%s\", falling back to PCL 5c/e.\n",
382 val);
383 output_format = PCL;
384 }
385 } else if (!strcasecmp(p, "pclm")) {
386 output_format = PCLM;
387 /* PWG Raster output */
388 set_option_in_str(argv_nt[5], optbuflen, "MediaClass", NULL);
389 set_option_in_str(argv_nt[5], optbuflen, "media-class", "");
390 /* Page logging into page_log is not done by gstoraster/pdftoraster,
391 so let it be done by pdftopdf */
392 set_option_in_str(argv_nt[5], optbuflen, "page-logging", "on");
393 if (filter_present("gstoraster"))
394 cupsArrayAdd(filter_chain, "gstoraster");
395 else {
396 fprintf(stderr,
397 "DEBUG: Filter gstoraster missing for \"output-format=%s\", using pdftoraster.\n",
398 val);
399 if (filter_present("pdftoraster"))
400 cupsArrayAdd(filter_chain, "pdftoraster");
401 else {
402 fprintf(stderr,
403 "ERROR: Filter pdftoraster missing for \"output-format=%s\"\n",
404 val);
405 exit_status = 1;
406 goto error;
407 }
408 }
409 if (filter_present("rastertopclm"))
410 cupsArrayAdd(filter_chain, "rastertopclm");
411 else {
412 fprintf(stderr,
413 "ERROR: Filter rastertopclm missing for \"output-format=%s\"\n",
414 val);
415 exit_status = 1;
416 goto error;
417 }
418 } else {
419 output_format = PCL;
420 }
421 } else {
422 fprintf(stderr,
423 "ERROR: Invalid value for \"output-format\": \"%s\"\n", val);
424 exit_status = 1;
425 goto error;
426 }
427 } else {
428 fprintf(stderr,
429 "ERROR: Missing option \"output-format\".\n");
430 exit_status = 1;
431 goto error;
432 }
433 if (output_format == PCL) {
434 /* We need CUPS Raster as we want to use rastertopclx with unprintable
435 margins */
436 set_option_in_str(argv_nt[5], optbuflen, "MediaClass", NULL);
437 set_option_in_str(argv_nt[5], optbuflen, "media-class", "");
438 /* Page logging into page_log is done by rastertopclx, so no need by
439 pdftopdf */
440 set_option_in_str(argv_nt[5], optbuflen, "page-logging", "off");
441 /* Does the client send info about margins? */
442 if (!get_option_in_str(argv_nt[5], "media-left-margin", 0) &&
443 !get_option_in_str(argv_nt[5], "media-right-margin", 0) &&
444 !get_option_in_str(argv_nt[5], "media-top-margin", 0) &&
445 !get_option_in_str(argv_nt[5], "media-bottom-margin", 0)) {
446 /* Set default 12pt margins if there is no info about printer's
447 unprintable margins (100th of mm units, 12.0 * 2540.0 / 72.0 = 423.33)
448 */
449 set_option_in_str(argv_nt[5], optbuflen, "media-left-margin", "423.33");
450 set_option_in_str(argv_nt[5], optbuflen, "media-right-margin", "423.33");
451 set_option_in_str(argv_nt[5], optbuflen, "media-top-margin", "423.33");
452 set_option_in_str(argv_nt[5], optbuflen, "media-bottom-margin", "423.33");
453 }
454 /* Check whether the job is requested to be printed in color and if so,
455 set the color space to RGB as this is the best color printing support
456 in PCL 5c */
457 color_printing = 0;
458 for (i = 0; color_mode_option_names[i]; i ++) {
459 p = get_option_in_str(argv_nt[5], color_mode_option_names[i], 1);
460 if (p && (strcasestr(p, "RGB") || strcasestr(p, "CMY") ||
461 strcasestr(p, "color"))) {
462 color_printing = 1;
463 break;
464 }
465 }
466 if (color_printing == 1) {
467 /* Remove unneeded color mode options */
468 for (i = 0; color_mode_option_names[i]; i ++)
469 set_option_in_str(argv_nt[5], optbuflen, color_mode_option_names[i],
470 NULL);
471 /* Set RGB as color mode */
472 set_option_in_str(argv_nt[5], optbuflen, "print-color-mode", "RGB");
473 }
474 if (filter_present("gstoraster"))
475 cupsArrayAdd(filter_chain, "gstoraster");
476 else {
477 fprintf(stderr,
478 "DEBUG: Filter gstoraster missing for \"output-format=%s\", using pdftoraster.\n",
479 val);
480 if (filter_present("pdftoraster"))
481 cupsArrayAdd(filter_chain, "pdftoraster");
482 else {
483 fprintf(stderr,
484 "ERROR: Filter pdftoraster missing for \"output-format=%s\"\n",
485 val);
486 exit_status = 1;
487 goto error;
488 }
489 }
490 if (filter_present("rastertopclx"))
491 cupsArrayAdd(filter_chain, "rastertopclx");
492 else {
493 fprintf(stderr,
494 "ERROR: Filter rastertopclx missing for \"output-format=%s\"\n",
495 val);
496 exit_status = 1;
497 goto error;
498 }
499 }
500
501 fprintf(stderr,
502 "DEBUG: Printer supports output formats: %s\nDEBUG: Using following CUPS filter chain to convert input data to the %s format:",
503 val,
504 output_format == PDF ? "PDF" :
505 (output_format == POSTSCRIPT ? "Postscript" :
506 (output_format == PWGRASTER ? "PWG Raster" :
507 (output_format == PCLXL ? "PCL XL" :
508 (output_format == PCL ? "PCL 5c/e" :
509 (output_format == APPLERASTER ? "APPLE Raster" :
510 (output_format == PCLM ? "PCLm" : "unknown")))))));
511 for (filter = (char *)cupsArrayFirst(filter_chain);
512 filter;
513 filter = (char *)cupsArrayNext(filter_chain))
514 fprintf(stderr, " %s", filter);
515 fprintf(stderr, "\n");
516
517 /*
518 * Execute the filter chain
519 */
520
521 exit_status = exec_filters(filter_chain, (char **)argv_nt);
522
523 /*
524 * Cleanup and exit...
525 */
526
527 error:
528
529 if (tempfile[0])
530 unlink(tempfile);
531
532 return (exit_status);
533 }
534
535
536 /*
537 * 'cancel_job()' - Flag the job as canceled.
538 */
539
540 static void
cancel_job(int sig)541 cancel_job(int sig) /* I - Signal number (unused) */
542 {
543 (void)sig;
544
545 job_canceled = 1;
546 }
547
548
549 static int
filter_present(const char * filter)550 filter_present(const char *filter) /* I - Filter name */
551 {
552 char filter_path[1024]; /* Path to filter executable */
553 const char *cups_serverbin; /* CUPS_SERVERBIN environment variable */
554
555 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
556 cups_serverbin = CUPS_SERVERBIN;
557
558 snprintf(filter_path, sizeof(filter_path), "%s/filter/%s",
559 cups_serverbin, filter);
560
561 if (access(filter_path, X_OK) == 0)
562 return 1;
563
564 return 0;
565 }
566
567
568 /*
569 * 'compare_pids()' - Compare two filter PIDs...
570 */
571
572 static int /* O - Result of comparison */
compare_pids(filter_pid_t * a,filter_pid_t * b)573 compare_pids(filter_pid_t *a, /* I - First filter */
574 filter_pid_t *b) /* I - Second filter */
575 {
576 return (a->pid - b->pid);
577 }
578
579
580 /*
581 * 'exec_filter()' - Execute a single filter.
582 */
583
584 static int /* O - Process ID or -1 on error */
exec_filter(const char * filter,char ** argv,int infd,int outfd)585 exec_filter(const char *filter, /* I - Filter to execute */
586 char **argv, /* I - Original command line args */
587 int infd, /* I - Stdin file descriptor */
588 int outfd) /* I - Stdout file descriptor */
589 {
590 int pid, /* Process ID */
591 fd; /* Temporary file descriptor */
592
593 if ((pid = fork()) == 0) {
594 /*
595 * Child process goes here...
596 *
597 * Update stdin/stdout/stderr as needed...
598 */
599
600 if (infd != 0) {
601 if (infd < 0)
602 infd = open("/dev/null", O_RDONLY);
603
604 if (infd > 0) {
605 dup2(infd, 0);
606 close(infd);
607 }
608 }
609
610 if (outfd != 1) {
611 if (outfd < 0)
612 outfd = open("/dev/null", O_WRONLY);
613
614 if (outfd > 1) {
615 dup2(outfd, 1);
616 close(outfd);
617 }
618 }
619
620 /* Send stderr to the Nirwana if we are running gziptoany, as
621 gziptoany emits a false "PAGE: 1 1" */
622 if (strcasestr(filter, "gziptoany")) {
623 if ((fd = open("/dev/null", O_RDWR)) > 2) {
624 dup2(fd, 2);
625 close(fd);
626 } else
627 close(fd);
628 fcntl(2, F_SETFL, O_NDELAY);
629 }
630
631 if ((fd = open("/dev/null", O_RDWR)) > 3) {
632 dup2(fd, 3);
633 close(fd);
634 }
635 else
636 close(fd);
637 fcntl(3, F_SETFL, O_NDELAY);
638
639 if ((fd = open("/dev/null", O_RDWR)) > 4) {
640 dup2(fd, 4);
641 close(fd);
642 } else
643 close(fd);
644 fcntl(4, F_SETFL, O_NDELAY);
645
646 /*
647 * Execute command...
648 */
649
650 execvp(filter, argv);
651
652 perror(filter);
653
654 exit(errno);
655 }
656
657 return (pid);
658 }
659
660
661 /*
662 * 'exec_filters()' - Execute filters for the given file and options.
663 */
664
665 static int /* O - 0 on success, 1 on error */
exec_filters(cups_array_t * filters,char ** argv)666 exec_filters(cups_array_t *filters, /* I - Array of filters to run */
667 char **argv) /* I - Filter options */
668 {
669 int i; /* Looping var */
670 char program[1024]; /* Program to run */
671 char *filter, /* Current filter */
672 *next; /* Next filter */
673 int current, /* Current filter */
674 filterfds[2][2], /* Pipes for filters */
675 pid, /* Process ID of filter */
676 status, /* Exit status */
677 retval; /* Return value */
678 cups_array_t *pids; /* Executed filters array */
679 filter_pid_t *pid_entry, /* Entry in executed filters array */
680 key; /* Search key for filters */
681 const char *cups_serverbin; /* CUPS_SERVERBIN environment variable */
682
683 /*
684 * Remove NULL ("-") filters...
685 */
686
687 for (filter = (char *)cupsArrayFirst(filters);
688 filter;
689 filter = (char *)cupsArrayNext(filters))
690 if (!strcmp(filter, "-"))
691 cupsArrayRemove(filters, filter);
692
693 for (i = 0; argv[i]; i ++)
694 fprintf(stderr, "DEBUG: argv[%d]=\"%s\"\n", i, argv[i]);
695
696 /*
697 * Execute all of the filters...
698 */
699
700 pids = cupsArrayNew((cups_array_func_t)compare_pids, NULL);
701 current = 0;
702 filterfds[0][0] = 0;
703 filterfds[0][1] = -1;
704 filterfds[1][0] = -1;
705 filterfds[1][1] = -1;
706
707 for (filter = (char *)cupsArrayFirst(filters);
708 filter;
709 filter = next, current = 1 - current) {
710 next = (char *)cupsArrayNext(filters);
711
712 if (filter[0] == '/') {
713 strncpy(program, filter, sizeof(program) - 1);
714 if (strlen(filter) > 1023)
715 program[1023] = '\0';
716 } else {
717 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
718 cups_serverbin = CUPS_SERVERBIN;
719 snprintf(program, sizeof(program), "%s/filter/%s", cups_serverbin,
720 filter);
721 }
722
723 if (filterfds[!current][1] > 1) {
724 close(filterfds[1 - current][0]);
725 close(filterfds[1 - current][1]);
726
727 filterfds[1 - current][0] = -1;
728 filterfds[1 - current][0] = -1;
729 }
730
731 if (next)
732 open_pipe(filterfds[1 - current]);
733 else
734 filterfds[1 - current][1] = 1;
735
736 pid = exec_filter(program, argv,
737 filterfds[current][0], filterfds[1 - current][1]);
738
739 if (pid > 0) {
740 fprintf(stderr, "INFO: %s (PID %d) started.\n", filter, pid);
741
742 pid_entry = malloc(sizeof(filter_pid_t));
743 pid_entry->pid = pid;
744 pid_entry->name = filter;
745 cupsArrayAdd(pids, pid_entry);
746 } else
747 break;
748
749 argv[6] = NULL;
750 }
751
752 /*
753 * Close remaining pipes...
754 */
755
756 if (filterfds[0][1] > 1) {
757 close(filterfds[0][0]);
758 close(filterfds[0][1]);
759 }
760
761 if (filterfds[1][1] > 1) {
762 close(filterfds[1][0]);
763 close(filterfds[1][1]);
764 }
765
766 /*
767 * Wait for the children to exit...
768 */
769
770 retval = 0;
771
772 while (cupsArrayCount(pids) > 0) {
773 if ((pid = wait(&status)) < 0) {
774 if (errno == EINTR && job_canceled) {
775 fprintf(stderr, "DEBUG: Job canceled, killing filters ...\n");
776 for (pid_entry = (filter_pid_t *)cupsArrayFirst(pids);
777 pid_entry;
778 pid_entry = (filter_pid_t *)cupsArrayNext(pids))
779 kill(pid_entry->pid, SIGTERM);
780 job_canceled = 0;
781 } else
782 continue;
783 }
784
785 key.pid = pid;
786 if ((pid_entry = (filter_pid_t *)cupsArrayFind(pids, &key)) != NULL) {
787 cupsArrayRemove(pids, pid_entry);
788
789 if (status) {
790 if (WIFEXITED(status))
791 fprintf(stderr, "ERROR: %s (PID %d) stopped with status %d\n",
792 pid_entry->name, pid, WEXITSTATUS(status));
793 else
794 fprintf(stderr, "ERROR: %s (PID %d) crashed on signal %d\n",
795 pid_entry->name, pid, WTERMSIG(status));
796
797 retval = 1;
798 } else
799 fprintf(stderr, "INFO: %s (PID %d) exited with no errors.\n",
800 pid_entry->name, pid);
801
802 free(pid_entry);
803 }
804 }
805
806 cupsArrayDelete(pids);
807
808 return (retval);
809 }
810
811
812 /*
813 * 'open_pipe()' - Create a pipe which is closed on exec.
814 */
815
816 static int /* O - 0 on success, -1 on error */
open_pipe(int * fds)817 open_pipe(int *fds) /* O - Pipe file descriptors (2) */
818 {
819 /*
820 * Create the pipe...
821 */
822
823 if (pipe(fds)) {
824 fds[0] = -1;
825 fds[1] = -1;
826
827 return (-1);
828 }
829
830 /*
831 * Set the "close on exec" flag on each end of the pipe...
832 */
833
834 if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC)) {
835 close(fds[0]);
836 close(fds[1]);
837
838 fds[0] = -1;
839 fds[1] = -1;
840
841 return (-1);
842 }
843
844 if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC)) {
845 close(fds[0]);
846 close(fds[1]);
847
848 fds[0] = -1;
849 fds[1] = -1;
850
851 return (-1);
852 }
853
854 /*
855 * Return 0 indicating success...
856 */
857
858 return (0);
859 }
860
861
862 /*
863 * Get option value in a string of options
864 */
865
866 static char* /* O - Value, NULL if option not set */
get_option_in_str(char * buf,const char * option,int return_value)867 get_option_in_str(char *buf, /* I - Buffer with option list string */
868 const char *option, /* I - Option of which to get value */
869 int return_value) /* I - Return value or only check
870 presence of option? */
871 {
872 char *p1, *p2;
873 char *result;
874
875 if (!buf || !option)
876 return NULL;
877 if ((p1 = strcasestr(buf, option)) == NULL)
878 return NULL;
879 if (p1 > buf && *(p1 - 1) != ' ' && *(p1 - 1) != '\t')
880 return NULL;
881 p2 = p1 + strlen(option);
882 if (*p2 == ' ' || *p2 == '\t' || *p2 == '\0')
883 return "";
884 if (*p2 != '=')
885 return NULL;
886 if (!return_value)
887 return "";
888 p1 = p2 + 1;
889 for (p2 = p1; *p2 != ' ' && *p2 != '\t' && *p2 != '\0'; p2 ++);
890 if (p2 == p1)
891 return "";
892 result = calloc(p2 - p1 + 1, sizeof(char));
893 memcpy(result, p1, p2 - p1);
894 result[p2 - p1] = '\0';
895 return result;
896 }
897
898
899