]>
git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/cupsfilter.c
713a0f1cd36c31e520da0b7a6dd6a5405cec3cfb
2 * "$Id: cupsfilter.c 6816 2007-08-20 20:16:00Z 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 *infile
,
78 const char *outfile
, const char *ppdfile
,
79 const char *printer
, const char *user
,
80 const char *title
, int num_options
,
81 cups_option_t
*options
);
82 static int open_pipe(int *fds
);
83 static int read_cupsd_conf(const char *filename
);
84 static void set_string(char **s
, const char *val
);
85 static void usage(const char *command
, const char *opt
);
89 * 'main()' - Main entry for the test program.
92 int /* O - Exit status */
93 main(int argc
, /* I - Number of command-line args */
94 char *argv
[]) /* I - Command-line arguments */
96 int i
; /* Looping vars */
97 const char *command
, /* Command name */
98 *opt
; /* Current option */
99 char *srctype
, /* Source type */
100 *dsttype
, /* Destination type */
101 super
[MIME_MAX_SUPER
], /* Super-type name */
102 type
[MIME_MAX_TYPE
]; /* Type name */
103 int compression
; /* Compression of file */
104 int cost
; /* Cost of filters */
105 mime_t
*mime
; /* MIME database */
106 char *infile
, /* File to filter */
107 *outfile
; /* File to create */
108 char cupsdconf
[1024]; /* cupsd.conf file */
109 const char *server_root
; /* CUPS_SERVERROOT environment variable */
110 mime_type_t
*src
, /* Source type */
111 *dst
; /* Destination type */
112 cups_array_t
*filters
; /* Filters for the file */
113 int num_options
; /* Number of options */
114 cups_option_t
*options
; /* Options */
115 const char *ppdfile
; /* PPD file */
116 const char *title
, /* Title string */
117 *user
; /* Username */
118 int removeppd
, /* Remove PPD file */
119 removeinfile
; /* Remove input file */
120 int status
; /* Execution status */
127 if ((command
= strrchr(argv
[0], '/')) != NULL
)
134 dsttype
= "application/pdf";
145 if ((server_root
= getenv("CUPS_SERVERROOT")) == NULL
)
146 server_root
= CUPS_SERVERROOT
;
148 snprintf(cupsdconf
, sizeof(cupsdconf
), "%s/cupsd.conf", server_root
);
151 * Process command-line arguments...
154 _cupsSetLocale(argv
);
156 for (i
= 1; i
< argc
; i
++)
157 if (argv
[i
][0] == '-')
159 for (opt
= argv
[i
] + 1; *opt
; opt
++)
162 case '-' : /* Next argument is a filename... */
164 if (i
< argc
&& !infile
)
170 case 'a' : /* Specify option... */
173 num_options
= cupsParseOptions(argv
[i
], num_options
, &options
);
178 case 'c' : /* Specify cupsd.conf file location... */
182 if (!strcmp(command
, "convert"))
183 num_options
= cupsAddOption("copies", argv
[i
], num_options
,
186 strlcpy(cupsdconf
, argv
[i
], sizeof(cupsdconf
));
192 case 'D' : /* Delete input file after conversion */
196 case 'f' : /* Specify input file... */
198 if (i
< argc
&& !infile
)
204 case 'i' : /* Specify source MIME type... */
208 if (sscanf(argv
[i
], "%15[^/]/%255s", super
, type
) != 2)
217 case 'j' : /* Specify destination MIME type... */
218 case 'm' : /* Specify destination MIME type... */
222 if (sscanf(argv
[i
], "%15[^/]/%255s", super
, type
) != 2)
231 case 'n' : /* Specify number of copies... */
234 num_options
= cupsAddOption("copies", argv
[i
], num_options
,
240 case 'o' : /* Specify option(s) or output filename */
244 if (!strcmp(command
, "convert"))
247 usage(command
, NULL
);
252 num_options
= cupsParseOptions(argv
[i
], num_options
,
259 case 'p' : /* Specify PPD file... */
260 case 'P' : /* Specify PPD file... */
268 case 't' : /* Specify title... */
269 case 'J' : /* Specify title... */
277 case 'u' : /* Delete PPD file after conversion */
281 case 'U' : /* Specify username... */
289 default : /* Something we don't understand... */
296 if (strcmp(command
, "convert"))
300 _cupsLangPuts(stderr
,
301 _("convert: Use the -f option to specify a file to "
303 usage(command
, NULL
);
308 _cupsLangPuts(stderr
,
309 _("cupsfilter: Only one filename can be specified!\n"));
310 usage(command
, NULL
);
313 if (!infile
&& !srctype
)
314 usage(command
, NULL
);
320 else if ((title
= strrchr(infile
, '/')) != NULL
)
327 * Load the cupsd.conf file and create the MIME database...
330 if (read_cupsd_conf(cupsdconf
))
333 if ((mime
= mimeLoad(ServerRoot
, Path
)) == NULL
)
335 _cupsLangPrintf(stderr
,
336 _("%s: Unable to read MIME database from \"%s\"!\n"),
337 command
, ServerRoot
);
342 * Get the source and destination types...
347 sscanf(srctype
, "%15[^/]/%255s", super
, type
);
348 if ((src
= mimeType(mime
, super
, type
)) == NULL
)
350 _cupsLangPrintf(stderr
,
351 _("%s: Unknown source MIME type %s/%s!\n"),
352 command
, super
, type
);
356 else if ((src
= mimeFileType(mime
, infile
, infile
, &compression
)) == NULL
)
358 _cupsLangPrintf(stderr
,
359 _("%s: Unable to determine MIME type of \"%s\"!\n"),
364 sscanf(dsttype
, "%15[^/]/%255s", super
, type
);
365 if ((dst
= mimeType(mime
, super
, type
)) == NULL
)
367 _cupsLangPrintf(stderr
,
368 _("%s: Unknown destination MIME type %s/%s!\n"),
369 command
, super
, type
);
374 * Figure out how to filter the file...
380 * Special case - no filtering needed...
383 filters
= cupsArrayNew(NULL
, NULL
);
384 cupsArrayAdd(filters
, &GZIPFilter
);
386 else if ((filters
= mimeFilter(mime
, src
, dst
, &cost
)) == NULL
)
388 _cupsLangPrintf(stderr
,
389 _("%s: No filter to convert from %s/%s to %s/%s!\n"),
390 command
, src
->super
, src
->type
, dst
->super
, dst
->type
);
393 else if (compression
)
394 cupsArrayInsert(filters
, &GZIPFilter
);
400 status
= exec_filters(filters
, infile
, outfile
, ppdfile
,
401 !strcmp(command
, "convert") ? "tofile" : "cupsfilter",
402 user
, title
, num_options
, options
);
405 * Remove files as needed, then exit...
408 if (removeppd
&& ppdfile
)
411 if (removeinfile
&& infile
)
419 * 'compare_pids()' - Compare two filter PIDs...
422 static int /* O - Result of comparison */
423 compare_pids(mime_filter_t
*a
, /* I - First filter */
424 mime_filter_t
*b
) /* I - Second filter */
427 * Because we're particularly lazy, we store the process ID in the "cost"
431 return (a
->cost
- b
->cost
);
436 * 'escape_options()' - Convert an options array to a string.
439 static char * /* O - Option string */
441 int num_options
, /* I - Number of options */
442 cups_option_t
*options
) /* I - Options */
444 int i
; /* Looping var */
445 cups_option_t
*option
; /* Current option */
446 int bytes
; /* Number of bytes needed */
447 char *s
, /* Option string */
448 *sptr
, /* Pointer into string */
449 *vptr
; /* Pointer into value */
453 * Figure out the worst-case number of bytes we need for the option string.
456 for (i
= num_options
, option
= options
, bytes
= 1; i
> 0; i
--, option
++)
457 bytes
+= 2 * (strlen(option
->name
) + strlen(option
->value
)) + 2;
462 * Copy the options to the string...
465 for (i
= num_options
, option
= options
, sptr
= s
; i
> 0; i
--, option
++)
467 if (!strcmp(option
->name
, "copies"))
473 strcpy(sptr
, option
->name
);
474 sptr
+= strlen(sptr
);
477 for (vptr
= option
->value
; *vptr
;)
479 if (strchr("\\ \t\n", *vptr
))
493 * 'exec_filter()' - Execute a single filter.
496 static int /* O - Process ID or -1 on error */
497 exec_filter(const char *filter
, /* I - Filter to execute */
498 char **argv
, /* I - Argument list */
499 char **envp
, /* I - Environment list */
500 int infd
, /* I - Stdin file descriptor */
501 int outfd
) /* I - Stdout file descriptor */
503 int pid
; /* Process ID */
504 #if defined(__APPLE__)
505 char processPath
[1024], /* CFProcessPath environment variable */
506 linkpath
[1024]; /* Link path for symlinks... */
507 int linkbytes
; /* Bytes for link path */
511 * Add special voodoo magic for MacOS X - this allows MacOS X
512 * programs to access their bundle resources properly...
515 if ((linkbytes
= readlink(filter
, linkpath
, sizeof(linkpath
) - 1)) > 0)
518 * Yes, this is a symlink to the actual program, nul-terminate and
522 linkpath
[linkbytes
] = '\0';
524 if (linkpath
[0] == '/')
525 snprintf(processPath
, sizeof(processPath
), "CFProcessPath=%s",
528 snprintf(processPath
, sizeof(processPath
), "CFProcessPath=%s/%s",
529 dirname((char *)filter
), linkpath
);
532 snprintf(processPath
, sizeof(processPath
), "CFProcessPath=%s", filter
);
534 envp
[0] = processPath
; /* Replace <CFProcessPath> string */
535 #endif /* __APPLE__ */
537 if ((pid
= fork()) == 0)
540 * Child process goes here...
542 * Update stdin/stdout/stderr as needed...
551 open("/dev/null", O_RDONLY
);
560 open("/dev/null", O_WRONLY
);
564 open("/dev/null", O_RDWR
);
565 fcntl(3, F_SETFL
, O_NDELAY
);
568 open("/dev/null", O_RDWR
);
569 fcntl(4, F_SETFL
, O_NDELAY
);
575 execve(filter
, argv
, envp
);
587 * 'exec_filters()' - Execute filters for the given file and options.
590 static int /* O - 0 on success, 1 on error */
591 exec_filters(cups_array_t
*filters
, /* I - Array of filters to run */
592 const char *infile
, /* I - File to filter */
593 const char *outfile
, /* I - File to create */
594 const char *ppdfile
, /* I - PPD file, if any */
595 const char *printer
, /* I - Printer name */
596 const char *user
, /* I - Username */
597 const char *title
, /* I - Job title */
598 int num_options
, /* I - Number of filter options */
599 cups_option_t
*options
) /* I - Filter options */
601 int i
; /* Looping var */
602 const char *argv
[8], /* Command-line arguments */
603 *envp
[11], /* Environment variables */
604 *temp
; /* Temporary string */
605 char *optstr
, /* Filter options */
606 cups_datadir
[1024], /* CUPS_DATADIR */
607 cups_fontpath
[1024], /* CUPS_FONTPATH */
608 cups_serverbin
[1024], /* CUPS_SERVERBIN */
609 cups_serverroot
[1024], /* CUPS_SERVERROOT */
610 lang
[1024], /* LANG */
611 path
[1024], /* PATH */
613 rip_cache
[1024], /* RIP_CACHE */
614 userenv
[1024], /* USER */
615 program
[1024]; /* Program to run */
616 mime_filter_t
*filter
, /* Current filter */
617 *next
; /* Next filter */
618 int current
, /* Current filter */
619 filterfds
[2][2], /* Pipes for filters */
620 pid
, /* Process ID of filter */
621 status
, /* Exit status */
622 retval
; /* Return value */
623 cups_array_t
*pids
; /* Executed filters array */
624 mime_filter_t key
; /* Search key for filters */
625 cups_lang_t
*language
; /* Current language */
629 * Setup the filter environment and command-line...
632 optstr
= escape_options(num_options
, options
);
634 snprintf(cups_datadir
, sizeof(cups_datadir
), "CUPS_DATADIR=%s", DataDir
);
635 snprintf(cups_fontpath
, sizeof(cups_fontpath
), "CUPS_FONTPATH=%s", FontPath
);
636 snprintf(cups_serverbin
, sizeof(cups_serverbin
), "CUPS_SERVERBIN=%s",
638 snprintf(cups_serverroot
, sizeof(cups_serverroot
), "CUPS_SERVERROOT=%s",
640 language
= cupsLangDefault();
641 snprintf(lang
, sizeof(lang
), "LANG=%s.UTF8", language
->language
);
642 snprintf(path
, sizeof(path
), "PATH=%s", Path
);
644 snprintf(ppd
, sizeof(ppd
), "PPD=%s", ppdfile
);
645 else if ((temp
= getenv("PPD")) != NULL
)
646 snprintf(ppd
, sizeof(ppd
), "PPD=%s", temp
);
649 if (!access("/System/Library/Frameworks/ApplicationServices.framework/"
650 "Versions/A/Frameworks/PrintCore.framework/Versions/A/"
651 "Resources/English.lproj/Generic.ppd", 0))
652 strlcpy(ppd
, "PPD=/System/Library/Frameworks/ApplicationServices.framework/"
653 "Versions/A/Frameworks/PrintCore.framework/Versions/A/"
654 "Resources/English.lproj/Generic.ppd", sizeof(ppd
));
656 strlcpy(ppd
, "PPD=/System/Library/Frameworks/ApplicationServices.framework/"
657 "Versions/A/Frameworks/PrintCore.framework/Versions/A/"
658 "Resources/Generic.ppd", sizeof(ppd
));
660 snprintf(ppd
, sizeof(ppd
), "PPD=%s/model/laserjet.ppd", DataDir
);
661 #endif /* __APPLE__ */
662 snprintf(rip_cache
, sizeof(rip_cache
), "RIP_CACHE=%s", RIPCache
);
663 snprintf(userenv
, sizeof(userenv
), "USER=%s", user
);
665 argv
[0] = (char *)printer
;
669 argv
[4] = cupsGetOption("copies", num_options
, options
);
677 envp
[0] = "<CFProcessPath>";
678 envp
[1] = cups_datadir
;
679 envp
[2] = cups_fontpath
;
680 envp
[3] = cups_serverbin
;
681 envp
[4] = cups_serverroot
;
689 for (i
= 0; argv
[i
]; i
++)
690 fprintf(stderr
, "DEBUG: argv[%d]=\"%s\"\n", i
, argv
[i
]);
692 for (i
= 0; envp
[i
]; i
++)
693 fprintf(stderr
, "DEBUG: envp[%d]=\"%s\"\n", i
, envp
[i
]);
696 * Execute all of the filters...
699 pids
= cupsArrayNew((cups_array_func_t
)compare_pids
, NULL
);
701 filterfds
[0][0] = -1;
702 filterfds
[0][1] = -1;
703 filterfds
[1][0] = -1;
704 filterfds
[1][1] = -1;
709 for (filter
= (mime_filter_t
*)cupsArrayFirst(filters
);
711 filter
= next
, current
= 1 - current
)
713 next
= (mime_filter_t
*)cupsArrayNext(filters
);
715 if (filter
->filter
[0] == '/')
716 strlcpy(program
, filter
->filter
, sizeof(program
));
718 snprintf(program
, sizeof(program
), "%s/filter/%s", ServerBin
,
721 if (filterfds
[!current
][1] > 1)
723 close(filterfds
[1 - current
][0]);
724 close(filterfds
[1 - current
][1]);
726 filterfds
[1 - current
][0] = -1;
727 filterfds
[1 - current
][0] = -1;
731 open_pipe(filterfds
[1 - current
]);
734 filterfds
[1 - current
][1] = open(outfile
, O_CREAT
| O_TRUNC
| O_WRONLY
,
737 if (filterfds
[1 - current
][1] < 0)
738 fprintf(stderr
, "ERROR: Unable to create \"%s\" - %s\n", outfile
,
742 filterfds
[1 - current
][1] = 1;
744 pid
= exec_filter(program
, (char **)argv
, (char **)envp
,
745 filterfds
[current
][0], filterfds
[1 - current
][1]);
749 fprintf(stderr
, "INFO: %s (PID %d) started.\n", filter
->filter
, pid
);
752 cupsArrayAdd(pids
, filter
);
761 * Close remaining pipes...
764 if (filterfds
[0][1] > 1)
766 close(filterfds
[0][0]);
767 close(filterfds
[0][1]);
770 if (filterfds
[1][1] > 1)
772 close(filterfds
[1][0]);
773 close(filterfds
[1][1]);
777 * Wait for the children to exit...
782 while (cupsArrayCount(pids
) > 0)
784 if ((pid
= wait(&status
)) < 0)
788 if ((filter
= (mime_filter_t
*)cupsArrayFind(pids
, &key
)) != NULL
)
790 cupsArrayRemove(pids
, filter
);
794 if (WIFEXITED(status
))
795 fprintf(stderr
, "ERROR: %s (PID %d) stopped with status %d!\n",
796 filter
->filter
, pid
, WEXITSTATUS(status
));
798 fprintf(stderr
, "ERROR: %s (PID %d) crashed on signal %d!\n",
799 filter
->filter
, pid
, WTERMSIG(status
));
804 fprintf(stderr
, "INFO: %s (PID %d) exited with no errors.\n",
805 filter
->filter
, pid
);
814 * 'open_pipe()' - Create a pipe which is closed on exec.
817 int /* O - 0 on success, -1 on error */
818 open_pipe(int *fds
) /* O - Pipe file descriptors (2) */
833 * Set the "close on exec" flag on each end of the pipe...
836 if (fcntl(fds
[0], F_SETFD
, fcntl(fds
[0], F_GETFD
) | FD_CLOEXEC
))
847 if (fcntl(fds
[1], F_SETFD
, fcntl(fds
[1], F_GETFD
) | FD_CLOEXEC
))
859 * Return 0 indicating success...
867 * 'read_cupsd_conf()' - Read the cupsd.conf file to get the filter settings.
870 static int /* O - 0 on success, 1 on error */
871 read_cupsd_conf(const char *filename
) /* I - File to read */
873 cups_file_t
*fp
; /* cupsd.conf file */
874 const char *temp
; /* Temporary string */
875 char line
[1024], /* Line from file */
876 *ptr
; /* Pointer into line */
877 int linenum
; /* Current line number */
880 if ((temp
= getenv("CUPS_DATADIR")) != NULL
)
881 set_string(&DataDir
, temp
);
883 set_string(&DataDir
, CUPS_DATADIR
);
885 if ((temp
= getenv("CUPS_FONTPATH")) != NULL
)
886 set_string(&FontPath
, temp
);
888 set_string(&FontPath
, CUPS_FONTPATH
);
890 set_string(&RIPCache
, "8m");
892 if ((temp
= getenv("CUPS_SERVERBIN")) != NULL
)
893 set_string(&ServerBin
, temp
);
895 set_string(&ServerBin
, CUPS_SERVERBIN
);
897 strlcpy(line
, filename
, sizeof(line
));
898 if ((ptr
= strrchr(line
, '/')) != NULL
)
901 getcwd(line
, sizeof(line
));
903 set_string(&ServerRoot
, line
);
905 if ((fp
= cupsFileOpen(filename
, "r")) != NULL
)
909 while (cupsFileGetConf(fp
, line
, sizeof(line
), &ptr
, &linenum
))
911 if (!strcasecmp(line
, "DataDir"))
912 set_string(&DataDir
, ptr
);
913 else if (!strcasecmp(line
, "FontPath"))
914 set_string(&FontPath
, ptr
);
915 else if (!strcasecmp(line
, "RIPCache"))
916 set_string(&RIPCache
, ptr
);
917 else if (!strcasecmp(line
, "ServerBin"))
918 set_string(&ServerBin
, ptr
);
919 else if (!strcasecmp(line
, "ServerRoot"))
920 set_string(&ServerRoot
, ptr
);
926 snprintf(line
, sizeof(line
),
927 "%s/filter:" CUPS_BINDIR
":" CUPS_SBINDIR
":/bin/usr/bin",
929 set_string(&Path
, line
);
936 * 'set_string()' - Copy and set a string.
940 set_string(char **s
, /* O - Copy of string */
941 const char *val
) /* I - String to copy */
951 * 'usage()' - Show program usage...
955 usage(const char *command
, /* I - Command name */
956 const char *opt
) /* I - Incorrect option, if any */
959 _cupsLangPrintf(stderr
, _("%s: Unknown option '%c'!\n"), command
, *opt
);
961 if (!strcmp(command
, "cupsfilter"))
962 _cupsLangPuts(stdout
,
963 _("Usage: cupsfilter -m mime/type [ options ] filename\n"
967 " -c cupsd.conf Set cupsd.conf file to use\n"
968 " -n copies Set number of copies\n"
969 " -o name=value Set option(s)\n"
970 " -p filename.ppd Set PPD file\n"
971 " -t title Set title\n"));
973 _cupsLangPuts(stdout
,
974 _("Usage: convert [ options ]\n"
978 " -f filename Set file to be converted (otherwise stdin)\n"
979 " -o filename Set file to be generated (otherwise stdout)\n"
980 " -i mime/type Set input MIME type (otherwise auto-typed)\n"
981 " -j mime/type Set output MIME type (otherwise application/pdf)\n"
982 " -P filename.ppd Set PPD file\n"
983 " -a 'name=value ...' Set option(s)\n"
984 " -U username Set username for job\n"
985 " -J title Set title\n"
986 " -c copies Set number of copies\n"
987 " -u Remove the PPD file when finished\n"
988 " -D Remove the input file when finished\n"));
995 * End of "$Id: cupsfilter.c 6816 2007-08-20 20:16:00Z mike $".