+
+ for (i = 0; i < num_pids; i++) {
+ /* Let's just be a little careful where we
+ * point that gun, hey? See kill(2) for the
+ * magic caused by negative values. */
+ pid_t p = all_pids[i];
+
+ if (p == getpid())
+ continue;
+ if (p <= 0)
+ continue;
+
+ kill(p, sig);
+ }
+}
+
+
+/** Turn a user name into a uid */
+int name_to_uid(char *name, uid_t *uid)
+{
+ struct passwd *pass;
+ if (!name || !*name)
+ return 0;
+ pass = getpwnam(name);
+ if (pass) {
+ *uid = pass->pw_uid;
+ return 1;
+ }
+ return 0;
+}
+
+/** Turn a group name into a gid */
+int name_to_gid(char *name, gid_t *gid)
+{
+ struct group *grp;
+ if (!name || !*name)
+ return 0;
+ grp = getgrnam(name);
+ if (grp) {
+ *gid = grp->gr_gid;
+ return 1;
+ }
+ return 0;
+}
+
+
+/** Lock a byte range in a open file */
+int lock_range(int fd, int offset, int len)
+{
+ struct flock lock;
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = offset;
+ lock.l_len = len;
+ lock.l_pid = 0;
+
+ return fcntl(fd,F_SETLK,&lock) == 0;
+}
+
+static int exclude_server_path(char *arg)
+{
+ char *s;
+
+ if (server_exclude_list.head) {
+ for (s = arg; (s = strchr(s, '/')) != NULL; ) {
+ *s = '\0';
+ if (check_exclude(&server_exclude_list, arg, 1) < 0) {
+ /* We must leave arg truncated! */
+ return 1;
+ }
+ *s++ = '/';
+ }
+ }
+ return 0;
+}
+
+static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr,
+ int *maxargs_ptr)
+{
+ char **argv = *argv_ptr;
+ int argc = *argc_ptr;
+ int maxargs = *maxargs_ptr;
+#if !(defined(HAVE_GLOB) && defined(HAVE_GLOB_H))
+ if (argc == maxargs) {
+ maxargs += MAX_ARGS;
+ if (!(argv = realloc_array(argv, char *, maxargs)))
+ out_of_memory("glob_expand_one");
+ *argv_ptr = argv;
+ *maxargs_ptr = maxargs;
+ }
+ if (!*s)
+ s = ".";
+ s = argv[argc++] = strdup(s);
+ exclude_server_path(s);
+#else
+ glob_t globbuf;
+ int i;
+
+ if (maxargs <= argc)
+ return;
+ if (!*s)
+ s = ".";
+
+ s = strdup(s);
+ if (sanitize_paths)
+ sanitize_path(s, NULL);
+
+ memset(&globbuf, 0, sizeof globbuf);
+ if (!exclude_server_path(s))
+ glob(s, 0, NULL, &globbuf);
+ if (MAX((int)globbuf.gl_pathc, 1) > maxargs - argc) {
+ maxargs += globbuf.gl_pathc + MAX_ARGS;
+ if (!(argv = realloc_array(argv, char *, maxargs)))
+ out_of_memory("glob_expand_one");
+ *argv_ptr = argv;
+ *maxargs_ptr = maxargs;
+ }
+ if (globbuf.gl_pathc == 0)
+ argv[argc++] = s;
+ else {
+ int j = globbuf.gl_pathc;
+ free(s);
+ for (i = 0; i < j; i++) {
+ if (!(argv[argc++] = strdup(globbuf.gl_pathv[i])))
+ out_of_memory("glob_expand_one");
+ }
+ }
+ globfree(&globbuf);
+#endif
+ *argc_ptr = argc;
+}
+
+/* This routine is only used in daemon mode. */
+void glob_expand(char *base1, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr)
+{
+ char *s = (*argv_ptr)[*argc_ptr];
+ char *p, *q;
+ char *base = base1;
+ int base_len = strlen(base);
+
+ if (!s || !*s)
+ return;
+
+ if (strncmp(s, base, base_len) == 0)
+ s += base_len;
+
+ if (!(s = strdup(s)))
+ out_of_memory("glob_expand");
+
+ if (asprintf(&base," %s/", base1) <= 0)
+ out_of_memory("glob_expand");
+ base_len++;
+
+ for (q = s; *q; q = p + base_len) {
+ if ((p = strstr(q, base)) != NULL)
+ *p = '\0'; /* split it at this point */
+ glob_expand_one(q, argv_ptr, argc_ptr, maxargs_ptr);
+ if (!p)
+ break;
+ }
+
+ free(s);
+ free(base);
+}
+
+/**
+ * Convert a string to lower case
+ **/
+void strlower(char *s)
+{
+ while (*s) {
+ if (isupper(*(unsigned char *)s))
+ *s = tolower(*(unsigned char *)s);
+ s++;
+ }
+}
+
+/* Join strings p1 & p2 into "dest" with a guaranteed '/' between them. (If
+ * p1 ends with a '/', no extra '/' is inserted.) Returns the length of both
+ * strings + 1 (if '/' was inserted), regardless of whether the null-terminated
+ * string fits into destsize. */
+size_t pathjoin(char *dest, size_t destsize, const char *p1, const char *p2)
+{
+ size_t len = strlcpy(dest, p1, destsize);
+ if (len < destsize - 1) {
+ if (!len || dest[len-1] != '/')
+ dest[len++] = '/';
+ if (len < destsize - 1)
+ len += strlcpy(dest + len, p2, destsize - len);
+ else {
+ dest[len] = '\0';
+ len += strlen(p2);
+ }
+ }
+ else
+ len += strlen(p2) + 1; /* Assume we'd insert a '/'. */
+ return len;
+}
+
+/* Join any number of strings together, putting them in "dest". The return
+ * value is the length of all the strings, regardless of whether the null-
+ * terminated whole fits in destsize. Your list of string pointers must end
+ * with a NULL to indicate the end of the list. */
+size_t stringjoin(char *dest, size_t destsize, ...)
+{
+ va_list ap;
+ size_t len, ret = 0;
+ const char *src;
+
+ va_start(ap, destsize);
+ while (1) {
+ if (!(src = va_arg(ap, const char *)))
+ break;
+ len = strlen(src);
+ ret += len;
+ if (destsize > 1) {
+ if (len >= destsize)
+ len = destsize - 1;
+ memcpy(dest, src, len);
+ destsize -= len;
+ dest += len;
+ }
+ }
+ *dest = '\0';
+ va_end(ap);
+
+ return ret;
+}
+
+void clean_fname(char *name)
+{
+ char *p;
+ int l;
+ int modified = 1;
+
+ if (!name)
+ return;
+
+ while (modified) {
+ modified = 0;
+
+ if ((p = strstr(name,"/./")) != NULL) {
+ modified = 1;
+ while (*p) {
+ p[0] = p[2];
+ p++;
+ }
+ }
+
+ if ((p = strstr(name,"//")) != NULL) {
+ modified = 1;
+ while (*p) {
+ p[0] = p[1];
+ p++;
+ }
+ }
+
+ if (strncmp(p = name, "./", 2) == 0) {
+ modified = 1;
+ do {
+ p[0] = p[2];
+ } while (*p++);
+ }
+
+ l = strlen(p = name);
+ if (l > 1 && p[l-1] == '/') {
+ modified = 1;
+ p[l-1] = 0;
+ }
+ }
+}
+
+/**
+ * Make path appear as if a chroot had occurred:
+ *
+ * @li 1. remove leading "/" (or replace with "." if at end)
+ *
+ * @li 2. remove leading ".." components (except those allowed by @p reldir)
+ *
+ * @li 3. delete any other "<dir>/.." (recursively)
+ *
+ * Can only shrink paths, so sanitizes in place.
+ *
+ * While we're at it, remove double slashes and "." components like
+ * clean_fname() does, but DON'T remove a trailing slash because that
+ * is sometimes significant on command line arguments.
+ *
+ * If @p reldir is non-null, it is a sanitized directory that the path will be
+ * relative to, so allow as many ".." at the beginning of the path as
+ * there are components in reldir. This is used for symbolic link targets.
+ * If reldir is non-null and the path began with "/", to be completely like
+ * a chroot we should add in depth levels of ".." at the beginning of the
+ * path, but that would blow the assumption that the path doesn't grow and
+ * it is not likely to end up being a valid symlink anyway, so just do
+ * the normal removal of the leading "/" instead.
+ *
+ * Contributed by Dave Dykstra <dwd@bell-labs.com>
+ */
+void sanitize_path(char *p, char *reldir)
+{
+ char *start, *sanp;
+ int depth = 0;
+ int allowdotdot = 0;
+
+ if (reldir) {
+ int new_component = 1;
+ while (*reldir) {
+ if (*reldir++ == '/')
+ new_component = 1;
+ else if (new_component) {
+ new_component = 0;
+ depth++;
+ }
+ }
+ }
+ start = p;
+ sanp = p;
+ while (*p == '/') {
+ /* remove leading slashes */
+ p++;
+ }
+ while (*p != '\0') {
+ /* this loop iterates once per filename component in p.
+ * both p (and sanp if the original had a slash) should
+ * always be left pointing after a slash
+ */
+ if (*p == '.' && (p[1] == '/' || p[1] == '\0')) {
+ /* skip "." component */
+ while (*++p == '/') {
+ /* skip following slashes */
+ ;
+ }
+ continue;
+ }
+ allowdotdot = 0;
+ if (*p == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) {
+ /* ".." component followed by slash or end */
+ if (depth > 0 && sanp == start) {
+ /* allow depth levels of .. at the beginning */
+ --depth;
+ allowdotdot = 1;
+ } else {
+ p += 2;
+ if (*p == '/')
+ p++;
+ if (sanp != start) {
+ /* back up sanp one level */
+ --sanp; /* now pointing at slash */
+ while (sanp > start && sanp[-1] != '/') {
+ /* skip back up to slash */
+ sanp--;
+ }
+ }
+ continue;
+ }
+ }
+ while (1) {
+ /* copy one component through next slash */
+ *sanp++ = *p++;
+ if (*p == '\0' || p[-1] == '/') {
+ while (*p == '/') {
+ /* skip multiple slashes */
+ p++;
+ }
+ break;
+ }
+ }
+ if (allowdotdot) {
+ /* move the virtual beginning to leave the .. alone */
+ start = sanp;
+ }
+ }
+ if (sanp == start && !allowdotdot) {
+ /* ended up with nothing, so put in "." component */
+ /*
+ * note that the !allowdotdot doesn't prevent this from
+ * happening in all allowed ".." situations, but I didn't
+ * think it was worth putting in an extra variable to ensure
+ * it since an extra "." won't hurt in those situations.
+ */
+ *sanp++ = '.';
+ }
+ *sanp = '\0';
+}
+
+/* Works much like sanitize_path(), with these differences: (1) a new buffer
+ * is allocated for the sanitized path rather than modifying it in-place; (2)
+ * a leading slash gets transformed into the rootdir value (which can be empty
+ * or NULL if you just want the slash to get dropped); (3) no "reldir" can be
+ * specified. */
+char *alloc_sanitize_path(const char *path, const char *rootdir)
+{
+ char *buf;
+ int rlen, plen = strlen(path);
+
+ if (*path == '/' && rootdir) {
+ rlen = strlen(rootdir);
+ if (rlen == 1)
+ path++;
+ } else
+ rlen = 0;
+ if (!(buf = new_array(char, rlen + plen + 1)))
+ out_of_memory("alloc_sanitize_path");
+ if (rlen)
+ memcpy(buf, rootdir, rlen);
+ memcpy(buf + rlen, path, plen + 1);
+
+ if (rlen > 1)
+ rlen++;
+ sanitize_path(buf + rlen, NULL);
+ if (rlen && buf[rlen] == '.' && buf[rlen+1] == '\0') {
+ if (rlen > 1)
+ rlen--;
+ buf[rlen] = '\0';
+ }
+
+ return buf;
+}
+
+char curr_dir[MAXPATHLEN];
+unsigned int curr_dir_len;
+
+/**
+ * Like chdir(), but it keeps track of the current directory (in the
+ * global "curr_dir"), and ensures that the path size doesn't overflow.
+ * Also cleans the path using the clean_fname() function.
+ **/
+int push_dir(char *dir)
+{
+ static int initialised;
+ unsigned int len;
+
+ if (!initialised) {
+ initialised = 1;
+ getcwd(curr_dir, sizeof curr_dir - 1);
+ curr_dir_len = strlen(curr_dir);
+ }
+
+ if (!dir) /* this call was probably just to initialize */
+ return 0;
+
+ len = strlen(dir);
+ if (len == 1 && *dir == '.')
+ return 1;
+
+ if ((*dir == '/' ? len : curr_dir_len + 1 + len) >= sizeof curr_dir)
+ return 0;
+
+ if (chdir(dir))
+ return 0;
+
+ if (*dir == '/') {
+ memcpy(curr_dir, dir, len + 1);
+ curr_dir_len = len;
+ } else {
+ curr_dir[curr_dir_len++] = '/';
+ memcpy(curr_dir + curr_dir_len, dir, len + 1);
+ curr_dir_len += len;