• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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