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