|
|
/*
|
|
|
* Device scanning mini-daemon for CUPS.
|
|
|
*
|
|
|
* Copyright © 2007-2018 by Apple Inc.
|
|
|
* Copyright © 1997-2006 by Easy Software Products.
|
|
|
*
|
|
|
* Licensed under Apache License v2.0. See the file "LICENSE" for more
|
|
|
* information.
|
|
|
*/
|
|
|
|
|
|
/*
|
|
|
* Include necessary headers...
|
|
|
*/
|
|
|
|
|
|
#include "util.h"
|
|
|
#include <cups/array.h>
|
|
|
#include <cups/dir.h>
|
|
|
#include <fcntl.h>
|
|
|
#include <sys/wait.h>
|
|
|
#include <poll.h>
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Constants...
|
|
|
*/
|
|
|
|
|
|
#define MAX_BACKENDS 200 /* Maximum number of backends we'll run */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Backend information...
|
|
|
*/
|
|
|
|
|
|
typedef struct
|
|
|
{
|
|
|
char *name; /* Name of backend */
|
|
|
int pid, /* Process ID */
|
|
|
status; /* Exit status */
|
|
|
cups_file_t *pipe; /* Pipe from backend stdout */
|
|
|
int count; /* Number of devices found */
|
|
|
} cupsd_backend_t;
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Device information structure...
|
|
|
*/
|
|
|
|
|
|
typedef struct
|
|
|
{
|
|
|
char device_class[128], /* Device class */
|
|
|
device_info[128], /* Device info/description */
|
|
|
device_uri[1024]; /* Device URI */
|
|
|
} cupsd_device_t;
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Local globals...
|
|
|
*/
|
|
|
|
|
|
static int num_backends = 0,
|
|
|
/* Total backends */
|
|
|
active_backends = 0;
|
|
|
/* Active backends */
|
|
|
static cupsd_backend_t backends[MAX_BACKENDS];
|
|
|
/* Array of backends */
|
|
|
static struct pollfd backend_fds[MAX_BACKENDS];
|
|
|
/* Array for poll() */
|
|
|
static cups_array_t *devices; /* Array of devices */
|
|
|
static uid_t normal_user; /* Normal user ID */
|
|
|
static int device_limit; /* Maximum number of devices */
|
|
|
static int send_class, /* Send device-class attribute? */
|
|
|
send_info, /* Send device-info attribute? */
|
|
|
send_make_and_model,
|
|
|
/* Send device-make-and-model attribute? */
|
|
|
send_uri, /* Send device-uri attribute? */
|
|
|
send_id, /* Send device-id attribute? */
|
|
|
send_location; /* Send device-location attribute? */
|
|
|
static int dead_children = 0;
|
|
|
/* Dead children? */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Local functions...
|
|
|
*/
|
|
|
|
|
|
static int add_device(const char *device_class,
|
|
|
const char *device_make_and_model,
|
|
|
const char *device_info,
|
|
|
const char *device_uri,
|
|
|
const char *device_id,
|
|
|
const char *device_location);
|
|
|
static int compare_devices(cupsd_device_t *p0,
|
|
|
cupsd_device_t *p1);
|
|
|
static double get_current_time(void);
|
|
|
static int get_device(cupsd_backend_t *backend);
|
|
|
static void process_children(void);
|
|
|
static void sigchld_handler(int sig);
|
|
|
static int start_backend(const char *backend, int root);
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'main()' - Scan for devices and return an IPP response.
|
|
|
*
|
|
|
* Usage:
|
|
|
*
|
|
|
* cups-deviced request_id limit options
|
|
|
*/
|
|
|
|
|
|
int /* O - Exit code */
|
|
|
main(int argc, /* I - Number of command-line args */
|
|
|
char *argv[]) /* I - Command-line arguments */
|
|
|
{
|
|
|
int i; /* Looping var */
|
|
|
int request_id; /* Request ID */
|
|
|
int timeout; /* Timeout in seconds */
|
|
|
const char *server_bin; /* CUPS_SERVERBIN environment variable */
|
|
|
char filename[1024]; /* Backend directory filename */
|
|
|
cups_dir_t *dir; /* Directory pointer */
|
|
|
cups_dentry_t *dent; /* Directory entry */
|
|
|
double current_time, /* Current time */
|
|
|
end_time; /* Ending time */
|
|
|
int num_options; /* Number of options */
|
|
|
cups_option_t *options; /* Options */
|
|
|
cups_array_t *requested, /* requested-attributes values */
|
|
|
*exclude, /* exclude-schemes values */
|
|
|
*include; /* include-schemes values */
|
|
|
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
|
|
|
struct sigaction action; /* Actions for POSIX signals */
|
|
|
#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
|
|
|
|
|
|
|
|
|
setbuf(stderr, NULL);
|
|
|
|
|
|
/*
|
|
|
* Check the command-line...
|
|
|
*/
|
|
|
|
|
|
if (argc != 6)
|
|
|
{
|
|
|
fputs("Usage: cups-deviced request-id limit timeout user-id options\n", stderr);
|
|
|
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
request_id = atoi(argv[1]);
|
|
|
if (request_id < 1)
|
|
|
{
|
|
|
fprintf(stderr, "ERROR: [cups-deviced] Bad request ID %d!\n", request_id);
|
|
|
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
device_limit = atoi(argv[2]);
|
|
|
if (device_limit < 0)
|
|
|
{
|
|
|
fprintf(stderr, "ERROR: [cups-deviced] Bad limit %d!\n", device_limit);
|
|
|
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
timeout = atoi(argv[3]);
|
|
|
if (timeout < 1)
|
|
|
{
|
|
|
fprintf(stderr, "ERROR: [cups-deviced] Bad timeout %d!\n", timeout);
|
|
|
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
normal_user = (uid_t)atoi(argv[4]);
|
|
|
if (normal_user <= 0)
|
|
|
{
|
|
|
fprintf(stderr, "ERROR: [cups-deviced] Bad user %d!\n", normal_user);
|
|
|
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
num_options = cupsParseOptions(argv[5], 0, &options);
|
|
|
requested = cupsdCreateStringsArray(cupsGetOption("requested-attributes",
|
|
|
num_options, options));
|
|
|
exclude = cupsdCreateStringsArray(cupsGetOption("exclude-schemes",
|
|
|
num_options, options));
|
|
|
include = cupsdCreateStringsArray(cupsGetOption("include-schemes",
|
|
|
num_options, options));
|
|
|
|
|
|
if (!requested || cupsArrayFind(requested, "all") != NULL)
|
|
|
{
|
|
|
send_class = send_info = send_make_and_model = send_uri = send_id =
|
|
|
send_location = 1;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
send_class = cupsArrayFind(requested, "device-class") != NULL;
|
|
|
send_info = cupsArrayFind(requested, "device-info") != NULL;
|
|
|
send_make_and_model = cupsArrayFind(requested, "device-make-and-model") != NULL;
|
|
|
send_uri = cupsArrayFind(requested, "device-uri") != NULL;
|
|
|
send_id = cupsArrayFind(requested, "device-id") != NULL;
|
|
|
send_location = cupsArrayFind(requested, "device-location") != NULL;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Listen to child signals...
|
|
|
*/
|
|
|
|
|
|
#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
|
|
|
sigset(SIGCHLD, sigchld_handler);
|
|
|
#elif defined(HAVE_SIGACTION)
|
|
|
memset(&action, 0, sizeof(action));
|
|
|
|
|
|
sigemptyset(&action.sa_mask);
|
|
|
sigaddset(&action.sa_mask, SIGCHLD);
|
|
|
action.sa_handler = sigchld_handler;
|
|
|
sigaction(SIGCHLD, &action, NULL);
|
|
|
#else
|
|
|
signal(SIGCLD, sigchld_handler); /* No, SIGCLD isn't a typo... */
|
|
|
#endif /* HAVE_SIGSET */
|
|
|
|
|
|
/*
|
|
|
* Try opening the backend directory...
|
|
|
*/
|
|
|
|
|
|
if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
|
|
|
server_bin = CUPS_SERVERBIN;
|
|
|
|
|
|
snprintf(filename, sizeof(filename), "%s/backend", server_bin);
|
|
|
|
|
|
if ((dir = cupsDirOpen(filename)) == NULL)
|
|
|
{
|
|
|
fprintf(stderr, "ERROR: [cups-deviced] Unable to open backend directory "
|
|
|
"\"%s\": %s", filename, strerror(errno));
|
|
|
|
|
|
return (1);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Setup the devices array...
|
|
|
*/
|
|
|
|
|
|
devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL);
|
|
|
|
|
|
/*
|
|
|
* Loop through all of the device backends...
|
|
|
*/
|
|
|
|
|
|
while ((dent = cupsDirRead(dir)) != NULL)
|
|
|
{
|
|
|
/*
|
|
|
* Skip entries that are not executable files...
|
|
|
*/
|
|
|
|
|
|
if (!S_ISREG(dent->fileinfo.st_mode) ||
|
|
|
!isalnum(dent->filename[0] & 255) ||
|
|
|
(dent->fileinfo.st_mode & (S_IRUSR | S_IXUSR)) != (S_IRUSR | S_IXUSR))
|
|
|
continue;
|
|
|
|
|
|
/*
|
|
|
* Skip excluded or not included backends...
|
|
|
*/
|
|
|
|
|
|
if (cupsArrayFind(exclude, dent->filename) ||
|
|
|
(include && !cupsArrayFind(include, dent->filename)))
|
|
|
continue;
|
|
|
|
|
|
/*
|
|
|
* Backends without permissions for normal users run as root,
|
|
|
* all others run as the unprivileged user...
|
|
|
*/
|
|
|
|
|
|
start_backend(dent->filename, !(dent->fileinfo.st_mode & (S_IWGRP | S_IRWXO)));
|
|
|
}
|
|
|
|
|
|
cupsDirClose(dir);
|
|
|
|
|
|
/*
|
|
|
* Collect devices...
|
|
|
*/
|
|
|
|
|
|
if (getenv("SOFTWARE"))
|
|
|
puts("Content-Type: application/ipp\n");
|
|
|
|
|
|
cupsdSendIPPHeader(IPP_OK, request_id);
|
|
|
cupsdSendIPPGroup(IPP_TAG_OPERATION);
|
|
|
cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
|
|
|
cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
|
|
|
|
|
|
end_time = get_current_time() + timeout;
|
|
|
|
|
|
while (active_backends > 0 && (current_time = get_current_time()) < end_time)
|
|
|
{
|
|
|
/*
|
|
|
* Collect the output from the backends...
|
|
|
*/
|
|
|
|
|
|
timeout = (int)(1000 * (end_time - current_time));
|
|
|
|
|
|
if (poll(backend_fds, (nfds_t)num_backends, timeout) > 0)
|
|
|
{
|
|
|
for (i = 0; i < num_backends; i ++)
|
|
|
if (backend_fds[i].revents && backends[i].pipe)
|
|
|
{
|
|
|
cups_file_t *bpipe = backends[i].pipe;
|
|
|
/* Copy of pipe for backend... */
|
|
|
|
|
|
do
|
|
|
{
|
|
|
if (get_device(backends + i))
|
|
|
{
|
|
|
backend_fds[i].fd = 0;
|
|
|
backend_fds[i].events = 0;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
while (_cupsFilePeekAhead(bpipe, '\n'));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Get exit status from children...
|
|
|
*/
|
|
|
|
|
|
if (dead_children)
|
|
|
process_children();
|
|
|
}
|
|
|
|
|
|
cupsdSendIPPTrailer();
|
|
|
|
|
|
/*
|
|
|
* Terminate any remaining backends and exit...
|
|
|
*/
|
|
|
|
|
|
if (active_backends > 0)
|
|
|
{
|
|
|
for (i = 0; i < num_backends; i ++)
|
|
|
if (backends[i].pid)
|
|
|
kill(backends[i].pid, SIGTERM);
|
|
|
}
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'add_device()' - Add a new device to the list.
|
|
|
*/
|
|
|
|
|
|
static int /* O - 0 on success, -1 on error */
|
|
|
add_device(
|
|
|
const char *device_class, /* I - Device class */
|
|
|
const char *device_make_and_model, /* I - Device make and model */
|
|
|
const char *device_info, /* I - Device information */
|
|
|
const char *device_uri, /* I - Device URI */
|
|
|
const char *device_id, /* I - 1284 device ID */
|
|
|
const char *device_location) /* I - Physical location */
|
|
|
{
|
|
|
cupsd_device_t *device; /* New device */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Allocate memory for the device record...
|
|
|
*/
|
|
|
|
|
|
if ((device = calloc(1, sizeof(cupsd_device_t))) == NULL)
|
|
|
{
|
|
|
fputs("ERROR: [cups-deviced] Ran out of memory allocating a device!\n",
|
|
|
stderr);
|
|
|
return (-1);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Copy the strings over...
|
|
|
*/
|
|
|
|
|
|
strlcpy(device->device_class, device_class, sizeof(device->device_class));
|
|
|
strlcpy(device->device_info, device_info, sizeof(device->device_info));
|
|
|
strlcpy(device->device_uri, device_uri, sizeof(device->device_uri));
|
|
|
|
|
|
/*
|
|
|
* Add the device to the array and return...
|
|
|
*/
|
|
|
|
|
|
if (cupsArrayFind(devices, device))
|
|
|
{
|
|
|
/*
|
|
|
* Avoid duplicates!
|
|
|
*/
|
|
|
|
|
|
free(device);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
cupsArrayAdd(devices, device);
|
|
|
|
|
|
if (device_limit <= 0 || cupsArrayCount(devices) < device_limit)
|
|
|
{
|
|
|
/*
|
|
|
* Send device info...
|
|
|
*/
|
|
|
|
|
|
cupsdSendIPPGroup(IPP_TAG_PRINTER);
|
|
|
if (send_class)
|
|
|
cupsdSendIPPString(IPP_TAG_KEYWORD, "device-class",
|
|
|
device_class);
|
|
|
if (send_info)
|
|
|
cupsdSendIPPString(IPP_TAG_TEXT, "device-info", device_info);
|
|
|
if (send_make_and_model)
|
|
|
cupsdSendIPPString(IPP_TAG_TEXT, "device-make-and-model",
|
|
|
device_make_and_model);
|
|
|
if (send_uri)
|
|
|
cupsdSendIPPString(IPP_TAG_URI, "device-uri", device_uri);
|
|
|
if (send_id)
|
|
|
cupsdSendIPPString(IPP_TAG_TEXT, "device-id",
|
|
|
device_id ? device_id : "");
|
|
|
if (send_location)
|
|
|
cupsdSendIPPString(IPP_TAG_TEXT, "device-location",
|
|
|
device_location ? device_location : "");
|
|
|
|
|
|
fflush(stdout);
|
|
|
fputs("DEBUG: Flushed attributes...\n", stderr);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'compare_devices()' - Compare device names to eliminate duplicates.
|
|
|
*/
|
|
|
|
|
|
static int /* O - Result of comparison */
|
|
|
compare_devices(cupsd_device_t *d0, /* I - First device */
|
|
|
cupsd_device_t *d1) /* I - Second device */
|
|
|
{
|
|
|
int diff; /* Difference between strings */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Sort devices by device-info, device-class, and device-uri...
|
|
|
*/
|
|
|
|
|
|
if ((diff = cupsdCompareNames(d0->device_info, d1->device_info)) != 0)
|
|
|
return (diff);
|
|
|
else if ((diff = _cups_strcasecmp(d0->device_class, d1->device_class)) != 0)
|
|
|
return (diff);
|
|
|
else
|
|
|
return (_cups_strcasecmp(d0->device_uri, d1->device_uri));
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'get_current_time()' - Get the current time as a double value in seconds.
|
|
|
*/
|
|
|
|
|
|
static double /* O - Time in seconds */
|
|
|
get_current_time(void)
|
|
|
{
|
|
|
struct timeval curtime; /* Current time */
|
|
|
|
|
|
|
|
|
gettimeofday(&curtime, NULL);
|
|
|
|
|
|
return (curtime.tv_sec + 0.000001 * curtime.tv_usec);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'get_device()' - Get a device from a backend.
|
|
|
*/
|
|
|
|
|
|
static int /* O - 0 on success, -1 on error */
|
|
|
get_device(cupsd_backend_t *backend) /* I - Backend to read from */
|
|
|
{
|
|
|
char line[2048], /* Line from backend */
|
|
|
temp[2048], /* Copy of line */
|
|
|
*ptr, /* Pointer into line */
|
|
|
*dclass, /* Device class */
|
|
|
*uri, /* Device URI */
|
|
|
*make_model, /* Make and model */
|
|
|
*info, /* Device info */
|
|
|
*device_id, /* 1284 device ID */
|
|
|
*location; /* Physical location */
|
|
|
|
|
|
|
|
|
if (cupsFileGets(backend->pipe, line, sizeof(line)))
|
|
|
{
|
|
|
/*
|
|
|
* Each line is of the form:
|
|
|
*
|
|
|
* class URI "make model" "name" ["1284 device ID"] ["location"]
|
|
|
*/
|
|
|
|
|
|
strlcpy(temp, line, sizeof(temp));
|
|
|
|
|
|
/*
|
|
|
* device-class
|
|
|
*/
|
|
|
|
|
|
dclass = temp;
|
|
|
|
|
|
for (ptr = temp; *ptr; ptr ++)
|
|
|
if (isspace(*ptr & 255))
|
|
|
break;
|
|
|
|
|
|
while (isspace(*ptr & 255))
|
|
|
*ptr++ = '\0';
|
|
|
|
|
|
/*
|
|
|
* device-uri
|
|
|
*/
|
|
|
|
|
|
if (!*ptr)
|
|
|
goto error;
|
|
|
|
|
|
for (uri = ptr; *ptr; ptr ++)
|
|
|
if (isspace(*ptr & 255))
|
|
|
break;
|
|
|
|
|
|
while (isspace(*ptr & 255))
|
|
|
*ptr++ = '\0';
|
|
|
|
|
|
/*
|
|
|
* device-make-and-model
|
|
|
*/
|
|
|
|
|
|
if (*ptr != '\"')
|
|
|
goto error;
|
|
|
|
|
|
for (ptr ++, make_model = ptr; *ptr && *ptr != '\"'; ptr ++)
|
|
|
{
|
|
|
if (*ptr == '\\' && ptr[1])
|
|
|
_cups_strcpy(ptr, ptr + 1);
|
|
|
}
|
|
|
|
|
|
if (*ptr != '\"')
|
|
|
goto error;
|
|
|
|
|
|
for (*ptr++ = '\0'; isspace(*ptr & 255); *ptr++ = '\0');
|
|
|
|
|
|
/*
|
|
|
* device-info
|
|
|
*/
|
|
|
|
|
|
if (*ptr != '\"')
|
|
|
goto error;
|
|
|
|
|
|
for (ptr ++, info = ptr; *ptr && *ptr != '\"'; ptr ++)
|
|
|
{
|
|
|
if (*ptr == '\\' && ptr[1])
|
|
|
_cups_strcpy(ptr, ptr + 1);
|
|
|
}
|
|
|
|
|
|
if (*ptr != '\"')
|
|
|
goto error;
|
|
|
|
|
|
for (*ptr++ = '\0'; isspace(*ptr & 255); *ptr++ = '\0');
|
|
|
|
|
|
/*
|
|
|
* device-id
|
|
|
*/
|
|
|
|
|
|
if (*ptr == '\"')
|
|
|
{
|
|
|
for (ptr ++, device_id = ptr; *ptr && *ptr != '\"'; ptr ++)
|
|
|
{
|
|
|
if (*ptr == '\\' && ptr[1])
|
|
|
_cups_strcpy(ptr, ptr + 1);
|
|
|
}
|
|
|
|
|
|
if (*ptr != '\"')
|
|
|
goto error;
|
|
|
|
|
|
for (*ptr++ = '\0'; isspace(*ptr & 255); *ptr++ = '\0');
|
|
|
|
|
|
/*
|
|
|
* device-location
|
|
|
*/
|
|
|
|
|
|
if (*ptr == '\"')
|
|
|
{
|
|
|
for (ptr ++, location = ptr; *ptr && *ptr != '\"'; ptr ++)
|
|
|
{
|
|
|
if (*ptr == '\\' && ptr[1])
|
|
|
_cups_strcpy(ptr, ptr + 1);
|
|
|
}
|
|
|
|
|
|
if (*ptr != '\"')
|
|
|
goto error;
|
|
|
|
|
|
*ptr = '\0';
|
|
|
}
|
|
|
else
|
|
|
location = NULL;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
device_id = NULL;
|
|
|
location = NULL;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Add the device to the array of available devices...
|
|
|
*/
|
|
|
|
|
|
if (!add_device(dclass, make_model, info, uri, device_id, location))
|
|
|
fprintf(stderr, "DEBUG: [cups-deviced] Found device \"%s\"...\n", uri);
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* End of file...
|
|
|
*/
|
|
|
|
|
|
cupsFileClose(backend->pipe);
|
|
|
backend->pipe = NULL;
|
|
|
|
|
|
return (-1);
|
|
|
|
|
|
/*
|
|
|
* Bad format; strip trailing newline and write an error message.
|
|
|
*/
|
|
|
|
|
|
error:
|
|
|
|
|
|
if (line[strlen(line) - 1] == '\n')
|
|
|
line[strlen(line) - 1] = '\0';
|
|
|
|
|
|
fprintf(stderr, "ERROR: [cups-deviced] Bad line from \"%s\": %s\n",
|
|
|
backend->name, line);
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'process_children()' - Process all dead children...
|
|
|
*/
|
|
|
|
|
|
static void
|
|
|
process_children(void)
|
|
|
{
|
|
|
int i; /* Looping var */
|
|
|
int status; /* Exit status of child */
|
|
|
int pid; /* Process ID of child */
|
|
|
cupsd_backend_t *backend; /* Current backend */
|
|
|
const char *name; /* Name of process */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Reset the dead_children flag...
|
|
|
*/
|
|
|
|
|
|
dead_children = 0;
|
|
|
|
|
|
/*
|
|
|
* Collect the exit status of some children...
|
|
|
*/
|
|
|
|
|
|
#ifdef HAVE_WAITPID
|
|
|
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
|
|
|
#elif defined(HAVE_WAIT3)
|
|
|
while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
|
|
|
#else
|
|
|
if ((pid = wait(&status)) > 0)
|
|
|
#endif /* HAVE_WAITPID */
|
|
|
{
|
|
|
if (status == SIGTERM)
|
|
|
status = 0;
|
|
|
|
|
|
for (i = num_backends, backend = backends; i > 0; i --, backend ++)
|
|
|
if (backend->pid == pid)
|
|
|
break;
|
|
|
|
|
|
if (i > 0)
|
|
|
{
|
|
|
name = backend->name;
|
|
|
backend->pid = 0;
|
|
|
backend->status = status;
|
|
|
|
|
|
active_backends --;
|
|
|
}
|
|
|
else
|
|
|
name = "Unknown";
|
|
|
|
|
|
if (status)
|
|
|
{
|
|
|
if (WIFEXITED(status))
|
|
|
fprintf(stderr,
|
|
|
"ERROR: [cups-deviced] PID %d (%s) stopped with status %d!\n",
|
|
|
pid, name, WEXITSTATUS(status));
|
|
|
else
|
|
|
fprintf(stderr,
|
|
|
"ERROR: [cups-deviced] PID %d (%s) crashed on signal %d!\n",
|
|
|
pid, name, WTERMSIG(status));
|
|
|
}
|
|
|
else
|
|
|
fprintf(stderr,
|
|
|
"DEBUG: [cups-deviced] PID %d (%s) exited with no errors.\n",
|
|
|
pid, name);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'sigchld_handler()' - Handle 'child' signals from old processes.
|
|
|
*/
|
|
|
|
|
|
static void
|
|
|
sigchld_handler(int sig) /* I - Signal number */
|
|
|
{
|
|
|
(void)sig;
|
|
|
|
|
|
/*
|
|
|
* Flag that we have dead children...
|
|
|
*/
|
|
|
|
|
|
dead_children = 1;
|
|
|
|
|
|
/*
|
|
|
* Reset the signal handler as needed...
|
|
|
*/
|
|
|
|
|
|
#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
|
|
|
signal(SIGCLD, sigchld_handler);
|
|
|
#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'start_backend()' - Run a backend to gather the available devices.
|
|
|
*/
|
|
|
|
|
|
static int /* O - 0 on success, -1 on error */
|
|
|
start_backend(const char *name, /* I - Backend to run */
|
|
|
int root) /* I - Run as root? */
|
|
|
{
|
|
|
const char *server_bin; /* CUPS_SERVERBIN environment variable */
|
|
|
char program[1024]; /* Full path to backend */
|
|
|
cupsd_backend_t *backend; /* Current backend */
|
|
|
char *argv[2]; /* Command-line arguments */
|
|
|
|
|
|
|
|
|
if (num_backends >= MAX_BACKENDS)
|
|
|
{
|
|
|
fprintf(stderr, "ERROR: Too many backends (%d)!\n", num_backends);
|
|
|
return (-1);
|
|
|
}
|
|
|
|
|
|
if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
|
|
|
server_bin = CUPS_SERVERBIN;
|
|
|
|
|
|
snprintf(program, sizeof(program), "%s/backend/%s", server_bin, name);
|
|
|
|
|
|
if (_cupsFileCheck(program, _CUPS_FILE_CHECK_PROGRAM, !geteuid(),
|
|
|
_cupsFileCheckFilter, NULL))
|
|
|
return (-1);
|
|
|
|
|
|
backend = backends + num_backends;
|
|
|
|
|
|
argv[0] = (char *)name;
|
|
|
argv[1] = NULL;
|
|
|
|
|
|
if ((backend->pipe = cupsdPipeCommand(&(backend->pid), program, argv,
|
|
|
root ? 0 : normal_user)) == NULL)
|
|
|
{
|
|
|
fprintf(stderr, "ERROR: [cups-deviced] Unable to execute \"%s\" - %s\n",
|
|
|
program, strerror(errno));
|
|
|
return (-1);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Fill in the rest of the backend information...
|
|
|
*/
|
|
|
|
|
|
fprintf(stderr, "DEBUG: [cups-deviced] Started backend %s (PID %d)\n",
|
|
|
program, backend->pid);
|
|
|
|
|
|
backend_fds[num_backends].fd = cupsFileNumber(backend->pipe);
|
|
|
backend_fds[num_backends].events = POLLIN;
|
|
|
|
|
|
backend->name = strdup(name);
|
|
|
backend->status = 0;
|
|
|
backend->count = 0;
|
|
|
|
|
|
active_backends ++;
|
|
|
num_backends ++;
|
|
|
|
|
|
return (0);
|
|
|
}
|