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 = 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_IRWXG | 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, 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 (bpipe->ptr && memchr(bpipe->ptr, '\n', bpipe->end - bpipe->ptr)); } } /* * 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); }
int /* O - Exit code */ main(int argc, /* I - Number of command-line args */ char *argv[]) /* I - Command-line arguments */ { const char *server_bin; /* CUPS_SERVERBIN environment variable */ char backends[1024]; /* Location of backends */ int request_id; /* Request ID */ int count; /* Number of devices from backend */ int compat; /* Compatibility device? */ char *backend_argv[2]; /* Arguments for backend */ cups_file_t *fp; /* Pipe to device backend */ int pid; /* Process ID of backend */ cups_dir_t *dir; /* Directory pointer */ cups_dentry_t *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 */ device_id[1024]; /* 1284 device ID */ int num_options; /* Number of options */ cups_option_t *options; /* Options */ const char *requested; /* requested-attributes option */ 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? */ 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 */ setbuf(stderr, NULL); /* * Check the command-line... */ 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; } /* * Try opening the backend directory... */ 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); } /* * Setup the devices array... */ devs = cupsArrayNew((cups_array_func_t)compare_devs, 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) || (dent->fileinfo.st_mode & (S_IRUSR | S_IXUSR)) != (S_IRUSR | S_IXUSR)) continue; /* * Change effective users depending on the backend permissions... */ snprintf(filename, sizeof(filename), "%s/%s", backends, dent->filename); /* * Backends without permissions for normal users run as root, * all others run as the unprivileged user... */ 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); /* * Collect the output from the backend... */ if (fp) { /* * 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_tripped = 0; count = 0; compat = !strcmp(dent->filename, "smb"); alarm(30); while (cupsFileGets(fp, line, sizeof(line))) { /* * Reset the alarm clock... */ alarm(30); /* * Each line is of the form: * * class URI "make model" "name" ["1284 device ID"] */ 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) { /* * Bad format; strip trailing newline and write an error message. */ 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 { /* * Add the device to the array of available devices... */ 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 ++; } } /* * Turn the alarm clock off and close the pipe to the command... */ 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); /* * Hack for backends that don't support the CUPS 1.1 calling convention: * add a network device with the method == backend name. */ 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); /* * Output the list of devices... */ 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)) { /* * Add strings to attributes... */ 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(); /* * Free the devices array and return... */ for (dev = (dev_info_t *)cupsArrayFirst(devs); dev; dev = (dev_info_t *)cupsArrayNext(devs)) free(dev); cupsArrayDelete(devs); return (0); }