]>
git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/cups-driverd.c
9c5032246f75d49cdf0aed10f7b312f02b976d35
4 * PPD/driver support for the Common UNIX Printing System (CUPS).
6 * This program handles listing and installing both static PPD files
7 * in CUPS_DATADIR/model and dynamically generated PPD files using
8 * the driver helper programs in CUPS_SERVERBIN/driver.
10 * Copyright 1997-2005 by Easy Software Products.
12 * These coded instructions, statements, and computer programs are the
13 * property of Easy Software Products and are protected by Federal
14 * copyright law. Distribution and use rights are outlined in the file
15 * "LICENSE.txt" which should have been included with this file. If this
16 * file is missing or damaged please contact Easy Software Products
19 * Attn: CUPS Licensing Information
20 * Easy Software Products
21 * 44141 Airport View Drive, Suite 204
22 * Hollywood, Maryland 20636 USA
24 * Voice: (301) 373-9600
25 * EMail: cups-info@cups.org
26 * WWW: http://www.cups.org
33 * Include necessary headers...
41 * PPD information structures...
44 typedef struct /**** PPD record ****/
46 time_t mtime
; /* Modification time */
47 size_t size
; /* Size in bytes */
48 char name
[512 - sizeof(time_t) - sizeof(size_t)],
50 natural_language
[128], /* Natural language(s) */
51 make
[128], /* Manufacturer */
52 make_and_model
[256]; /* Make and model */
55 typedef struct /**** In-memory record ****/
57 int found
; /* 1 if PPD is found */
58 ppd_rec_t record
; /* PPDs.dat record */
66 int NumPPDs
, /* Number of PPD files */
67 SortedPPDs
, /* Number of sorted PPD files */
68 AllocPPDs
; /* Number of allocated entries */
69 ppd_info_t
*PPDs
; /* PPD file info */
70 int ChangedPPD
; /* Did we change the PPD database? */
77 ppd_info_t
*add_ppd(const char *name
, const char *natural_language
,
78 const char *make
, const char *make_and_model
,
79 time_t mtime
, size_t size
);
80 int cat_ppd(const char *name
);
81 int compare_names(const ppd_info_t
*p0
, const ppd_info_t
*p1
);
82 int compare_ppds(const ppd_info_t
*p0
, const ppd_info_t
*p1
);
83 int list_ppds(int request_id
, int limit
, const char *opt
);
84 int load_drivers(void);
85 int load_ppds(const char *d
, const char *p
);
89 * 'main()' - Scan for drivers and return an IPP response.
93 * cups-driverd request_id limit options
96 int /* O - Exit code */
97 main(int argc
, /* I - Number of command-line args */
98 char *argv
[]) /* I - Command-line arguments */
101 * Check the command-line...
105 (!strcmp(argv
[1], "cat") && argc
!= 3) ||
106 (!strcmp(argv
[1], "list") && argc
!= 5))
108 fputs("Usage: cups-driverd cat ppd-name\n", stderr
);
109 fputs("Usage: cups-driverd list request_id limit options\n", stderr
);
114 * Install or list PPDs...
117 if (!strcmp(argv
[1], "cat"))
118 return (cat_ppd(argv
[2]));
120 return (list_ppds(atoi(argv
[2]), atoi(argv
[3]), argv
[4]));
125 * 'add_ppd()' - Add a PPD file.
128 ppd_info_t
* /* O - PPD */
129 add_ppd(const char *name
, /* I - PPD name */
130 const char *natural_language
, /* I - Language(s) */
131 const char *make
, /* I - Manufacturer */
132 const char *make_and_model
, /* I - NickName */
133 time_t mtime
, /* I - Modification time */
134 size_t size
) /* I - File size */
136 ppd_info_t
*ppd
; /* PPD */
140 * Add a new PPD file...
143 if (NumPPDs
>= AllocPPDs
)
146 * Allocate (more) memory for the PPD files...
152 ppd
= malloc(sizeof(ppd_info_t
) * AllocPPDs
);
154 ppd
= realloc(PPDs
, sizeof(ppd_info_t
) * AllocPPDs
);
158 fprintf(stderr
, "ERROR: [cups-driverd] Ran out of memory for %d PPD files!\n",
166 ppd
= PPDs
+ NumPPDs
;
170 * Zero-out the PPD data and copy the values over...
173 memset(ppd
, 0, sizeof(ppd_info_t
));
176 ppd
->record
.mtime
= mtime
;
177 ppd
->record
.size
= size
;
179 strlcpy(ppd
->record
.name
, name
, sizeof(ppd
->record
.name
));
180 strlcpy(ppd
->record
.natural_language
, natural_language
,
181 sizeof(ppd
->record
.natural_language
));
182 strlcpy(ppd
->record
.make
, make
, sizeof(ppd
->record
.make
));
183 strlcpy(ppd
->record
.make_and_model
, make_and_model
,
184 sizeof(ppd
->record
.make_and_model
));
187 * Return the new PPD pointer...
195 * 'cat_ppd()' - Copy a PPD file to stdout.
198 int /* O - Exit code */
199 cat_ppd(const char *name
) /* I - PPD name */
206 * 'compare_names()' - Compare PPD filenames for sorting.
209 int /* O - Result of comparison */
210 compare_names(const ppd_info_t
*p0
, /* I - First PPD file */
211 const ppd_info_t
*p1
) /* I - Second PPD file */
213 return (strcasecmp(p0
->record
.name
, p1
->record
.name
));
218 * 'compare_ppds()' - Compare PPD file make and model names for sorting.
221 int /* O - Result of comparison */
222 compare_ppds(const ppd_info_t
*p0
, /* I - First PPD file */
223 const ppd_info_t
*p1
) /* I - Second PPD file */
225 int diff
; /* Difference between strings */
228 * First compare manufacturers...
231 if ((diff
= strcasecmp(p0
->record
.make
, p1
->record
.make
)) != 0)
233 else if ((diff
= cupsdCompareNames(p0
->record
.make_and_model
,
234 p1
->record
.make_and_model
)) != 0)
237 return (strcasecmp(p0
->record
.natural_language
,
238 p1
->record
.natural_language
));
243 * 'list_ppds()' - List PPD files.
246 int /* O - Exit code */
247 list_ppds(int request_id
, /* I - Request ID */
248 int limit
, /* I - Limit */
249 const char *opt
) /* I - Option argument */
251 int i
; /* Looping var */
252 int count
; /* Number of PPDs to send */
253 ppd_info_t
*ppd
; /* Current PPD file */
254 cups_file_t
*fp
; /* ppds.dat file */
255 struct stat fileinfo
; /* ppds.dat information */
256 char filename
[1024], /* ppds.dat filename */
257 model
[1024]; /* Model directory */
258 const char *cups_cachedir
; /* CUPS_CACHEDIR environment variable */
259 const char *cups_datadir
; /* CUPS_DATADIR environment variable */
260 int num_options
; /* Number of options */
261 cups_option_t
*options
; /* Options */
262 const char *requested
, /* requested-attributes option */
263 *make
; /* ppd-make option */
264 int send_natural_language
, /* Send ppd-natural-language attribute? */
265 send_make
, /* Send ppd-make attribute? */
266 send_make_and_model
, /* Send ppd-make-and-model attribute? */
267 send_name
; /* Send ppd-name attribute? */
271 * See if we a PPD database file...
276 PPDs
= (ppd_info_t
*)NULL
;
279 if ((cups_cachedir
= getenv("CUPS_CACHEDIR")) == NULL
)
280 cups_cachedir
= CUPS_CACHEDIR
;
282 snprintf(filename
, sizeof(filename
), "%s/ppds.dat", cups_cachedir
);
283 if (!stat(filename
, &fileinfo
) &&
284 (fileinfo
.st_size
% sizeof(ppd_rec_t
)) == 0 &&
285 (NumPPDs
= fileinfo
.st_size
/ sizeof(ppd_rec_t
)) > 0)
288 * We have a ppds.dat file, so read it!
293 if ((PPDs
= malloc(sizeof(ppd_info_t
) * NumPPDs
)) == NULL
)
295 fprintf(stderr
, "ERROR: [cups-driverd] Unable to allocate memory for %d PPD files!\n",
300 else if ((fp
= cupsFileOpen(filename
, "r")) != NULL
)
302 for (i
= NumPPDs
, ppd
= PPDs
; i
> 0; i
--, ppd
++)
304 cupsFileRead(fp
, (char *)&(ppd
->record
), sizeof(ppd_rec_t
));
310 fprintf(stderr
, "INFO: [cups-driverd] Read \"%s\", %d PPDs...\n",
315 fprintf(stderr
, "ERROR: [cups-driverd] Unable to read \"%s\" - %s\n", filename
,
322 * Load all PPDs in the specified directory and below...
325 SortedPPDs
= NumPPDs
;
327 if ((cups_datadir
= getenv("CUPS_DATADIR")) == NULL
)
328 cups_datadir
= CUPS_DATADIR
;
330 snprintf(model
, sizeof(model
), "%s/model", cups_datadir
);
331 load_ppds(model
, "");
334 * Cull PPD files that are no longer present...
337 for (i
= NumPPDs
, ppd
= PPDs
; i
> 0; i
--, ppd
++)
341 * Remove this PPD file from the list...
345 memmove(ppd
, ppd
+ 1, (i
- 1) * sizeof(ppd_info_t
));
352 * Sort the PPDs by name...
356 qsort(PPDs
, NumPPDs
, sizeof(ppd_info_t
),
357 (int (*)(const void *, const void *))compare_names
);
360 * Write the new ppds.dat file...
365 if ((fp
= cupsFileOpen(filename
, "w")) != NULL
)
367 for (i
= NumPPDs
, ppd
= PPDs
; i
> 0; i
--, ppd
++)
368 cupsFileWrite(fp
, (char *)&(ppd
->record
), sizeof(ppd_rec_t
));
372 fprintf(stderr
, "INFO: [cups-driverd] Wrote \"%s\", %d PPDs...\n",
376 fprintf(stderr
, "ERROR: [cups-driverd] Unable to write \"%s\" - %s",
377 filename
, strerror(errno
));
380 fputs("INFO: [cups-driverd] No new or changed PPDs...\n", stderr
);
383 * Scan for dynamic PPD files...
389 * Add the raw driver...
392 add_ppd("raw", "en", "Raw", "Raw Queue", 0, 0);
395 * Sort the PPDs by make and model...
399 qsort(PPDs
, NumPPDs
, sizeof(ppd_info_t
),
400 (int (*)(const void *, const void *))compare_ppds
);
403 * Send IPP attributes...
406 num_options
= cupsParseOptions(opt
, 0, &options
);
407 requested
= cupsGetOption("requested-attributes", num_options
, options
);
408 make
= cupsGetOption("ppd-make", num_options
, options
);
410 if (!requested
|| strstr(requested
, "all"))
414 send_make_and_model
= 1;
415 send_natural_language
= 1;
419 send_name
= strstr(requested
, "ppd-name") != NULL
;
420 send_make
= strstr(requested
, "ppd-make,") != NULL
||
421 strstr(requested
, ",ppd-make") != NULL
||
422 !strcmp(requested
, "ppd-make");
423 send_make_and_model
= strstr(requested
, "ppd-make-and-model") != NULL
;
424 send_natural_language
= strstr(requested
, "ppd-natural-language") != NULL
;
427 puts("Content-Type: application/ipp\n");
429 cupsdSendIPPHeader(IPP_OK
, request_id
);
430 cupsdSendIPPGroup(IPP_TAG_OPERATION
);
431 cupsdSendIPPString(IPP_TAG_CHARSET
, "attributes-charset", "utf-8");
432 cupsdSendIPPString(IPP_TAG_LANGUAGE
, "attributes-natural-language", "en-US");
434 if (limit
<= 0 || limit
> NumPPDs
)
439 for (i
= NumPPDs
, ppd
= PPDs
; count
> 0 && i
> 0; i
--, ppd
++)
440 if (!make
|| !strcasecmp(ppd
->record
.make
, make
))
448 cupsdSendIPPGroup(IPP_TAG_PRINTER
);
451 cupsdSendIPPString(IPP_TAG_NAME
, "ppd-name", ppd
->record
.name
);
453 if (send_natural_language
)
454 cupsdSendIPPString(IPP_TAG_LANGUAGE
, "ppd-natural-language",
455 ppd
->record
.natural_language
);
458 cupsdSendIPPString(IPP_TAG_TEXT
, "ppd-make", ppd
->record
.make
);
460 if (send_make_and_model
)
461 cupsdSendIPPString(IPP_TAG_TEXT
, "ppd-make-and-model",
462 ppd
->record
.make_and_model
);
465 * If we have only requested the ppd-make attribute, then skip
466 * the remaining PPDs with this make...
469 if (requested
&& !strcmp(requested
, "ppd-make"))
471 const char *this_make
; /* This ppd-make */
474 for (this_make
= ppd
->record
.make
, i
--, ppd
++; i
> 0; i
--, ppd
++)
475 if (strcasecmp(this_make
, ppd
->record
.make
))
483 cupsdSendIPPTrailer();
490 * 'load_ppds()' - Load PPD files recursively.
493 int /* O - 1 on success, 0 on failure */
494 load_ppds(const char *d
, /* I - Actual directory */
495 const char *p
) /* I - Virtual path in name */
497 int i
; /* Looping var */
498 cups_file_t
*fp
; /* Pointer to file */
499 cups_dir_t
*dir
; /* Directory pointer */
500 cups_dentry_t
*dent
; /* Directory entry */
501 char filename
[1024], /* Name of PPD or directory */
502 line
[256], /* Line from backend */
503 *ptr
, /* Pointer into name */
504 name
[128], /* Name of PPD file */
505 language
[64], /* PPD language version */
506 country
[64], /* Country code */
507 manufacturer
[256], /* Manufacturer */
508 make_model
[256], /* Make and Model */
509 model_name
[256], /* ModelName */
510 nick_name
[256]; /* NickName */
511 ppd_info_t
*ppd
, /* New PPD file */
512 key
; /* Search key */
513 int new_ppd
; /* Is this a new PPD? */
514 struct /* LanguageVersion translation table */
516 const char *version
, /* LanguageVersion string */
517 *language
; /* Language code */
529 { "japanese", "jp" },
530 { "norwegian", "no" },
532 { "portuguese", "pt" },
541 if ((dir
= cupsDirOpen(d
)) == NULL
)
543 fprintf(stderr
, "ERROR: [cups-driverd] Unable to open PPD directory \"%s\": %s\n",
548 while ((dent
= cupsDirRead(dir
)) != NULL
)
551 * See if this is a file...
554 snprintf(filename
, sizeof(filename
), "%s/%s", d
, dent
->filename
);
557 snprintf(name
, sizeof(name
), "%s/%s", p
, dent
->filename
);
559 strlcpy(name
, dent
->filename
, sizeof(name
));
561 if (S_ISDIR(dent
->fileinfo
.st_mode
))
567 if (!load_ppds(filename
, name
))
577 * See if this file has been scanned before...
582 strcpy(key
.record
.name
, name
);
584 ppd
= bsearch(&key
, PPDs
, SortedPPDs
, sizeof(ppd_info_t
),
585 (int (*)(const void *, const void *))compare_names
);
588 ppd
->record
.size
== dent
->fileinfo
.st_size
&&
589 ppd
->record
.mtime
== dent
->fileinfo
.st_mtime
)
599 * No, file is new/changed, so re-scan it...
602 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
606 * Now see if this is a PPD file...
610 cupsFileGets(fp
, line
, sizeof(line
));
612 if (strncmp(line
, "*PPD-Adobe:", 11))
615 * Nope, close the file and continue...
624 * Now read until we get the NickName field...
627 model_name
[0] = '\0';
629 manufacturer
[0] = '\0';
630 strcpy(language
, "en");
632 while (cupsFileGets(fp
, line
, sizeof(line
)) != NULL
)
634 if (!strncmp(line
, "*Manufacturer:", 14))
635 sscanf(line
, "%*[^\"]\"%255[^\"]", manufacturer
);
636 else if (!strncmp(line
, "*ModelName:", 11))
637 sscanf(line
, "%*[^\"]\"%127[^\"]", model_name
);
638 else if (!strncmp(line
, "*LanguageVersion:", 17))
639 sscanf(line
, "%*[^:]:%63s", language
);
640 else if (!strncmp(line
, "*NickName:", 10))
641 sscanf(line
, "%*[^\"]\"%255[^\"]", nick_name
);
642 else if (!strncmp(line
, "*OpenUI", 7))
645 * Stop early if we have a NickName or ModelName attributes
646 * before the first OpenUI...
649 if (model_name
[0] || nick_name
[0])
654 * Stop early if we have both the Manufacturer and NickName
658 if (manufacturer
[0] && nick_name
[0])
669 * See if we got all of the required info...
673 strcpy(make_model
, nick_name
);
675 strcpy(make_model
, model_name
);
677 while (isspace(make_model
[0] & 255))
678 cups_strcpy(make_model
, make_model
+ 1);
681 continue; /* Nope... */
684 * See if we got a manufacturer...
687 while (isspace(manufacturer
[0] & 255))
688 cups_strcpy(manufacturer
, manufacturer
+ 1);
690 if (!manufacturer
[0] || !strcmp(manufacturer
, "ESP"))
693 * Nope, copy the first part of the make and model then...
696 strlcpy(manufacturer
, make_model
, sizeof(manufacturer
));
699 * Truncate at the first space, dash, or slash, or make the
700 * manufacturer "Other"...
703 for (ptr
= manufacturer
; *ptr
; ptr
++)
704 if (*ptr
== ' ' || *ptr
== '-' || *ptr
== '/')
707 if (*ptr
&& ptr
> manufacturer
)
709 else if (!strncasecmp(manufacturer
, "agfa", 4))
710 strcpy(manufacturer
, "AGFA");
711 else if (!strncasecmp(manufacturer
, "herk", 4) ||
712 !strncasecmp(manufacturer
, "linotype", 8))
713 strcpy(manufacturer
, "LHAG");
715 strcpy(manufacturer
, "Other");
718 * Hack for various vendors...
721 if (!strcasecmp(manufacturer
, "XPrint"))
722 strcpy(manufacturer
, "Xerox");
723 else if (!strcasecmp(manufacturer
, "Eastman"))
724 strcpy(manufacturer
, "Kodak");
725 else if (!strcasecmp(manufacturer
, "laserwriter"))
726 strcpy(manufacturer
, "Apple");
727 else if (!strcasecmp(manufacturer
, "colorpoint"))
728 strcpy(manufacturer
, "Seiko");
729 else if (!strcasecmp(manufacturer
, "fiery"))
730 strcpy(manufacturer
, "EFI");
731 else if (!strcasecmp(manufacturer
, "ps") ||
732 !strcasecmp(manufacturer
, "colorpass"))
733 strcpy(manufacturer
, "Canon");
734 else if (!strncasecmp(manufacturer
, "primera", 7))
735 strcpy(manufacturer
, "Fargo");
736 else if (!strcasecmp(manufacturer
, "designjet"))
737 strcpy(manufacturer
, "HP");
739 else if (!strncasecmp(manufacturer
, "LHAG", 4) ||
740 !strncasecmp(manufacturer
, "linotype", 8))
741 strcpy(manufacturer
, "LHAG");
744 * Fix the language as needed...
747 if ((ptr
= strchr(language
, '-')) != NULL
)
749 else if ((ptr
= strchr(language
, '_')) != NULL
)
755 * Setup the country suffix...
759 cups_strcpy(country
+ 1, ptr
);
764 * No country suffix...
770 for (i
= 0; i
< (int)(sizeof(languages
) / sizeof(languages
[0])); i
++)
771 if (!strcasecmp(languages
[i
].version
, language
))
774 if (i
< (int)(sizeof(languages
) / sizeof(languages
[0])))
777 * Found a known language...
780 snprintf(language
, sizeof(language
), "%s%s", languages
[i
].language
,
786 * Unknown language; use "xx"...
789 strcpy(language
, "xx");
793 * Add the PPD file...
801 * Add new PPD file...
804 fprintf(stderr
, "DEBUG: [cups-driverd] Adding ppd \"%s\"...\n", name
);
806 if (!add_ppd(name
, language
, manufacturer
, make_model
,
807 dent
->fileinfo
.st_mtime
, dent
->fileinfo
.st_size
))
816 * Update existing record...
819 fprintf(stderr
, "DEBUG: [cups-driverd] Updating ppd \"%s\"...\n", name
);
821 memset(ppd
, 0, sizeof(ppd_info_t
));
824 ppd
->record
.mtime
= dent
->fileinfo
.st_mtime
;
825 ppd
->record
.size
= dent
->fileinfo
.st_size
;
827 strlcpy(ppd
->record
.name
, name
, sizeof(ppd
->record
.name
));
828 strlcpy(ppd
->record
.make
, manufacturer
, sizeof(ppd
->record
.make
));
829 strlcpy(ppd
->record
.make_and_model
, make_model
,
830 sizeof(ppd
->record
.make_and_model
));
831 strlcpy(ppd
->record
.natural_language
, language
,
832 sizeof(ppd
->record
.natural_language
));
845 * 'load_drivers()' - Load driver-generated PPD files.
848 int /* O - 1 on success, 0 on failure */
851 const char *server_bin
; /* CUPS_SERVERBIN environment variable */
852 char drivers
[1024]; /* Location of driver programs */
853 FILE *fp
; /* Pipe to driver program */
854 cups_dir_t
*dir
; /* Directory pointer */
855 cups_dentry_t
*dent
; /* Directory entry */
856 char filename
[1024], /* Name of driver */
857 line
[2048], /* Line from driver */
858 name
[512], /* ppd-name */
859 natural_language
[128], /* ppd-natural-language */
860 make
[128], /* ppd-make */
861 make_and_model
[256]; /* ppd-make-and-model */
865 * Try opening the driver directory...
868 if ((server_bin
= getenv("CUPS_SERVERBIN")) == NULL
)
869 server_bin
= CUPS_SERVERBIN
;
871 snprintf(drivers
, sizeof(drivers
), "%s/driver", server_bin
);
873 if ((dir
= cupsDirOpen(drivers
)) == NULL
)
875 fprintf(stderr
, "ERROR: [cups-driverd] Unable to open driver directory \"%s\": %s",
876 drivers
, strerror(errno
));
881 * Loop through all of the device drivers...
884 while ((dent
= cupsDirRead(dir
)) != NULL
)
887 * Only look at executable files...
890 if (!(dent
->fileinfo
.st_mode
& 0111) || !S_ISREG(dent
->fileinfo
.st_mode
))
894 * Run the driver with no arguments and collect the output...
897 snprintf(filename
, sizeof(filename
), "%s/%s", drivers
, dent
->filename
);
898 if ((fp
= popen(filename
, "r")) != NULL
)
900 while (fgets(line
, sizeof(line
), fp
) != NULL
)
903 * Each line is of the form:
905 * \"ppd-name\" ppd-natural-language "ppd-make" "ppd-make-and-model"
908 if (sscanf(line
, "\"%511[^\"]\"%127s%*[ \t]\"%127[^\"]\"%*[ \t]\"%256[^\"]\"",
909 name
, natural_language
, make
, make_and_model
) != 4)
912 * Bad format; strip trailing newline and write an error message.
915 if (line
[strlen(line
) - 1] == '\n')
916 line
[strlen(line
) - 1] = '\0';
918 fprintf(stderr
, "ERROR: [cups-driverd] Bad line from \"%s\": %s\n",
919 dent
->filename
, line
);
925 * Add the device to the array of available devices...
928 if (!add_ppd(name
, natural_language
, make
, make_and_model
, 0, 0))
934 fprintf(stderr
, "DEBUG: [cups-driverd] Added dynamic PPD \"%s\"...\n",
942 fprintf(stderr
, "WARNING: [cups-driverd] Unable to execute \"%s\": %s\n",
943 filename
, strerror(errno
));