]>
git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/cupsfilter.c
2 * "$Id: cupsfilter.c 6668 2007-07-13 23:09:49Z mike $"
4 * CUPS filtering program for the Common UNIX Printing System (CUPS).
6 * Copyright 2007 by Apple Inc.
7 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
17 * main() - Main entry for the test program.
18 * compare_pids() - Compare two filter PIDs...
19 * escape_options() - Convert an options array to a string.
20 * exec_filter() - Execute a single filter.
21 * exec_filters() - Execute filters for the given file and options.
22 * open_pipe() - Create a pipe which is closed on exec.
23 * read_cupsd_conf() - Read the cupsd.conf file to get the filter settings.
24 * set_string() - Copy and set a string.
25 * usage() - Show program usage...
29 * Include necessary headers...
32 #include <cups/cups.h>
33 #include <cups/i18n.h>
34 #include <cups/string.h>
41 #if defined(__APPLE__)
43 #endif /* __APPLE__ */
50 static char *DataDir
= NULL
;/* CUPS_DATADIR environment variable */
51 static char *FontPath
= NULL
;
52 /* CUPS_FONTPATH environment variable */
53 static mime_filter_t GZIPFilter
= /* gziptoany filter */
55 NULL
, /* Source type */
56 NULL
, /* Destination type */
58 "gziptoany" /* Filter program to run */
60 static char *Path
= NULL
; /* PATH environment variable */
61 static char *ServerBin
= NULL
;
62 /* CUPS_SERVERBIN environment variable */
63 static char *ServerRoot
= NULL
;
64 /* CUPS_SERVERROOT environment variable */
65 static char *RIPCache
= NULL
;
66 /* RIP_CACHE environment variable */
73 static int compare_pids(mime_filter_t
*a
, mime_filter_t
*b
);
74 static char *escape_options(int num_options
, cups_option_t
*options
);
75 static int exec_filter(const char *filter
, char **argv
, char **envp
,
77 static int exec_filters(cups_array_t
*filters
, const char *filename
,
78 const char *ppdfile
, const char *title
,
79 int num_options
, cups_option_t
*options
);
80 static int open_pipe(int *fds
);
81 static int read_cupsd_conf(const char *filename
);
82 static void set_string(char **s
, const char *val
);
83 static void usage(const char *opt
);
87 * 'main()' - Main entry for the test program.
90 int /* O - Exit status */
91 main(int argc
, /* I - Number of command-line args */
92 char *argv
[]) /* I - Command-line arguments */
94 int i
; /* Looping vars */
95 const char *opt
; /* Current option */
96 char super
[MIME_MAX_SUPER
], /* Super-type name */
97 type
[MIME_MAX_TYPE
]; /* Type name */
98 int compression
; /* Compression of file */
99 int cost
; /* Cost of filters */
100 mime_t
*mime
; /* MIME database */
101 char *filename
; /* File to filter */
102 char cupsdconf
[1024]; /* cupsd.conf file */
103 const char *server_root
; /* CUPS_SERVERROOT environment variable */
104 mime_type_t
*src
, /* Source type */
105 *dst
; /* Destination type */
106 cups_array_t
*filters
; /* Filters for the file */
107 int num_options
; /* Number of options */
108 cups_option_t
*options
; /* Options */
109 const char *ppdfile
; /* PPD file */
110 const char *title
; /* Title string */
128 if ((server_root
= getenv("CUPS_SERVERROOT")) == NULL
)
129 server_root
= CUPS_SERVERROOT
;
131 snprintf(cupsdconf
, sizeof(cupsdconf
), "%s/cupsd.conf", server_root
);
134 * Process command-line arguments...
137 _cupsSetLocale(argv
);
139 for (i
= 1; i
< argc
; i
++)
140 if (argv
[i
][0] == '-')
142 for (opt
= argv
[i
] + 1; *opt
; opt
++)
145 case '-' : /* Next argument is a filename... */
147 if (i
< argc
&& !filename
)
153 case 'c' : /* Specify cupsd.conf file location... */
156 strlcpy(cupsdconf
, argv
[i
], sizeof(cupsdconf
));
161 case 'm' : /* Specify destination MIME type... */
165 if (sscanf(argv
[i
], "%15[^/]/%255s", super
, type
) != 2)
172 case 'n' : /* Specify number of copies... */
175 num_options
= cupsAddOption("copies", argv
[i
], num_options
,
181 case 'o' : /* Specify option... */
184 num_options
= cupsParseOptions(argv
[i
], num_options
, &options
);
189 case 'p' : /* Specify PPD file... */
197 case 't' : /* Specify number of copies... */
205 default : /* Something we don't understand... */
214 _cupsLangPuts(stderr
,
215 _("cupsfilter: Only one filename can be specified!\n"));
219 if (!filename
|| !super
[0] || !type
[0])
224 if ((title
= strrchr(filename
, '/')) != NULL
)
231 * Load the cupsd.conf file and create the MIME database...
234 if (read_cupsd_conf(cupsdconf
))
237 if ((mime
= mimeLoad(ServerRoot
, Path
)) == NULL
)
239 _cupsLangPrintf(stderr
,
240 _("cupsfilter: Unable to read MIME database from \"%s\"!\n"),
246 * Get the source and destination types...
249 if ((src
= mimeFileType(mime
, filename
, filename
, &compression
)) == NULL
)
251 _cupsLangPrintf(stderr
,
252 _("cupsfilter: Unable to determine MIME type of \"%s\"!\n"),
257 if ((dst
= mimeType(mime
, super
, type
)) == NULL
)
259 _cupsLangPrintf(stderr
,
260 _("cupsfilter: Unknown destination MIME type %s/%s!\n"),
266 * Figure out how to filter the file...
272 * Special case - no filtering needed...
275 filters
= cupsArrayNew(NULL
, NULL
);
276 cupsArrayAdd(filters
, &GZIPFilter
);
278 else if ((filters
= mimeFilter(mime
, src
, dst
, &cost
)) == NULL
)
280 _cupsLangPrintf(stderr
,
281 _("cupsfilter: No filter to convert from %s/%s to %s/%s!\n"),
282 src
->super
, src
->type
, dst
->super
, dst
->type
);
285 else if (compression
)
286 cupsArrayInsert(filters
, &GZIPFilter
);
292 return (exec_filters(filters
, filename
, ppdfile
, title
, num_options
, options
));
297 * 'compare_pids()' - Compare two filter PIDs...
300 static int /* O - Result of comparison */
301 compare_pids(mime_filter_t
*a
, /* I - First filter */
302 mime_filter_t
*b
) /* I - Second filter */
305 * Because we're particularly lazy, we store the process ID in the "cost"
309 return (a
->cost
- b
->cost
);
314 * 'escape_options()' - Convert an options array to a string.
317 static char * /* O - Option string */
319 int num_options
, /* I - Number of options */
320 cups_option_t
*options
) /* I - Options */
322 int i
; /* Looping var */
323 cups_option_t
*option
; /* Current option */
324 int bytes
; /* Number of bytes needed */
325 char *s
, /* Option string */
326 *sptr
, /* Pointer into string */
327 *vptr
; /* Pointer into value */
331 * Figure out the worst-case number of bytes we need for the option string.
334 for (i
= num_options
, option
= options
, bytes
= 1; i
> 0; i
--, option
++)
335 bytes
+= 2 * (strlen(option
->name
) + strlen(option
->value
)) + 2;
340 * Copy the options to the string...
343 for (i
= num_options
, option
= options
, sptr
= s
; i
> 0; i
--, option
++)
345 if (!strcmp(option
->name
, "copies"))
351 strcpy(sptr
, option
->name
);
352 sptr
+= strlen(sptr
);
355 for (vptr
= option
->value
; *vptr
;)
357 if (strchr("\\ \t\n", *vptr
))
366 fprintf(stderr
, "DEBUG: options=\"%s\"\n", s
);
373 * 'exec_filter()' - Execute a single filter.
376 static int /* O - Process ID or -1 on error */
377 exec_filter(const char *filter
, /* I - Filter to execute */
378 char **argv
, /* I - Argument list */
379 char **envp
, /* I - Environment list */
380 int infd
, /* I - Stdin file descriptor */
381 int outfd
) /* I - Stdout file descriptor */
383 int pid
; /* Process ID */
384 #if defined(__APPLE__)
385 char processPath
[1024], /* CFProcessPath environment variable */
386 linkpath
[1024]; /* Link path for symlinks... */
387 int linkbytes
; /* Bytes for link path */
391 * Add special voodoo magic for MacOS X - this allows MacOS X
392 * programs to access their bundle resources properly...
395 if ((linkbytes
= readlink(filter
, linkpath
, sizeof(linkpath
) - 1)) > 0)
398 * Yes, this is a symlink to the actual program, nul-terminate and
402 linkpath
[linkbytes
] = '\0';
404 if (linkpath
[0] == '/')
405 snprintf(processPath
, sizeof(processPath
), "CFProcessPath=%s",
408 snprintf(processPath
, sizeof(processPath
), "CFProcessPath=%s/%s",
409 dirname((char *)filter
), linkpath
);
412 snprintf(processPath
, sizeof(processPath
), "CFProcessPath=%s", filter
);
414 envp
[0] = processPath
; /* Replace <CFProcessPath> string */
415 #endif /* __APPLE__ */
417 if ((pid
= fork()) == 0)
420 * Child process goes here...
422 * Update stdin/stdout/stderr as needed...
431 open("/dev/null", O_RDONLY
);
440 open("/dev/null", O_WRONLY
);
444 open("/dev/null", O_RDWR
);
445 fcntl(3, F_SETFL
, O_NDELAY
);
448 open("/dev/null", O_RDWR
);
449 fcntl(4, F_SETFL
, O_NDELAY
);
455 execve(filter
, argv
, envp
);
467 * 'exec_filters()' - Execute filters for the given file and options.
470 static int /* O - 0 on success, 1 on error */
471 exec_filters(cups_array_t
*filters
, /* I - Array of filters to run */
472 const char *filename
, /* I - File to filter */
473 const char *ppdfile
, /* I - PPD file, if any */
474 const char *title
, /* I - Job title */
475 int num_options
, /* I - Number of filter options */
476 cups_option_t
*options
) /* I - Filter options */
478 const char *argv
[8], /* Command-line arguments */
479 *envp
[11], /* Environment variables */
480 *temp
; /* Temporary string */
481 char *optstr
, /* Filter options */
482 cups_datadir
[1024], /* CUPS_DATADIR */
483 cups_fontpath
[1024], /* CUPS_FONTPATH */
484 cups_serverbin
[1024], /* CUPS_SERVERBIN */
485 cups_serverroot
[1024], /* CUPS_SERVERROOT */
486 lang
[1024], /* LANG */
487 path
[1024], /* PATH */
489 rip_cache
[1024], /* RIP_CACHE */
490 user
[1024], /* USER */
491 program
[1024]; /* Program to run */
492 mime_filter_t
*filter
, /* Current filter */
493 *next
; /* Next filter */
494 int current
, /* Current filter */
495 filterfds
[2][2], /* Pipes for filters */
496 pid
, /* Process ID of filter */
497 status
, /* Exit status */
498 retval
; /* Return value */
499 cups_array_t
*pids
; /* Executed filters array */
500 mime_filter_t key
; /* Search key for filters */
504 * Setup the filter environment and command-line...
507 optstr
= escape_options(num_options
, options
);
509 snprintf(cups_datadir
, sizeof(cups_datadir
), "CUPS_DATADIR=%s", DataDir
);
510 snprintf(cups_fontpath
, sizeof(cups_fontpath
), "CUPS_FONTPATH=%s", FontPath
);
511 snprintf(cups_serverbin
, sizeof(cups_serverbin
), "CUPS_SERVERBIN=%s",
513 snprintf(cups_serverroot
, sizeof(cups_serverroot
), "CUPS_SERVERROOT=%s",
515 if ((temp
= getenv("LANG")) != NULL
)
516 snprintf(lang
, sizeof(lang
), "LANG=%s", temp
);
517 else if ((temp
= getenv("LC_ALL")) != NULL
)
518 snprintf(lang
, sizeof(lang
), "LC_ALL=%s", temp
);
520 strcpy(lang
, "LANG=C");
521 snprintf(path
, sizeof(path
), "PATH=%s", Path
);
523 snprintf(ppd
, sizeof(ppd
), "PPD=%s", ppdfile
);
524 else if ((temp
= getenv("PPD")) != NULL
)
525 snprintf(ppd
, sizeof(ppd
), "PPD=%s", temp
);
527 snprintf(ppd
, sizeof(ppd
), "PPD=%s/model/laserjet.ppd", DataDir
);
528 snprintf(rip_cache
, sizeof(rip_cache
), "RIP_CACHE=%s", RIPCache
);
529 snprintf(user
, sizeof(user
), "USER=%s", cupsUser());
531 argv
[0] = "cupsfilter";
533 argv
[2] = cupsUser();
535 argv
[4] = cupsGetOption("copies", num_options
, options
);
543 envp
[0] = "<CFProcessPath>";
544 envp
[1] = cups_datadir
;
545 envp
[2] = cups_fontpath
;
546 envp
[3] = cups_serverbin
;
547 envp
[4] = cups_serverroot
;
556 * Execute all of the filters...
559 pids
= cupsArrayNew((cups_array_func_t
)compare_pids
, NULL
);
561 filterfds
[0][0] = -1;
562 filterfds
[0][1] = -1;
563 filterfds
[1][0] = -1;
564 filterfds
[1][1] = -1;
566 for (filter
= (mime_filter_t
*)cupsArrayFirst(filters
);
568 filter
= next
, current
= 1 - current
)
570 next
= (mime_filter_t
*)cupsArrayNext(filters
);
572 if (filter
->filter
[0] == '/')
573 strlcpy(program
, filter
->filter
, sizeof(program
));
575 snprintf(program
, sizeof(program
), "%s/filter/%s", ServerBin
,
578 if (filterfds
[!current
][1] > 1)
580 close(filterfds
[1 - current
][0]);
581 close(filterfds
[1 - current
][1]);
583 filterfds
[1 - current
][0] = -1;
584 filterfds
[1 - current
][0] = -1;
588 open_pipe(filterfds
[1 - current
]);
590 filterfds
[1 - current
][1] = 1;
592 pid
= exec_filter(program
, (char **)argv
, (char **)envp
,
593 filterfds
[current
][0], filterfds
[1 - current
][1]);
597 fprintf(stderr
, "INFO: %s (PID %d) started.\n", filter
->filter
, pid
);
600 cupsArrayAdd(pids
, filter
);
609 * Close remaining pipes...
612 if (filterfds
[0][1] > 1)
614 close(filterfds
[0][0]);
615 close(filterfds
[0][1]);
618 if (filterfds
[1][1] > 1)
620 close(filterfds
[1][0]);
621 close(filterfds
[1][1]);
625 * Wait for the children to exit...
630 while (cupsArrayCount(pids
) > 0)
632 if ((pid
= wait(&status
)) < 0)
636 if ((filter
= (mime_filter_t
*)cupsArrayFind(pids
, &key
)) != NULL
)
638 cupsArrayRemove(pids
, filter
);
642 if (WIFEXITED(status
))
643 fprintf(stderr
, "ERROR: %s (PID %d) stopped with status %d!\n",
644 filter
->filter
, pid
, WEXITSTATUS(status
));
646 fprintf(stderr
, "ERROR: %s (PID %d) crashed on signal %d!\n",
647 filter
->filter
, pid
, WTERMSIG(status
));
652 fprintf(stderr
, "INFO: %s (PID %d) exited with no errors.\n",
653 filter
->filter
, pid
);
662 * 'open_pipe()' - Create a pipe which is closed on exec.
665 int /* O - 0 on success, -1 on error */
666 open_pipe(int *fds
) /* O - Pipe file descriptors (2) */
681 * Set the "close on exec" flag on each end of the pipe...
684 if (fcntl(fds
[0], F_SETFD
, fcntl(fds
[0], F_GETFD
) | FD_CLOEXEC
))
695 if (fcntl(fds
[1], F_SETFD
, fcntl(fds
[1], F_GETFD
) | FD_CLOEXEC
))
707 * Return 0 indicating success...
715 * 'read_cupsd_conf()' - Read the cupsd.conf file to get the filter settings.
718 static int /* O - 0 on success, 1 on error */
719 read_cupsd_conf(const char *filename
) /* I - File to read */
721 const char *temp
; /* Temporary string */
722 char line
[1024], /* Line from file */
723 *ptr
; /* Pointer into line */
726 if ((temp
= getenv("CUPS_DATADIR")) != NULL
)
727 set_string(&DataDir
, temp
);
729 set_string(&DataDir
, CUPS_DATADIR
);
731 if ((temp
= getenv("CUPS_FONTPATH")) != NULL
)
732 set_string(&FontPath
, temp
);
734 set_string(&FontPath
, CUPS_FONTPATH
);
736 if ((temp
= getenv("CUPS_SERVERBIN")) != NULL
)
737 set_string(&ServerBin
, temp
);
739 set_string(&ServerBin
, CUPS_SERVERBIN
);
741 strlcpy(line
, filename
, sizeof(line
));
742 if ((ptr
= strrchr(line
, '/')) != NULL
)
745 getcwd(line
, sizeof(line
));
747 set_string(&ServerRoot
, line
);
749 snprintf(line
, sizeof(line
),
750 "%s/filter:" CUPS_BINDIR
":" CUPS_SBINDIR
":/bin/usr/bin",
752 set_string(&Path
, line
);
759 * 'set_string()' - Copy and set a string.
763 set_string(char **s
, /* O - Copy of string */
764 const char *val
) /* I - String to copy */
774 * 'usage()' - Show program usage...
778 usage(const char *opt
) /* I - Incorrect option, if any */
781 _cupsLangPrintf(stderr
, _("%s: Unknown option '%c'!\n"), "cupsfilter",
784 _cupsLangPuts(stdout
,
785 _("Usage: cupsfilter -m mime/type [ options ] filename(s)\n"
789 " -c cupsd.conf Set cupsd.conf file to use\n"
790 " -n copies Set number of copies\n"
791 " -o name=value Set option(s)\n"
792 " -p filename.ppd Set PPD file\n"
793 " -t title Set title\n"));
800 * End of "$Id: cupsfilter.c 6668 2007-07-13 23:09:49Z mike $".