#include "util.h"
#include <cups/array.h>
#include <cups/dir.h>
#include <fcntl.h>
typedef struct
{
char device_class[128],
device_make_and_model[128],
device_info[128],
device_uri[1024],
device_id[1024];
} dev_info_t;
static int alarm_tripped;
static cups_array_t *devs;
static int normal_user;
static dev_info_t *add_dev(const char *device_class,
const char *device_make_and_model,
const char *device_info,
const char *device_uri,
const char *device_id);
static int compare_devs(dev_info_t *p0, dev_info_t *p1);
static void sigalrm_handler(int sig);
int
main(int argc,
char *argv[])
{
const char *server_bin;
char backends[1024];
int request_id;
int count;
int compat;
char *backend_argv[2];
cups_file_t *fp;
int pid;
cups_dir_t *dir;
cups_dentry_t *dent;
char filename[1024],
line[2048],
dclass[64],
uri[1024],
info[128],
make_model[256],
device_id[1024];
int num_options;
cups_option_t *options;
const char *requested;
int send_class,
send_info,
send_make_and_model,
send_uri,
send_id;
dev_info_t *dev;
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action;
#endif
setbuf(stderr, NULL);
if (argc > 1)
request_id = atoi(argv[1]);
else
request_id = 1;
if (argc != 5)
{
fputs("Usage: cups-deviced request-id limit user-id options\n", stderr);
return (1);
}
if (request_id < 1)
{
fprintf(stderr, "cups-deviced: Bad request ID %d!\n", request_id);
return (1);
}
normal_user = atoi(argv[3]);
if (normal_user <= 0)
{
fprintf(stderr, "cups-deviced: Bad user %d!\n", normal_user);
return (1);
}
num_options = cupsParseOptions(argv[4], 0, &options);
requested = cupsGetOption("requested-attributes", num_options, options);
if (!requested || strstr(requested, "all"))
{
send_class = 1;
send_info = 1;
send_make_and_model = 1;
send_uri = 1;
send_id = 1;
}
else
{
send_class = strstr(requested, "device-class") != NULL;
send_info = strstr(requested, "device-info") != NULL;
send_make_and_model = strstr(requested, "device-make-and-model") != NULL;
send_uri = strstr(requested, "device-uri") != NULL;
send_id = strstr(requested, "device-id") != NULL;
}
if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
server_bin = CUPS_SERVERBIN;
snprintf(backends, sizeof(backends), "%s/backend", server_bin);
if ((dir = cupsDirOpen(backends)) == NULL)
{
fprintf(stderr, "ERROR: [cups-deviced] Unable to open backend directory "
"\"%s\": %s", backends, strerror(errno));
return (1);
}
devs = cupsArrayNew((cups_array_func_t)compare_devs, NULL);
while ((dent = cupsDirRead(dir)) != NULL)
{
if (!S_ISREG(dent->fileinfo.st_mode) ||
(dent->fileinfo.st_mode & (S_IRUSR | S_IXUSR)) != (S_IRUSR | S_IXUSR))
continue;
snprintf(filename, sizeof(filename), "%s/%s", backends, dent->filename);
backend_argv[0] = dent->filename;
backend_argv[1] = NULL;
fp = cupsdPipeCommand(&pid, filename, backend_argv,
(dent->fileinfo.st_mode & (S_IRWXG | S_IRWXO))
? normal_user : 0);
if (fp)
{
#ifdef HAVE_SIGSET
sigset(SIGALRM, sigalrm_handler);
#elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, SIGALRM);
action.sa_handler = sigalrm_handler;
sigaction(SIGALRM, &action, NULL);
#else
signal(SIGALRM, sigalrm_handler);
#endif
alarm_tripped = 0;
count = 0;
compat = !strcmp(dent->filename, "smb");
alarm(30);
while (cupsFileGets(fp, line, sizeof(line)))
{
alarm(30);
device_id[0] = '\0';
if (!strncasecmp(line, "Usage", 5))
compat = 1;
else if (sscanf(line,
"%63s%1023s%*[ \t]\"%255[^\"]\"%*[ \t]\"%127[^\"]\""
"%*[ \t]\"%1023[^\"]",
dclass, uri, make_model, info, device_id) < 4)
{
if (line[strlen(line) - 1] == '\n')
line[strlen(line) - 1] = '\0';
fprintf(stderr, "ERROR: [cups-deviced] Bad line from \"%s\": %s\n",
dent->filename, line);
compat = 1;
break;
}
else
{
dev = add_dev(dclass, make_model, info, uri, device_id);
if (!dev)
{
cupsDirClose(dir);
cupsFileClose(fp);
kill(pid, SIGTERM);
return (1);
}
fprintf(stderr, "DEBUG: [cups-deviced] Added device \"%s\"...\n",
uri);
count ++;
}
}
alarm(0);
if (alarm_tripped)
fprintf(stderr, "WARNING: [cups-deviced] Backend \"%s\" did not "
"respond within 30 seconds!\n", dent->filename);
cupsFileClose(fp);
kill(pid, SIGTERM);
if (count == 0 && compat)
{
snprintf(line, sizeof(line), "Unknown Network Device (%s)",
dent->filename);
dev = add_dev("network", line, "Unknown", dent->filename, "");
if (!dev)
{
cupsDirClose(dir);
return (1);
}
fprintf(stderr, "DEBUG: [cups-deviced] Compatibility device "
"\"%s\"...\n", dent->filename);
}
}
else
fprintf(stderr, "WARNING: [cups-deviced] Unable to execute \"%s\" "
"backend: %s\n", dent->filename, strerror(errno));
}
cupsDirClose(dir);
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");
if ((count = atoi(argv[2])) <= 0)
count = cupsArrayCount(devs);
if (count > cupsArrayCount(devs))
count = cupsArrayCount(devs);
for (dev = (dev_info_t *)cupsArrayFirst(devs);
count > 0;
count --, dev = (dev_info_t *)cupsArrayNext(devs))
{
cupsdSendIPPGroup(IPP_TAG_PRINTER);
if (send_class)
cupsdSendIPPString(IPP_TAG_KEYWORD, "device-class", dev->device_class);
if (send_info)
cupsdSendIPPString(IPP_TAG_TEXT, "device-info", dev->device_info);
if (send_make_and_model)
cupsdSendIPPString(IPP_TAG_TEXT, "device-make-and-model",
dev->device_make_and_model);
if (send_uri)
cupsdSendIPPString(IPP_TAG_URI, "device-uri", dev->device_uri);
if (send_id)
cupsdSendIPPString(IPP_TAG_TEXT, "device-id", dev->device_id);
}
cupsdSendIPPTrailer();
for (dev = (dev_info_t *)cupsArrayFirst(devs);
dev;
dev = (dev_info_t *)cupsArrayNext(devs))
free(dev);
cupsArrayDelete(devs);
return (0);
}
static dev_info_t *
add_dev(
const char *device_class,
const char *device_make_and_model,
const char *device_info,
const char *device_uri,
const char *device_id)
{
dev_info_t *dev,
*temp;
if ((dev = calloc(1, sizeof(dev_info_t))) == NULL)
{
fputs("ERROR: [cups-deviced] Ran out of memory allocating a device!\n",
stderr);
return (NULL);
}
strlcpy(dev->device_class, device_class, sizeof(dev->device_class));
strlcpy(dev->device_make_and_model, device_make_and_model,
sizeof(dev->device_make_and_model));
strlcpy(dev->device_info, device_info, sizeof(dev->device_info));
strlcpy(dev->device_uri, device_uri, sizeof(dev->device_uri));
strlcpy(dev->device_id, device_id, sizeof(dev->device_id));
if ((temp = cupsArrayFind(devs, dev)) != NULL)
{
free(dev);
dev = temp;
}
else
cupsArrayAdd(devs, dev);
return (dev);
}
static int
compare_devs(dev_info_t *d0,
dev_info_t *d1)
{
int diff;
if ((diff = cupsdCompareNames(d0->device_info, d1->device_info)) != 0)
return (diff);
else if ((diff = strcasecmp(d0->device_class, d1->device_class)) != 0)
return (diff);
else
return (strcasecmp(d0->device_uri, d1->device_uri));
}
static void
sigalrm_handler(int sig)
{
(void)sig;
alarm_tripped = 1;
}