#include "cupsd.h"
typedef struct
{
char device_class[128],
device_make_and_model[128],
device_info[128],
device_uri[1024];
} dev_info_t;
static int num_devs,
alloc_devs;
static dev_info_t *devs;
static int compare_devs(const dev_info_t *p0, const dev_info_t *p1);
static void sigalrm_handler(int sig);
void
LoadDevices(const char *d,
int exec_backends)
{
int i;
int count;
int compat;
FILE *fp;
DIR *dir;
DIRENT *dent;
char filename[1024],
line[2048],
dclass[64],
uri[1024],
info[128],
make_model[256];
dev_info_t *dev;
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action;
#endif
if (BackendsExeced)
return;
if (exec_backends)
BackendsExeced = 1;
if (Devices)
ippDelete(Devices);
Devices = ippNew();
if ((dir = opendir(d)) == NULL)
{
LogMessage(L_ERROR, "LoadDevices: Unable to open backend directory \"%s\": %s",
d, strerror(errno));
return;
}
alloc_devs = 0;
num_devs = 0;
devs = (dev_info_t *)0;
IgnoreChildSignals();
while ((dent = readdir(dir)) != NULL)
{
if (dent->d_name[0] == '.')
continue;
count = 0;
compat = strcmp(dent->d_name, "smb") == 0;
if (exec_backends)
{
snprintf(filename, sizeof(filename), "%s/%s", d, dent->d_name);
if ((fp = popen(filename, "r")) != NULL)
{
#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(30);
while (fgets(line, sizeof(line), fp) != NULL)
{
alarm(30);
if (strncasecmp(line, "Usage", 5) == 0)
compat = 1;
else if (sscanf(line, "%63s%1023s%*[ \t]\"%127[^\"]\"%*[ \t]\"%255[^\"]",
dclass, uri, make_model, info) != 4)
{
if (line[strlen(line) - 1] == '\n')
line[strlen(line) - 1] = '\0';
LogMessage(L_ERROR, "LoadDevices: Bad line from \"%s\": %s",
dent->d_name, line);
compat = 1;
break;
}
else
{
if (num_devs >= alloc_devs)
{
if (alloc_devs == 0)
dev = malloc(sizeof(dev_info_t) * 16);
else
dev = realloc(devs, sizeof(dev_info_t) * (alloc_devs + 16));
if (dev == NULL)
{
LogMessage(L_ERROR, "LoadDevices: Ran out of memory for %d devices!",
alloc_devs + 16);
closedir(dir);
return;
}
devs = dev;
alloc_devs += 16;
}
dev = devs + num_devs;
num_devs ++;
memset(dev, 0, sizeof(dev_info_t));
strlcpy(dev->device_class, dclass, sizeof(dev->device_class));
strlcpy(dev->device_info, info, sizeof(dev->device_info));
strlcpy(dev->device_make_and_model, make_model,
sizeof(dev->device_make_and_model));
strlcpy(dev->device_uri, uri, sizeof(dev->device_uri));
LogMessage(L_DEBUG, "LoadDevices: Added device \"%s\"...", uri);
count ++;
}
}
alarm(0);
pclose(fp);
}
else
LogMessage(L_WARN, "LoadDevices: Unable to execute \"%s\" backend: %s",
dent->d_name, strerror(errno));
}
if (!exec_backends || (count == 0 && compat))
{
if (num_devs >= alloc_devs)
{
if (alloc_devs == 0)
dev = malloc(sizeof(dev_info_t) * 16);
else
dev = realloc(devs, sizeof(dev_info_t) * (alloc_devs + 16));
if (dev == NULL)
{
LogMessage(L_ERROR, "LoadDevices: Ran out of memory for %d devices!",
alloc_devs + 16);
closedir(dir);
return;
}
devs = dev;
alloc_devs += 16;
}
dev = devs + num_devs;
num_devs ++;
memset(dev, 0, sizeof(dev_info_t));
strcpy(dev->device_class, "network");
snprintf(dev->device_info, sizeof(dev->device_info),
"Unknown Network Device (%s)", dent->d_name);
strcpy(dev->device_make_and_model, "Unknown");
strlcpy(dev->device_uri, dent->d_name, sizeof(dev->device_uri));
LogMessage(L_DEBUG, "LoadDevices: Compatibility device \"%s\"...",
dent->d_name);
}
}
closedir(dir);
CatchChildSignals();
if (num_devs > 1)
qsort(devs, num_devs, sizeof(dev_info_t),
(int (*)(const void *, const void *))compare_devs);
for (i = num_devs, dev = devs; i > 0; i --, dev ++)
{
if (i < num_devs)
ippAddSeparator(Devices);
ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
"device-class", NULL, dev->device_class);
ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_TEXT,
"device-info", NULL, dev->device_info);
ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_TEXT,
"device-make-and-model", NULL, dev->device_make_and_model);
ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_URI,
"device-uri", NULL, dev->device_uri);
}
if (alloc_devs)
free(devs);
}
static int
compare_devs(const dev_info_t *d0,
const dev_info_t *d1)
{
const char *s,
*t;
int diff,
digits;
s = d0->device_info;
t = d1->device_info;
while (*s && *t)
{
if (isdigit(*s & 255) && isdigit(*t & 255))
{
while (*s == '0')
s ++;
while (*t == '0')
t ++;
while (isdigit(*s & 255) && *s == *t)
{
s ++;
t ++;
}
if (isdigit(*s & 255) && !isdigit(*t & 255))
return (1);
else if (!isdigit(*s & 255) && isdigit(*t & 255))
return (-1);
else if (!isdigit(*s & 255) || !isdigit(*t & 255))
continue;
if (*s < *t)
diff = -1;
else
diff = 1;
digits = 0;
s ++;
t ++;
while (isdigit(*s & 255))
{
digits ++;
s ++;
}
while (isdigit(*t & 255))
{
digits --;
t ++;
}
if (digits < 0)
return (-1);
else if (digits > 0)
return (1);
else if (diff)
return (diff);
}
else if (tolower(*s) < tolower(*t))
return (-1);
else if (tolower(*s) > tolower(*t))
return (1);
else
{
s ++;
t ++;
}
}
if (*s)
return (1);
else if (*t)
return (-1);
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;
LogMessage(L_WARN, "LoadDevices: Backend did not respond within 30 seconds!");
}