Index: scheduler/util.c =================================================================== --- scheduler/util.c (revision 7617) +++ scheduler/util.c (working copy) @@ -3,7 +3,7 @@ * * Mini-daemon utility functions for the Common UNIX Printing System (CUPS). * - * Copyright 2007 by Apple Inc. + * Copyright 2007-2008 by Apple Inc. * Copyright 1997-2005 by Easy Software Products. * * These coded instructions, statements, and computer programs are the @@ -15,6 +15,8 @@ * Contents: * * cupsdCompareNames() - Compare two names. + * cupsdExec() - Run a program with the correct environment. + * cupsdPipeCommand() - Read output from a command. * cupsdSendIPPGroup() - Send a group tag. * cupsdSendIPPHeader() - Send the IPP response header. * cupsdSendIPPInteger() - Send an integer attribute. @@ -27,6 +29,13 @@ */ #include "util.h" +#include +#include +#include +#ifdef __APPLE__ +# include +extern char **environ; +#endif /* __APPLE__ */ /* @@ -144,6 +153,174 @@ /* + * 'cupsdExec()' - Run a program with the correct environment. + * + * On Mac OS X, we need to update the CFProcessPath environment variable that + * is passed in the environment so the child can access its bundled resources. + */ + +int /* O - exec() status */ +cupsdExec(const char *command, /* I - Full path to program */ + char **argv) /* I - Command-line arguments */ +{ +#ifdef __APPLE__ + int i, j; /* Looping vars */ + char *envp[500], /* Array of environment variables */ + cfprocesspath[1024], /* CFProcessPath environment variable */ + linkpath[1024]; /* Link path for symlinks... */ + int linkbytes; /* Bytes for link path */ + + + /* + * Some Mac OS X programs are bundled and need the CFProcessPath environment + * variable defined. If the command is a symlink, resolve the link and point + * to the resolved location, otherwise, use the command path itself. + */ + + if ((linkbytes = readlink(command, linkpath, sizeof(linkpath) - 1)) > 0) + { + /* + * Yes, this is a symlink to the actual program, nul-terminate and + * use it... + */ + + linkpath[linkbytes] = '\0'; + + if (linkpath[0] == '/') + snprintf(cfprocesspath, sizeof(cfprocesspath), "CFProcessPath=%s", + linkpath); + else + snprintf(cfprocesspath, sizeof(cfprocesspath), "CFProcessPath=%s/%s", + dirname((char *)command), linkpath); + } + else + snprintf(cfprocesspath, sizeof(cfprocesspath), "CFProcessPath=%s", command); + + envp[0] = cfprocesspath; + + /* + * Copy the rest of the environment except for any CFProcessPath that may + * already be there... + */ + + for (i = 1, j = 0; + environ[j] && i < (int)(sizeof(envp) / sizeof(envp[0]) - 1); + j ++) + if (strncmp(environ[j], "CFProcessPath=", 14)) + envp[i ++] = environ[j]; + + envp[i] = NULL; + + /* + * Use execve() to run the program... + */ + + return (execve(command, argv, envp)); + +#else + /* + * On other operating systems, just call execv() to use the same environment + * variables as the parent... + */ + + return (execv(command, argv)); +#endif /* __APPLE__ */ +} + + +/* + * 'cupsdPipeCommand()' - Read output from a command. + */ + +cups_file_t * /* O - CUPS file or NULL on error */ +cupsdPipeCommand(int *pid, /* O - Process ID or 0 on error */ + const char *command, /* I - Command to run */ + char **argv, /* I - Arguments to pass to command */ + int user) /* I - User to run as or 0 for current */ +{ + int fds[2]; /* Pipe file descriptors */ + + + /* + * First create the pipe... + */ + + if (pipe(fds)) + { + *pid = 0; + return (NULL); + } + + /* + * Set the "close on exec" flag on each end of the pipe... + */ + + if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC)) + { + close(fds[0]); + close(fds[1]); + + *pid = 0; + + return (NULL); + } + + if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC)) + { + close(fds[0]); + close(fds[1]); + + *pid = 0; + + return (NULL); + } + + /* + * Then run the command... + */ + + if ((*pid = fork()) < 0) + { + /* + * Unable to fork! + */ + + *pid = 0; + close(fds[0]); + close(fds[1]); + + return (NULL); + } + else if (!*pid) + { + /* + * Child comes here... + */ + + if (!getuid() && user) + setuid(user); /* Run as restricted user */ + + close(0); /* pipe */ + dup(fds[1]); + + cupsdExec(command, argv); + exit(errno); + } + + /* + * Parent comes here, open the input side of the pipe... + */ + + close(fds[1]); + + return (cupsFileOpenFd(fds[0], "r")); +} + + +/* * 'cupsdSendIPPGroup()' - Send a group tag. */ Index: scheduler/util.h =================================================================== --- scheduler/util.h (revision 7617) +++ scheduler/util.h (working copy) @@ -3,7 +3,7 @@ * * Mini-daemon utility definitions for the Common UNIX Printing System (CUPS). * - * Copyright 2007 by Apple Inc. + * Copyright 2007-2008 by Apple Inc. * Copyright 1997-2005 by Easy Software Products. * * These coded instructions, statements, and computer programs are the @@ -33,14 +33,18 @@ * Prototypes... */ -extern int cupsdCompareNames(const char *s, const char *t); -extern void cupsdSendIPPGroup(ipp_tag_t group_tag); -extern void cupsdSendIPPHeader(ipp_status_t status_code, int request_id); -extern void cupsdSendIPPInteger(ipp_tag_t value_tag, const char *name, - int value); -extern void cupsdSendIPPString(ipp_tag_t value_tag, const char *name, - const char *value); -extern void cupsdSendIPPTrailer(void); +extern int cupsdCompareNames(const char *s, const char *t); +extern int cupsdExec(const char *command, char **argv); +extern cups_file_t *cupsdPipeCommand(int *pid, const char *command, + char **argv, int user); +extern void cupsdSendIPPGroup(ipp_tag_t group_tag); +extern void cupsdSendIPPHeader(ipp_status_t status_code, + int request_id); +extern void cupsdSendIPPInteger(ipp_tag_t value_tag, + const char *name, int value); +extern void cupsdSendIPPString(ipp_tag_t value_tag, + const char *name, const char *value); +extern void cupsdSendIPPTrailer(void); #endif /* !_CUPSD_UTIL_H_ */ Index: scheduler/cups-driverd.c =================================================================== --- scheduler/cups-driverd.c (revision 7617) +++ scheduler/cups-driverd.c (working copy) @@ -300,6 +300,7 @@ */ const char *serverbin; /* CUPS_SERVERBIN env var */ + char *argv[4]; /* Arguments for program */ if ((serverbin = getenv("CUPS_SERVERBIN")) == NULL) @@ -346,7 +347,12 @@ cupsdSendIPPTrailer(); } - if (execl(line, scheme, "cat", name, (char *)NULL)) + argv[0] = scheme; + argv[1] = (char *)"cat"; + argv[2] = (char *)name; + argv[3] = NULL; + + if (cupsdExec(line, argv)) { /* * Unable to execute driver... @@ -1552,10 +1558,12 @@ *ptr; /* Pointer into string */ const char *server_bin; /* CUPS_SERVERBIN env variable */ char drivers[1024]; /* Location of driver programs */ - FILE *fp; /* Pipe to driver program */ + int pid; /* Process ID for driver program */ + cups_file_t *fp; /* Pipe to driver program */ cups_dir_t *dir; /* Directory pointer */ cups_dentry_t *dent; /* Directory entry */ - char filename[1024], /* Name of driver */ + char *argv[3], /* Arguments for command */ + filename[1024], /* Name of driver */ line[2048], /* Line from driver */ name[512], /* ppd-name */ make[128], /* ppd-make */ @@ -1590,6 +1598,9 @@ * Loop through all of the device drivers... */ + argv[1] = (char *)"list"; + argv[2] = NULL; + while ((dent = cupsDirRead(dir)) != NULL) { /* @@ -1603,10 +1614,12 @@ * Run the driver with no arguments and collect the output... */ - snprintf(filename, sizeof(filename), "%s/%s list", drivers, dent->filename); - if ((fp = popen(filename, "r")) != NULL) + argv[0] = dent->filename; + snprintf(filename, sizeof(filename), "%s/%s", drivers, dent->filename); + + if ((fp = cupsdPipeCommand(&pid, filename, argv, 0)) != NULL) { - while (fgets(line, sizeof(line), fp) != NULL) + while (cupsFileGets(fp, line, sizeof(line))) { /* * Each line is of the form: @@ -1655,7 +1668,8 @@ if (type >= (int)(sizeof(ppd_types) / sizeof(ppd_types[0]))) { - fprintf(stderr, "ERROR: [cups-driverd] Bad ppd-type \"%s\" ignored!\n", + fprintf(stderr, + "ERROR: [cups-driverd] Bad ppd-type \"%s\" ignored!\n", type_str); type = PPD_TYPE_UNKNOWN; } @@ -1666,7 +1680,7 @@ if (!ppd) { cupsDirClose(dir); - pclose(fp); + cupsFileClose(fp); return (0); } @@ -1691,7 +1705,7 @@ } } - pclose(fp); + cupsFileClose(fp); } else fprintf(stderr, "WARNING: [cups-driverd] Unable to execute \"%s\": %s\n", Index: scheduler/cups-deviced.c =================================================================== --- scheduler/cups-deviced.c (revision 7617) +++ scheduler/cups-deviced.c (working copy) @@ -17,7 +17,6 @@ * main() - Scan for devices and return an IPP response. * add_dev() - Add a new device to the list. * compare_devs() - Compare device names for sorting. - * run_backend() - Run a backend to gather the available devices. * sigalrm_handler() - Handle alarm signals for backends that get hung */ @@ -64,7 +63,6 @@ const char *device_uri, const char *device_id); static int compare_devs(dev_info_t *p0, dev_info_t *p1); -static FILE *run_backend(const char *backend, int uid, int *pid); static void sigalrm_handler(int sig); @@ -85,7 +83,8 @@ int request_id; /* Request ID */ int count; /* Number of devices from backend */ int compat; /* Compatibility device? */ - FILE *fp; /* Pipe to device backend */ + 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 */ @@ -211,11 +210,13 @@ * all others run as the unprivileged user... */ - fp = run_backend(filename, - (dent->fileinfo.st_mode & (S_IRWXG | S_IRWXO)) - ? normal_user : 0, - &pid); + 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... */ @@ -246,7 +247,7 @@ alarm(30); - while (fgets(line, sizeof(line), fp) != NULL) + while (cupsFileGets(fp, line, sizeof(line))) { /* * Reset the alarm clock... @@ -291,7 +292,7 @@ if (!dev) { cupsDirClose(dir); - fclose(fp); + cupsFileClose(fp); kill(pid, SIGTERM); return (1); } @@ -312,7 +313,7 @@ fprintf(stderr, "WARNING: [cups-deviced] Backend \"%s\" did not " "respond within 30 seconds!\n", dent->filename); - fclose(fp); + cupsFileClose(fp); kill(pid, SIGTERM); /* @@ -482,71 +483,6 @@ /* - * 'run_backend()' - Run a backend to gather the available devices. - */ - -static FILE * /* O - stdout of backend */ -run_backend(const char *backend, /* I - Backend to run */ - int uid, /* I - User ID to run as */ - int *pid) /* O - Process ID of backend */ -{ - int fds[2]; /* Pipe file descriptors */ - - - if (pipe(fds)) - { - fprintf(stderr, "ERROR: Unable to create a pipe for \"%s\" - %s\n", - backend, strerror(errno)); - return (NULL); - } - - if ((*pid = fork()) < 0) - { - /* - * Error! - */ - - fprintf(stderr, "ERROR: Unable to fork for \"%s\" - %s\n", backend, - strerror(errno)); - close(fds[0]); - close(fds[1]); - return (NULL); - } - else if (!*pid) - { - /* - * Child comes here... - */ - - if (!getuid() && uid) - setuid(uid); /* Run as restricted user */ - - close(0); /* pipe */ - dup(fds[1]); - - close(fds[0]); /* Close copies of pipes */ - close(fds[1]); - - execl(backend, backend, (char *)0); /* Run it! */ - fprintf(stderr, "ERROR: Unable to execute \"%s\" - %s\n", backend, - strerror(errno)); - exit(1); - } - - /* - * Parent comes here, make a FILE * from the input side of the pipe... - */ - - close(fds[1]); - - return (fdopen(fds[0], "r")); -} - - -/* * 'sigalrm_handler()' - Handle alarm signals for backends that get hung * trying to list the available devices... */