/* * "$Id: devices.c,v 1.4 2005/01/04 22:10:45 jlovell Exp $" * * Device scanning routines for the Common UNIX Printing System (CUPS). * * Copyright 1997-2005 by Easy Software Products. * * These coded instructions, statements, and computer programs are the * property of Easy Software Products and are protected by Federal * copyright law. Distribution and use rights are outlined in the file * "LICENSE.txt" which should have been included with this file. If this * file is missing or damaged please contact Easy Software Products * at: * * Attn: CUPS Licensing Information * Easy Software Products * 44141 Airport View Drive, Suite 204 * Hollywood, Maryland 20636 USA * * Voice: (301) 373-9600 * EMail: cups-info@cups.org * WWW: http://www.cups.org * * Contents: * * LoadDevices() - Load all available devices. * compare_devs() - Compare PPD file make and model names for sorting. * sigalrm_handler() - Handle alarm signals for backends that get hung */ /* * Include necessary headers... */ #include "cupsd.h" /* * Device information structure... */ typedef struct { char device_class[128], /* Device class */ device_make_and_model[128], /* Make and model, if known */ device_info[128], /* Device info/description */ device_uri[1024]; /* Device URI */ } dev_info_t; /* * Local globals... */ static int num_devs, /* Number of devices */ alloc_devs; /* Number of allocated entries */ static dev_info_t *devs; /* Device info */ /* * Local functions... */ static int compare_devs(const dev_info_t *p0, const dev_info_t *p1); static void sigalrm_handler(int sig); /* * 'LoadDevices()' - Load all available devices. */ void LoadDevices(const char *d, /* I - Directory to scan */ int exec_backends) /* I - Load backends? */ { int i; /* Looping var */ int count; /* Number of devices from backend */ int compat; /* Compatibility device? */ FILE *fp; /* Pipe to device backend */ DIR *dir; /* Directory pointer */ DIRENT *dent; /* Directory entry */ char filename[1024], /* Name of backend */ line[2048], /* Line from backend */ dclass[64], /* Device class */ uri[1024], /* Device URI */ info[128], /* Device info */ make_model[256];/* Make and model */ dev_info_t *dev; /* Current device */ #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) struct sigaction action; /* Actions for POSIX signals */ #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ /* * If we've already loaded the backends to discover devices don't * bother to do it again... */ if (BackendsExeced) return; if (exec_backends) BackendsExeced = 1; /* * Initialize the device list. */ if (Devices) ippDelete(Devices); Devices = ippNew(); /* * Try opening the backend directory... */ if ((dir = opendir(d)) == NULL) { LogMessage(L_ERROR, "LoadDevices: Unable to open backend directory \"%s\": %s", d, strerror(errno)); return; } /* * Setup the devices array... */ alloc_devs = 0; num_devs = 0; devs = (dev_info_t *)0; /* * Ignore child signals... */ IgnoreChildSignals(); /* * Loop through all of the device backends... */ while ((dent = readdir(dir)) != NULL) { /* * Skip "." and ".."... */ if (dent->d_name[0] == '.') continue; count = 0; compat = strcmp(dent->d_name, "smb") == 0; if (exec_backends) { /* * Run the backend with no arguments and collect the output... */ snprintf(filename, sizeof(filename), "%s/%s", d, dent->d_name); if ((fp = popen(filename, "r")) != NULL) { /* * Set an alarm for the first read from the backend; this avoids * problems when a backend is hung getting device information. */ #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */ 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 /* HAVE_SIGSET */ alarm(30); while (fgets(line, sizeof(line), fp) != NULL) { /* * Reset the alarm clock... */ alarm(30); /* * Each line is of the form: * * class URI "make model" "name" */ if (strncasecmp(line, "Usage", 5) == 0) compat = 1; else if (sscanf(line, "%63s%1023s%*[ \t]\"%127[^\"]\"%*[ \t]\"%255[^\"]", dclass, uri, make_model, info) != 4) { /* * Bad format; strip trailing newline and write an error message. */ 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 { /* * Add the device to the array of available devices... */ if (num_devs >= alloc_devs) { /* * Allocate (more) memory for the devices... */ 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 ++; } } /* * Turn the alarm clock off and close the pipe to the command... */ alarm(0); pclose(fp); } else LogMessage(L_WARN, "LoadDevices: Unable to execute \"%s\" backend: %s", dent->d_name, strerror(errno)); } /* * Hack for backends that don't support the CUPS 1.1 calling convention: * add a network device with the method == backend name. */ if (!exec_backends || (count == 0 && compat)) { if (num_devs >= alloc_devs) { /* * Allocate (more) memory for the devices... */ 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); /* * Catch child signals... */ CatchChildSignals(); /* * Sort the available devices... */ if (num_devs > 1) qsort(devs, num_devs, sizeof(dev_info_t), (int (*)(const void *, const void *))compare_devs); /* * Create the list of devices... */ for (i = num_devs, dev = devs; i > 0; i --, dev ++) { /* * Add strings to attributes... */ 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); } /* * Free the devices array... */ if (alloc_devs) free(devs); } /* * 'compare_devs()' - Compare device names for sorting. */ static int /* O - Result of comparison */ compare_devs(const dev_info_t *d0, /* I - First device */ const dev_info_t *d1) /* I - Second device */ { const char *s, /* First name */ *t; /* Second name */ int diff, /* Difference between digits */ digits; /* Number of digits */ /* * First compare names... */ s = d0->device_info; t = d1->device_info; /* * Loop through both nicknames, returning only when a difference is * seen. Also, compare whole numbers rather than just characters, too! */ while (*s && *t) { if (isdigit(*s & 255) && isdigit(*t & 255)) { /* * Got a number; start by skipping leading 0's... */ while (*s == '0') s ++; while (*t == '0') t ++; /* * Skip equal digits... */ while (isdigit(*s & 255) && *s == *t) { s ++; t ++; } /* * Bounce out if *s and *t aren't both digits... */ 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; /* * Figure out how many more digits there are... */ digits = 0; s ++; t ++; while (isdigit(*s & 255)) { digits ++; s ++; } while (isdigit(*t & 255)) { digits --; t ++; } /* * Return if the number or value of the digits is different... */ 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 ++; } } /* * Return the results of the final comparison... */ 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)); } /* * 'sigalrm_handler()' - Handle alarm signals for backends that get hung * trying to list the available devices... */ static void sigalrm_handler(int sig) /* I - Signal number */ { (void)sig; /* remove compiler warnings... */ LogMessage(L_WARN, "LoadDevices: Backend did not respond within 30 seconds!"); } /* * End of "$Id: devices.c,v 1.4 2005/01/04 22:10:45 jlovell Exp $". */