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