]>
git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/cups-deviced.c
4 * Device scanning mini-daemon for CUPS.
6 * Copyright 2007-2011 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 * get_current_time() - Get the current time as a double value in seconds.
21 * get_device() - Get a device from a backend.
22 * process_children() - Process all dead children...
23 * sigchld_handler() - Handle 'child' signals from old processes.
24 * start_backend() - Run a backend to gather the available devices.
28 * Include necessary headers...
32 #include <cups/array.h>
43 #define MAX_BACKENDS 200 /* Maximum number of backends we'll run */
47 * Backend information...
52 char *name
; /* Name of backend */
53 int pid
, /* Process ID */
54 status
; /* Exit status */
55 cups_file_t
*pipe
; /* Pipe from backend stdout */
56 int count
; /* Number of devices found */
61 * Device information structure...
66 char device_class
[128], /* Device class */
67 device_info
[128], /* Device info/description */
68 device_uri
[1024]; /* Device URI */
76 static int num_backends
= 0,
80 static cupsd_backend_t backends
[MAX_BACKENDS
];
81 /* Array of backends */
82 static struct pollfd backend_fds
[MAX_BACKENDS
];
83 /* Array for poll() */
84 static cups_array_t
*devices
; /* Array of devices */
85 static int normal_user
; /* Normal user ID */
86 static int device_limit
; /* Maximum number of devices */
87 static int send_class
, /* Send device-class attribute? */
88 send_info
, /* Send device-info attribute? */
90 /* Send device-make-and-model attribute? */
91 send_uri
, /* Send device-uri attribute? */
92 send_id
, /* Send device-id attribute? */
93 send_location
; /* Send device-location attribute? */
94 static int dead_children
= 0;
102 static int add_device(const char *device_class
,
103 const char *device_make_and_model
,
104 const char *device_info
,
105 const char *device_uri
,
106 const char *device_id
,
107 const char *device_location
);
108 static int compare_devices(cupsd_device_t
*p0
,
110 static double get_current_time(void);
111 static int get_device(cupsd_backend_t
*backend
);
112 static void process_children(void);
113 static void sigchld_handler(int sig
);
114 static int start_backend(const char *backend
, int root
);
118 * 'main()' - Scan for devices and return an IPP response.
122 * cups-deviced request_id limit options
125 int /* O - Exit code */
126 main(int argc
, /* I - Number of command-line args */
127 char *argv
[]) /* I - Command-line arguments */
129 int i
; /* Looping var */
130 int request_id
; /* Request ID */
131 int timeout
; /* Timeout in seconds */
132 const char *server_bin
; /* CUPS_SERVERBIN environment variable */
133 char filename
[1024]; /* Backend directory filename */
134 cups_dir_t
*dir
; /* Directory pointer */
135 cups_dentry_t
*dent
; /* Directory entry */
136 double current_time
, /* Current time */
137 end_time
; /* Ending time */
138 int num_options
; /* Number of options */
139 cups_option_t
*options
; /* Options */
140 cups_array_t
*requested
, /* requested-attributes values */
141 *exclude
, /* exclude-schemes values */
142 *include
; /* include-schemes values */
143 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
144 struct sigaction action
; /* Actions for POSIX signals */
145 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
148 setbuf(stderr
, NULL
);
151 * Check the command-line...
156 fputs("Usage: cups-deviced request-id limit timeout user-id options\n", stderr
);
161 request_id
= atoi(argv
[1]);
164 fprintf(stderr
, "ERROR: [cups-deviced] Bad request ID %d!\n", request_id
);
169 device_limit
= atoi(argv
[2]);
170 if (device_limit
< 0)
172 fprintf(stderr
, "ERROR: [cups-deviced] Bad limit %d!\n", device_limit
);
177 timeout
= atoi(argv
[3]);
180 fprintf(stderr
, "ERROR: [cups-deviced] Bad timeout %d!\n", timeout
);
185 normal_user
= atoi(argv
[4]);
186 if (normal_user
<= 0)
188 fprintf(stderr
, "ERROR: [cups-deviced] Bad user %d!\n", normal_user
);
193 num_options
= cupsParseOptions(argv
[5], 0, &options
);
194 requested
= cupsdCreateStringsArray(cupsGetOption("requested-attributes",
195 num_options
, options
));
196 exclude
= cupsdCreateStringsArray(cupsGetOption("exclude-schemes",
197 num_options
, options
));
198 include
= cupsdCreateStringsArray(cupsGetOption("include-schemes",
199 num_options
, options
));
201 if (!requested
|| cupsArrayFind(requested
, "all") != NULL
)
203 send_class
= send_info
= send_make_and_model
= send_uri
= send_id
=
208 send_class
= cupsArrayFind(requested
, "device-class") != NULL
;
209 send_info
= cupsArrayFind(requested
, "device-info") != NULL
;
210 send_make_and_model
= cupsArrayFind(requested
, "device-make-and-model") != NULL
;
211 send_uri
= cupsArrayFind(requested
, "device-uri") != NULL
;
212 send_id
= cupsArrayFind(requested
, "device-id") != NULL
;
213 send_location
= cupsArrayFind(requested
, "device-location") != NULL
;
217 * Listen to child signals...
220 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
221 sigset(SIGCHLD
, sigchld_handler
);
222 #elif defined(HAVE_SIGACTION)
223 memset(&action
, 0, sizeof(action
));
225 sigemptyset(&action
.sa_mask
);
226 sigaddset(&action
.sa_mask
, SIGCHLD
);
227 action
.sa_handler
= sigchld_handler
;
228 sigaction(SIGCHLD
, &action
, NULL
);
230 signal(SIGCLD
, sigchld_handler
); /* No, SIGCLD isn't a typo... */
231 #endif /* HAVE_SIGSET */
234 * Try opening the backend directory...
237 if ((server_bin
= getenv("CUPS_SERVERBIN")) == NULL
)
238 server_bin
= CUPS_SERVERBIN
;
240 snprintf(filename
, sizeof(filename
), "%s/backend", server_bin
);
242 if ((dir
= cupsDirOpen(filename
)) == NULL
)
244 fprintf(stderr
, "ERROR: [cups-deviced] Unable to open backend directory "
245 "\"%s\": %s", filename
, strerror(errno
));
251 * Setup the devices array...
254 devices
= cupsArrayNew((cups_array_func_t
)compare_devices
, NULL
);
257 * Loop through all of the device backends...
260 while ((dent
= cupsDirRead(dir
)) != NULL
)
263 * Skip entries that are not executable files...
266 if (!S_ISREG(dent
->fileinfo
.st_mode
) ||
267 !isalnum(dent
->filename
[0] & 255) ||
268 (dent
->fileinfo
.st_mode
& (S_IRUSR
| S_IXUSR
)) != (S_IRUSR
| S_IXUSR
))
272 * Skip excluded or not included backends...
275 if (cupsArrayFind(exclude
, dent
->filename
) ||
276 (include
&& !cupsArrayFind(include
, dent
->filename
)))
280 * Backends without permissions for normal users run as root,
281 * all others run as the unprivileged user...
284 start_backend(dent
->filename
,
285 !(dent
->fileinfo
.st_mode
& (S_IRWXG
| S_IRWXO
)));
294 if (getenv("SOFTWARE"))
295 puts("Content-Type: application/ipp\n");
297 cupsdSendIPPHeader(IPP_OK
, request_id
);
298 cupsdSendIPPGroup(IPP_TAG_OPERATION
);
299 cupsdSendIPPString(IPP_TAG_CHARSET
, "attributes-charset", "utf-8");
300 cupsdSendIPPString(IPP_TAG_LANGUAGE
, "attributes-natural-language", "en-US");
302 end_time
= get_current_time() + timeout
;
304 while (active_backends
> 0 && (current_time
= get_current_time()) < end_time
)
307 * Collect the output from the backends...
310 timeout
= (int)(1000 * (end_time
- current_time
));
312 if (poll(backend_fds
, num_backends
, timeout
) > 0)
314 for (i
= 0; i
< num_backends
; i
++)
315 if (backend_fds
[i
].revents
&& backends
[i
].pipe
)
317 cups_file_t
*bpipe
= backends
[i
].pipe
;
318 /* Copy of pipe for backend... */
322 if (get_device(backends
+ i
))
324 backend_fds
[i
].fd
= 0;
325 backend_fds
[i
].events
= 0;
330 memchr(bpipe
->ptr
, '\n', bpipe
->end
- bpipe
->ptr
));
335 * Get exit status from children...
342 cupsdSendIPPTrailer();
345 * Terminate any remaining backends and exit...
348 if (active_backends
> 0)
350 for (i
= 0; i
< num_backends
; i
++)
352 kill(backends
[i
].pid
, SIGTERM
);
360 * 'add_device()' - Add a new device to the list.
363 static int /* O - 0 on success, -1 on error */
365 const char *device_class
, /* I - Device class */
366 const char *device_make_and_model
, /* I - Device make and model */
367 const char *device_info
, /* I - Device information */
368 const char *device_uri
, /* I - Device URI */
369 const char *device_id
, /* I - 1284 device ID */
370 const char *device_location
) /* I - Physical location */
372 cupsd_device_t
*device
; /* New device */
376 * Allocate memory for the device record...
379 if ((device
= calloc(1, sizeof(cupsd_device_t
))) == NULL
)
381 fputs("ERROR: [cups-deviced] Ran out of memory allocating a device!\n",
387 * Copy the strings over...
390 strlcpy(device
->device_class
, device_class
, sizeof(device
->device_class
));
391 strlcpy(device
->device_info
, device_info
, sizeof(device
->device_info
));
392 strlcpy(device
->device_uri
, device_uri
, sizeof(device
->device_uri
));
395 * Add the device to the array and return...
398 if (cupsArrayFind(devices
, device
))
408 cupsArrayAdd(devices
, device
);
410 if (device_limit
<= 0 || cupsArrayCount(devices
) < device_limit
)
413 * Send device info...
416 cupsdSendIPPGroup(IPP_TAG_PRINTER
);
418 cupsdSendIPPString(IPP_TAG_KEYWORD
, "device-class",
421 cupsdSendIPPString(IPP_TAG_TEXT
, "device-info", device_info
);
422 if (send_make_and_model
)
423 cupsdSendIPPString(IPP_TAG_TEXT
, "device-make-and-model",
424 device_make_and_model
);
426 cupsdSendIPPString(IPP_TAG_URI
, "device-uri", device_uri
);
428 cupsdSendIPPString(IPP_TAG_TEXT
, "device-id",
429 device_id
? device_id
: "");
431 cupsdSendIPPString(IPP_TAG_TEXT
, "device-location",
432 device_location
? device_location
: "");
435 fputs("DEBUG: Flushed attributes...\n", stderr
);
444 * 'compare_devices()' - Compare device names to eliminate duplicates.
447 static int /* O - Result of comparison */
448 compare_devices(cupsd_device_t
*d0
, /* I - First device */
449 cupsd_device_t
*d1
) /* I - Second device */
451 int diff
; /* Difference between strings */
455 * Sort devices by device-info, device-class, and device-uri...
458 if ((diff
= cupsdCompareNames(d0
->device_info
, d1
->device_info
)) != 0)
460 else if ((diff
= _cups_strcasecmp(d0
->device_class
, d1
->device_class
)) != 0)
463 return (_cups_strcasecmp(d0
->device_uri
, d1
->device_uri
));
468 * 'get_current_time()' - Get the current time as a double value in seconds.
471 static double /* O - Time in seconds */
472 get_current_time(void)
474 struct timeval curtime
; /* Current time */
477 gettimeofday(&curtime
, NULL
);
479 return (curtime
.tv_sec
+ 0.000001 * curtime
.tv_usec
);
484 * 'get_device()' - Get a device from a backend.
487 static int /* O - 0 on success, -1 on error */
488 get_device(cupsd_backend_t
*backend
) /* I - Backend to read from */
490 char line
[2048], /* Line from backend */
491 temp
[2048], /* Copy of line */
492 *ptr
, /* Pointer into line */
493 *dclass
, /* Device class */
494 *uri
, /* Device URI */
495 *make_model
, /* Make and model */
496 *info
, /* Device info */
497 *device_id
, /* 1284 device ID */
498 *location
; /* Physical location */
501 if (cupsFileGets(backend
->pipe
, line
, sizeof(line
)))
504 * Each line is of the form:
506 * class URI "make model" "name" ["1284 device ID"] ["location"]
509 strlcpy(temp
, line
, sizeof(temp
));
517 for (ptr
= temp
; *ptr
; ptr
++)
518 if (isspace(*ptr
& 255))
521 while (isspace(*ptr
& 255))
531 for (uri
= ptr
; *ptr
; ptr
++)
532 if (isspace(*ptr
& 255))
535 while (isspace(*ptr
& 255))
539 * device-make-and-model
545 for (ptr
++, make_model
= ptr
; *ptr
&& *ptr
!= '\"'; ptr
++)
547 if (*ptr
== '\\' && ptr
[1])
548 _cups_strcpy(ptr
, ptr
+ 1);
554 for (*ptr
++ = '\0'; isspace(*ptr
& 255); *ptr
++ = '\0');
563 for (ptr
++, info
= ptr
; *ptr
&& *ptr
!= '\"'; ptr
++)
565 if (*ptr
== '\\' && ptr
[1])
566 _cups_strcpy(ptr
, ptr
+ 1);
572 for (*ptr
++ = '\0'; isspace(*ptr
& 255); *ptr
++ = '\0');
580 for (ptr
++, device_id
= ptr
; *ptr
&& *ptr
!= '\"'; ptr
++)
582 if (*ptr
== '\\' && ptr
[1])
583 _cups_strcpy(ptr
, ptr
+ 1);
589 for (*ptr
++ = '\0'; isspace(*ptr
& 255); *ptr
++ = '\0');
597 for (ptr
++, location
= ptr
; *ptr
&& *ptr
!= '\"'; ptr
++)
599 if (*ptr
== '\\' && ptr
[1])
600 _cups_strcpy(ptr
, ptr
+ 1);
618 * Add the device to the array of available devices...
621 if (!add_device(dclass
, make_model
, info
, uri
, device_id
, location
))
622 fprintf(stderr
, "DEBUG: [cups-deviced] Found device \"%s\"...\n", uri
);
631 cupsFileClose(backend
->pipe
);
632 backend
->pipe
= NULL
;
637 * Bad format; strip trailing newline and write an error message.
642 if (line
[strlen(line
) - 1] == '\n')
643 line
[strlen(line
) - 1] = '\0';
645 fprintf(stderr
, "ERROR: [cups-deviced] Bad line from \"%s\": %s\n",
646 backend
->name
, line
);
652 * 'process_children()' - Process all dead children...
656 process_children(void)
658 int i
; /* Looping var */
659 int status
; /* Exit status of child */
660 int pid
; /* Process ID of child */
661 cupsd_backend_t
*backend
; /* Current backend */
662 const char *name
; /* Name of process */
666 * Reset the dead_children flag...
672 * Collect the exit status of some children...
676 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0)
677 #elif defined(HAVE_WAIT3)
678 while ((pid
= wait3(&status
, WNOHANG
, NULL
)) > 0)
680 if ((pid
= wait(&status
)) > 0)
681 #endif /* HAVE_WAITPID */
683 if (status
== SIGTERM
)
686 for (i
= num_backends
, backend
= backends
; i
> 0; i
--, backend
++)
687 if (backend
->pid
== pid
)
692 name
= backend
->name
;
694 backend
->status
= status
;
703 if (WIFEXITED(status
))
705 "ERROR: [cups-deviced] PID %d (%s) stopped with status %d!\n",
706 pid
, name
, WEXITSTATUS(status
));
709 "ERROR: [cups-deviced] PID %d (%s) crashed on signal %d!\n",
710 pid
, name
, WTERMSIG(status
));
714 "DEBUG: [cups-deviced] PID %d (%s) exited with no errors.\n",
721 * 'sigchld_handler()' - Handle 'child' signals from old processes.
725 sigchld_handler(int sig
) /* I - Signal number */
730 * Flag that we have dead children...
736 * Reset the signal handler as needed...
739 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
740 signal(SIGCLD
, sigchld_handler
);
741 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
746 * 'start_backend()' - Run a backend to gather the available devices.
749 static int /* O - 0 on success, -1 on error */
750 start_backend(const char *name
, /* I - Backend to run */
751 int root
) /* I - Run as root? */
753 const char *server_bin
; /* CUPS_SERVERBIN environment variable */
754 char program
[1024]; /* Full path to backend */
755 cupsd_backend_t
*backend
; /* Current backend */
756 char *argv
[2]; /* Command-line arguments */
759 if (num_backends
>= MAX_BACKENDS
)
761 fprintf(stderr
, "ERROR: Too many backends (%d)!\n", num_backends
);
765 if ((server_bin
= getenv("CUPS_SERVERBIN")) == NULL
)
766 server_bin
= CUPS_SERVERBIN
;
768 snprintf(program
, sizeof(program
), "%s/backend/%s", server_bin
, name
);
770 if (_cupsFileCheck(program
, _CUPS_FILE_CHECK_PROGRAM
, !geteuid(),
771 _cupsFileCheckFilter
, NULL
))
774 backend
= backends
+ num_backends
;
776 argv
[0] = (char *)name
;
779 if ((backend
->pipe
= cupsdPipeCommand(&(backend
->pid
), program
, argv
,
780 root
? 0 : normal_user
)) == NULL
)
782 fprintf(stderr
, "ERROR: [cups-deviced] Unable to execute \"%s\" - %s\n",
783 program
, strerror(errno
));
788 * Fill in the rest of the backend information...
791 fprintf(stderr
, "DEBUG: [cups-deviced] Started backend %s (PID %d)\n",
792 program
, backend
->pid
);
794 backend_fds
[num_backends
].fd
= cupsFileNumber(backend
->pipe
);
795 backend_fds
[num_backends
].events
= POLLIN
;
797 backend
->name
= strdup(name
);