/*
- * "$Id: util.c 5305 2006-03-18 03:05:12Z mike $"
+ * Mini-daemon utility functions for CUPS.
*
- * Mini-daemon utility functions for the Common UNIX Printing System (CUPS).
+ * Copyright 2007-2014 by Apple Inc.
+ * Copyright 1997-2005 by Easy Software Products.
*
- * 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:
- *
- * cupsdCompareNames() - Compare two names.
- * cupsdSendIPPGroup() - Send a group tag.
- * cupsdSendIPPHeader() - Send the IPP response header.
- * cupsdSendIPPInteger() - Send an integer attribute.
- * cupsdSendIPPString() - Send a string attribute.
- * cupsdSendIPPTrailer() - Send the end-of-message tag.
+ * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
*/
/*
*/
#include "util.h"
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifdef __APPLE__
+# include <libgen.h>
+extern char **environ;
+#endif /* __APPLE__ */
/*
* 'cupsdCompareNames()' - Compare two names.
*
- * This function basically does a strcasecmp() of the two strings,
+ * This function basically does a _cups_strcasecmp() of the two strings,
* but is also aware of numbers so that "a2" < "a100".
*/
else if (!isdigit(*s & 255) && isdigit(*t & 255))
return (-1);
else if (!isdigit(*s & 255) || !isdigit(*t & 255))
- continue;
+ continue;
if (*s < *t)
diff = -1;
}
+/*
+ * 'cupsdCreateStringsArray()' - Create a CUPS array of strings.
+ */
+
+cups_array_t * /* O - CUPS array */
+cupsdCreateStringsArray(const char *s) /* I - Comma-delimited strings */
+{
+ if (!s || !*s)
+ return (NULL);
+ else
+ return (_cupsArrayNewStrings(s, ','));
+}
+
+
+/*
+ * 'cupsdExec()' - Run a program with the correct environment.
+ *
+ * On macOS, 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 macOS 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 */
+ uid_t user) /* I - User to run as or 0 for current */
+{
+ int fd, /* Temporary file descriptor */
+ 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 */
+
+ if ((fd = open("/dev/null", O_RDONLY)) > 0)
+ {
+ dup2(fd, 0); /* </dev/null */
+ close(fd);
+ }
+
+ dup2(fds[1], 1); /* >pipe */
+ close(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.
*/
/*
* Send IPP/1.1 response header: version number (2 bytes), status code
* (2 bytes), and request ID (4 bytes)...
+ *
+ * TODO: Add version number (IPP/2.x and IPP/1.0) support.
*/
putchar(1);
}
-#if 0 /* Not currently used */
/*
* 'cupsdSendIPPInteger()' - Send an integer attribute.
*/
/*
* Send IPP integer value: value tag (1 byte), name length (2 bytes),
- * name string (without nul), and value (4 bytes)...
+ * name string (without nul), value length (2 bytes), and value (4 bytes)...
*/
putchar(value_tag);
len = strlen(name);
- putchar(len >> 8);
- putchar(len);
+ putchar((int)(len >> 8));
+ putchar((int)len);
fputs(name, stdout);
+ putchar(0);
+ putchar(4);
+
putchar(value >> 24);
putchar(value >> 16);
putchar(value >> 8);
putchar(value);
}
-#endif /* 0 */
/*
putchar(value_tag);
len = strlen(name);
- putchar(len >> 8);
- putchar(len);
+ putchar((int)(len >> 8));
+ putchar((int)len);
fputs(name, stdout);
len = strlen(value);
- putchar(len >> 8);
- putchar(len);
+ putchar((int)(len >> 8));
+ putchar((int)len);
fputs(value, stdout);
}
putchar(IPP_TAG_END);
fflush(stdout);
}
-
-
-/*
- * End of "$Id: util.c 5305 2006-03-18 03:05:12Z mike $".
- */