]>
git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/cups-deviced.c
2 * "$Id: cups-deviced.c 7011 2007-10-10 21:13:35Z mike $"
4 * Device scanning mini-daemon for the Common UNIX Printing System (CUPS).
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 1997-2006 by Easy Software Products.
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() - Scan for devices and return an IPP response.
18 * add_device() - Add a new device to the list.
19 * compare_devices() - Compare device names to eliminate duplicates.
20 * create_strings_array() - Create a CUPS array of strings.
21 * get_current_time() - Get the current time as a double value in seconds.
22 * get_device() - Get a device from a backend.
23 * process_children() - Process all dead children...
24 * sigchld_handler() - Handle 'child' signals from old processes.
25 * start_backend() - Run a backend to gather the available devices.
29 * Include necessary headers...
33 #include <cups/array.h>
44 #define MAX_BACKENDS 200 /* Maximum number of backends we'll run */
48 * Backend information...
53 char *name
; /* Name of backend */
54 int pid
, /* Process ID */
55 status
; /* Exit status */
56 cups_file_t
*pipe
; /* Pipe from backend stdout */
57 int count
; /* Number of devices found */
62 * Device information structure...
67 char device_class
[128], /* Device class */
68 device_make_and_model
[128], /* Make and model, if known */
69 device_info
[128], /* Device info/description */
70 device_uri
[1024], /* Device URI */
71 device_id
[1024]; /* 1284 Device ID */
79 static int num_backends
= 0,
83 static cupsd_backend_t backends
[MAX_BACKENDS
];
84 /* Array of backends */
85 static struct pollfd backend_fds
[MAX_BACKENDS
];
86 /* Array for poll() */
87 static cups_array_t
*devices
; /* Array of devices */
88 static int normal_user
; /* Normal user ID */
89 static int device_limit
; /* Maximum number of devices */
90 static int send_class
, /* Send device-class attribute? */
91 send_info
, /* Send device-info attribute? */
93 /* Send device-make-and-model attribute? */
94 send_uri
, /* Send device-uri attribute? */
95 send_id
; /* Send device-id attribute? */
96 static int dead_children
= 0;
104 static int add_device(const char *device_class
,
105 const char *device_make_and_model
,
106 const char *device_info
,
107 const char *device_uri
,
108 const char *device_id
);
109 static int compare_devices(cupsd_device_t
*p0
,
111 static cups_array_t
*create_strings_array(const char *s
);
112 static double get_current_time(void);
113 static int get_device(cupsd_backend_t
*backend
);
114 static void process_children(void);
115 static void sigchld_handler(int sig
);
116 static int start_backend(const char *backend
, int root
);
120 * 'main()' - Scan for devices and return an IPP response.
124 * cups-deviced request_id limit options
127 int /* O - Exit code */
128 main(int argc
, /* I - Number of command-line args */
129 char *argv
[]) /* I - Command-line arguments */
131 int i
; /* Looping var */
132 int request_id
; /* Request ID */
133 int timeout
; /* Timeout in seconds */
134 const char *server_bin
; /* CUPS_SERVERBIN environment variable */
135 char filename
[1024]; /* Backend directory filename */
136 cups_dir_t
*dir
; /* Directory pointer */
137 cups_dentry_t
*dent
; /* Directory entry */
138 double current_time
, /* Current time */
139 end_time
; /* Ending time */
140 int num_options
; /* Number of options */
141 cups_option_t
*options
; /* Options */
142 cups_array_t
*requested
, /* requested-attributes values */
143 *exclude
; /* exclude-schemes values */
144 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
145 struct sigaction action
; /* Actions for POSIX signals */
146 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
149 setbuf(stderr
, NULL
);
152 * Check the command-line...
157 fputs("Usage: cups-deviced request-id limit timeout user-id options\n", stderr
);
162 request_id
= atoi(argv
[1]);
165 fprintf(stderr
, "ERROR: [cups-deviced] Bad request ID %d!\n", request_id
);
170 device_limit
= atoi(argv
[2]);
171 if (device_limit
< 0)
173 fprintf(stderr
, "ERROR: [cups-deviced] Bad limit %d!\n", device_limit
);
178 timeout
= atoi(argv
[3]);
181 fprintf(stderr
, "ERROR: [cups-deviced] Bad timeout %d!\n", timeout
);
186 normal_user
= atoi(argv
[4]);
187 if (normal_user
<= 0)
189 fprintf(stderr
, "ERROR: [cups-deviced] Bad user %d!\n", normal_user
);
194 num_options
= cupsParseOptions(argv
[5], 0, &options
);
195 requested
= create_strings_array(cupsGetOption("requested-attributes",
196 num_options
, options
));
197 exclude
= create_strings_array(cupsGetOption("exclude-schemes",
198 num_options
, options
));
200 if (!requested
|| cupsArrayFind(requested
, "all") != NULL
)
201 send_class
= send_info
= send_make_and_model
= send_uri
= send_id
= 1;
204 send_class
= cupsArrayFind(requested
, "device-class") != NULL
;
205 send_info
= cupsArrayFind(requested
, "device-info") != NULL
;
206 send_make_and_model
= cupsArrayFind(requested
, "device-make-and-model") != NULL
;
207 send_uri
= cupsArrayFind(requested
, "device-uri") != NULL
;
208 send_id
= cupsArrayFind(requested
, "device-id") != NULL
;
212 * Listen to child signals...
215 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
216 sigset(SIGCHLD
, sigchld_handler
);
217 #elif defined(HAVE_SIGACTION)
218 memset(&action
, 0, sizeof(action
));
220 sigemptyset(&action
.sa_mask
);
221 sigaddset(&action
.sa_mask
, SIGCHLD
);
222 action
.sa_handler
= sigchld_handler
;
223 sigaction(SIGCHLD
, &action
, NULL
);
225 signal(SIGCLD
, sigchld_handler
); /* No, SIGCLD isn't a typo... */
226 #endif /* HAVE_SIGSET */
229 * Try opening the backend directory...
232 if ((server_bin
= getenv("CUPS_SERVERBIN")) == NULL
)
233 server_bin
= CUPS_SERVERBIN
;
235 snprintf(filename
, sizeof(filename
), "%s/backend", server_bin
);
237 if ((dir
= cupsDirOpen(filename
)) == NULL
)
239 fprintf(stderr
, "ERROR: [cups-deviced] Unable to open backend directory "
240 "\"%s\": %s", filename
, strerror(errno
));
246 * Setup the devices array...
249 devices
= cupsArrayNew((cups_array_func_t
)compare_devices
, NULL
);
252 * Loop through all of the device backends...
255 while ((dent
= cupsDirRead(dir
)) != NULL
)
258 * Skip entries that are not executable files...
261 if (!S_ISREG(dent
->fileinfo
.st_mode
) ||
262 !isalnum(dent
->filename
[0] & 255) ||
263 (dent
->fileinfo
.st_mode
& (S_IRUSR
| S_IXUSR
)) != (S_IRUSR
| S_IXUSR
))
266 if (cupsArrayFind(exclude
, dent
->filename
))
270 * Backends without permissions for normal users run as root,
271 * all others run as the unprivileged user...
274 start_backend(dent
->filename
,
275 !(dent
->fileinfo
.st_mode
& (S_IRWXG
| S_IRWXO
)));
284 if (getenv("SOFTWARE"))
285 puts("Content-Type: application/ipp\n");
287 cupsdSendIPPHeader(IPP_OK
, request_id
);
288 cupsdSendIPPGroup(IPP_TAG_OPERATION
);
289 cupsdSendIPPString(IPP_TAG_CHARSET
, "attributes-charset", "utf-8");
290 cupsdSendIPPString(IPP_TAG_LANGUAGE
, "attributes-natural-language", "en-US");
292 end_time
= get_current_time() + timeout
;
294 while (active_backends
> 0 && (current_time
= get_current_time()) < end_time
)
297 * Collect the output from the backends...
300 timeout
= (int)(1000 * (end_time
- current_time
));
302 if (poll(backend_fds
, num_backends
, timeout
) > 0)
304 for (i
= 0; i
< num_backends
; i
++)
305 if (backend_fds
[i
].revents
&& backends
[i
].pipe
)
306 if (get_device(backends
+ i
))
308 backend_fds
[i
].fd
= 0;
309 backend_fds
[i
].events
= 0;
314 * Get exit status from children...
321 cupsdSendIPPTrailer();
324 * Terminate any remaining backends and exit...
327 if (active_backends
> 0)
329 for (i
= 0; i
< num_backends
; i
++)
331 kill(backends
[i
].pid
, SIGTERM
);
339 * 'add_device()' - Add a new device to the list.
342 static int /* O - 0 on success, -1 on error */
344 const char *device_class
, /* I - Device class */
345 const char *device_make_and_model
, /* I - Device make and model */
346 const char *device_info
, /* I - Device information */
347 const char *device_uri
, /* I - Device URI */
348 const char *device_id
) /* I - 1284 device ID */
350 cupsd_device_t
*device
, /* New device */
351 *temp
; /* Found device */
355 * Allocate memory for the device record...
358 if ((device
= calloc(1, sizeof(cupsd_device_t
))) == NULL
)
360 fputs("ERROR: [cups-deviced] Ran out of memory allocating a device!\n",
366 * Copy the strings over...
369 strlcpy(device
->device_class
, device_class
, sizeof(device
->device_class
));
370 strlcpy(device
->device_make_and_model
, device_make_and_model
,
371 sizeof(device
->device_make_and_model
));
372 strlcpy(device
->device_info
, device_info
, sizeof(device
->device_info
));
373 strlcpy(device
->device_uri
, device_uri
, sizeof(device
->device_uri
));
374 strlcpy(device
->device_id
, device_id
, sizeof(device
->device_id
));
377 * Add the device to the array and return...
380 if ((temp
= cupsArrayFind(devices
, device
)) != NULL
)
390 cupsArrayAdd(devices
, device
);
392 if (device_limit
<= 0 || cupsArrayCount(devices
) < device_limit
)
395 * Send device info...
398 cupsdSendIPPGroup(IPP_TAG_PRINTER
);
400 cupsdSendIPPString(IPP_TAG_KEYWORD
, "device-class",
401 device
->device_class
);
403 cupsdSendIPPString(IPP_TAG_TEXT
, "device-info", device
->device_info
);
404 if (send_make_and_model
)
405 cupsdSendIPPString(IPP_TAG_TEXT
, "device-make-and-model",
406 device
->device_make_and_model
);
408 cupsdSendIPPString(IPP_TAG_URI
, "device-uri", device
->device_uri
);
410 cupsdSendIPPString(IPP_TAG_TEXT
, "device-id", device
->device_id
);
413 fputs("DEBUG: Flushed attributes...\n", stderr
);
422 * 'compare_devices()' - Compare device names to eliminate duplicates.
425 static int /* O - Result of comparison */
426 compare_devices(cupsd_device_t
*d0
, /* I - First device */
427 cupsd_device_t
*d1
) /* I - Second device */
429 int diff
; /* Difference between strings */
433 * Sort devices by device-info, device-class, and device-uri...
436 if ((diff
= cupsdCompareNames(d0
->device_info
, d1
->device_info
)) != 0)
438 else if ((diff
= strcasecmp(d0
->device_class
, d1
->device_class
)) != 0)
441 return (strcasecmp(d0
->device_uri
, d1
->device_uri
));
446 * 'create_strings_array()' - Create a CUPS array of strings.
449 static cups_array_t
* /* O - CUPS array */
450 create_strings_array(const char *s
) /* I - Comma-delimited strings */
452 cups_array_t
*a
; /* CUPS array */
453 const char *start
, /* Start of string */
454 *end
; /* End of string */
455 char *ptr
; /* New string */
461 if ((a
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
)) != NULL
)
463 for (start
= end
= s
; *end
; start
= end
+ 1)
466 * Find the end of the current delimited string...
469 if ((end
= strchr(start
, ',')) == NULL
)
470 end
= start
+ strlen(start
);
473 * Duplicate the string and add it to the array...
476 if ((ptr
= calloc(1, end
- start
+ 1)) == NULL
)
479 memcpy(ptr
, start
, end
- start
);
480 cupsArrayAdd(a
, ptr
);
489 * 'get_current_time()' - Get the current time as a double value in seconds.
492 static double /* O - Time in seconds */
493 get_current_time(void)
495 struct timeval curtime
; /* Current time */
498 gettimeofday(&curtime
, NULL
);
500 return (curtime
.tv_sec
+ 0.000001 * curtime
.tv_usec
);
505 * 'get_device()' - Get a device from a backend.
508 static int /* O - 0 on success, -1 on error */
509 get_device(cupsd_backend_t
*backend
) /* I - Backend to read from */
511 char line
[2048], /* Line from backend */
512 dclass
[64], /* Device class */
513 uri
[1024], /* Device URI */
514 info
[128], /* Device info */
515 make_model
[256], /* Make and model */
516 device_id
[1024]; /* 1284 device ID */
519 if (cupsFileGets(backend
->pipe
, line
, sizeof(line
)))
522 * Each line is of the form:
524 * class URI "make model" "name" ["1284 device ID"]
530 "%63s%1023s%*[ \t]\"%255[^\"]\"%*[ \t]\"%127[^\"]\""
531 "%*[ \t]\"%1023[^\"]",
532 dclass
, uri
, make_model
, info
, device_id
) < 4)
535 * Bad format; strip trailing newline and write an error message.
538 if (line
[strlen(line
) - 1] == '\n')
539 line
[strlen(line
) - 1] = '\0';
541 fprintf(stderr
, "ERROR: [cups-deviced] Bad line from \"%s\": %s\n",
542 backend
->name
, line
);
547 * Add the device to the array of available devices...
550 if (!add_device(dclass
, make_model
, info
, uri
, device_id
))
551 fprintf(stderr
, "DEBUG: [cups-deviced] Found device \"%s\"...\n", uri
);
561 cupsFileClose(backend
->pipe
);
562 backend
->pipe
= NULL
;
569 * 'process_children()' - Process all dead children...
573 process_children(void)
575 int i
; /* Looping var */
576 int status
; /* Exit status of child */
577 int pid
; /* Process ID of child */
578 cupsd_backend_t
*backend
; /* Current backend */
579 const char *name
; /* Name of process */
583 * Reset the dead_children flag...
589 * Collect the exit status of some children...
593 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0)
594 #elif defined(HAVE_WAIT3)
595 while ((pid
= wait3(&status
, WNOHANG
, NULL
)) > 0)
597 if ((pid
= wait(&status
)) > 0)
598 #endif /* HAVE_WAITPID */
600 if (status
== SIGTERM
)
603 for (i
= num_backends
, backend
= backends
; i
> 0; i
--, backend
++)
604 if (backend
->pid
== pid
)
609 name
= backend
->name
;
611 backend
->status
= status
;
620 if (WIFEXITED(status
))
622 "ERROR: [cups-deviced] PID %d (%s) stopped with status %d!\n",
623 pid
, name
, WEXITSTATUS(status
));
626 "ERROR: [cups-deviced] PID %d (%s) crashed on signal %d!\n",
627 pid
, name
, WTERMSIG(status
));
631 "DEBUG: [cups-deviced] PID %d (%s) exited with no errors.\n",
638 * 'sigchld_handler()' - Handle 'child' signals from old processes.
642 sigchld_handler(int sig
) /* I - Signal number */
647 * Flag that we have dead children...
653 * Reset the signal handler as needed...
656 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
657 signal(SIGCLD
, sigchld_handler
);
658 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
663 * 'start_backend()' - Run a backend to gather the available devices.
666 static int /* O - 0 on success, -1 on error */
667 start_backend(const char *name
, /* I - Backend to run */
668 int root
) /* I - Run as root? */
670 const char *server_bin
; /* CUPS_SERVERBIN environment variable */
671 char program
[1024]; /* Full path to backend */
672 int fds
[2]; /* Pipe file descriptors */
673 cupsd_backend_t
*backend
; /* Current backend */
676 if (num_backends
>= MAX_BACKENDS
)
678 fprintf(stderr
, "ERROR: Too many backends (%d)!\n", num_backends
);
684 fprintf(stderr
, "ERROR: Unable to create a pipe for \"%s\" - %s\n",
685 name
, strerror(errno
));
689 if ((server_bin
= getenv("CUPS_SERVERBIN")) == NULL
)
690 server_bin
= CUPS_SERVERBIN
;
692 snprintf(program
, sizeof(program
), "%s/backend/%s", server_bin
, name
);
694 backend
= backends
+ num_backends
;
696 if ((backend
->pid
= fork()) < 0)
702 fprintf(stderr
, "ERROR: [cups-deviced] Unable to fork for \"%s\" - %s\n",
703 program
, strerror(errno
));
708 else if (!backend
->pid
)
711 * Child comes here...
714 if (!getuid() && !root
)
715 setuid(normal_user
); /* Run as restricted user */
717 close(0); /* </dev/null */
718 open("/dev/null", O_RDONLY
);
720 close(1); /* >pipe */
723 close(fds
[0]); /* Close copies of pipes */
726 execl(program
, name
, (char *)0); /* Run it! */
727 fprintf(stderr
, "ERROR: [cups-deviced] Unable to execute \"%s\" - %s\n",
728 program
, strerror(errno
));
733 * Parent comes here, allocate a backend and open the input side of the
737 fprintf(stderr
, "DEBUG: [cups-deviced] Started backend %s (PID %d)\n",
738 program
, backend
->pid
);
742 backend_fds
[num_backends
].fd
= fds
[0];
743 backend_fds
[num_backends
].events
= POLLIN
;
745 backend
->name
= strdup(name
);
747 backend
->pipe
= cupsFileOpenFd(fds
[0], "r");
758 * End of "$Id: cups-deviced.c 7011 2007-10-10 21:13:35Z mike $".