/* * idmapd.c * * Userland daemon for idmap. * * Copyright (c) 2002 The Regents of the University of Michigan. * All rights reserved. * * Marius Aamodt Eriksen * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include "nfs_idmap.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include "xlog.h" #include "conffile.h" #include "queue.h" #include "nfslib.h" #ifndef PIPEFS_DIR #define PIPEFS_DIR "/var/lib/nfs/rpc_pipefs/" #endif #ifndef NFSD_DIR #define NFSD_DIR "/proc/net/rpc" #endif #ifndef CLIENT_CACHE_TIMEOUT_FILE #define CLIENT_CACHE_TIMEOUT_FILE "/proc/sys/fs/nfs/idmap_cache_timeout" #endif #ifndef NFS4NOBODY_USER #define NFS4NOBODY_USER "nobody" #endif #ifndef NFS4NOBODY_GROUP #define NFS4NOBODY_GROUP "nobody" #endif /* From Niels */ #define CONF_SAVE(w, f) do { \ char *p = f; \ if (p != NULL) \ (w) = p; \ } while (0) #define IC_IDNAME 0 #define IC_IDNAME_CHAN NFSD_DIR "/nfs4.idtoname/channel" #define IC_IDNAME_FLUSH NFSD_DIR "/nfs4.idtoname/flush" #define IC_NAMEID 1 #define IC_NAMEID_CHAN NFSD_DIR "/nfs4.nametoid/channel" #define IC_NAMEID_FLUSH NFSD_DIR "/nfs4.nametoid/flush" struct idmap_client { short ic_which; char ic_clid[30]; char *ic_id; char ic_path[PATH_MAX]; int ic_fd; int ic_dirfd; int ic_scanned; struct event ic_event; TAILQ_ENTRY(idmap_client) ic_next; }; static struct idmap_client nfsd_ic[2] = { { .ic_which = IC_IDNAME, .ic_clid = "", .ic_id = "Server", .ic_path = IC_IDNAME_CHAN, .ic_fd = -1, .ic_dirfd = -1, .ic_scanned = 0 }, { .ic_which = IC_NAMEID, .ic_clid = "", .ic_id = "Server", .ic_path = IC_NAMEID_CHAN, .ic_fd = -1, .ic_dirfd = -1, .ic_scanned = 0 }, }; TAILQ_HEAD(idmap_clientq, idmap_client); static void dirscancb(int, short, void *); static void clntscancb(int, short, void *); static void svrreopen(int, short, void *); static int nfsopen(struct idmap_client *); static void nfscb(int, short, void *); static void nfsdcb(int, short, void *); static int addfield(char **, ssize_t *, char *); static int getfield(char **, char *, size_t); static void imconv(struct idmap_client *, struct idmap_msg *); static void idtonameres(struct idmap_msg *); static void nametoidres(struct idmap_msg *); static int nfsdopen(void); static int nfsdopenone(struct idmap_client *); static void nfsdreopen_one(struct idmap_client *); static void nfsdreopen(void); void mydaemon(int, int); void release_parent(void); static int verbose = 0; #define DEFAULT_IDMAP_CACHE_EXPIRY 600 /* seconds */ static int cache_entry_expiration = 0; static char pipefsdir[PATH_MAX]; static char *nobodyuser, *nobodygroup; static uid_t nobodyuid; static gid_t nobodygid; /* Used by conffile.c in libnfs.a */ char *conf_path; static int flush_nfsd_cache(char *path, time_t now) { int fd; char stime[20]; sprintf(stime, "%ld\n", now); fd = open(path, O_RDWR); if (fd == -1) return -1; if (write(fd, stime, strlen(stime)) != (ssize_t)strlen(stime)) { errx(1, "Flushing nfsd cache failed: errno %d (%s)", errno, strerror(errno)); } close(fd); return 0; } static int flush_nfsd_idmap_cache(void) { time_t now = time(NULL); int ret; ret = flush_nfsd_cache(IC_IDNAME_FLUSH, now); if (ret) return ret; ret = flush_nfsd_cache(IC_NAMEID_FLUSH, now); return ret; } int main(int argc, char **argv) { int fd = 0, opt, fg = 0, nfsdret = -1; struct idmap_clientq icq; struct event rootdirev, clntdirev, svrdirev; struct event initialize; struct passwd *pw; struct group *gr; struct stat sb; char *xpipefsdir = NULL; int serverstart = 1, clientstart = 1; int ret; char *progname; conf_path = _PATH_IDMAPDCONF; nobodyuser = NFS4NOBODY_USER; nobodygroup = NFS4NOBODY_GROUP; strlcpy(pipefsdir, PIPEFS_DIR, sizeof(pipefsdir)); if ((progname = strrchr(argv[0], '/'))) progname++; else progname = argv[0]; xlog_open(progname); #define GETOPTSTR "vfd:p:U:G:c:CS" opterr=0; /* Turn off error messages */ while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) { if (opt == 'c') conf_path = optarg; if (opt == '?') { if (strchr(GETOPTSTR, optopt)) errx(1, "'-%c' option requires an argument.", optopt); else errx(1, "'-%c' is an invalid argument.", optopt); } } optind = 1; if (stat(conf_path, &sb) == -1 && (errno == ENOENT || errno == EACCES)) { warn("Skipping configuration file \"%s\"", conf_path); conf_path = NULL; } else { conf_init(); verbose = conf_get_num("General", "Verbosity", 0); cache_entry_expiration = conf_get_num("General", "Cache-Expiration", DEFAULT_IDMAP_CACHE_EXPIRY); CONF_SAVE(xpipefsdir, conf_get_str("General", "Pipefs-Directory")); if (xpipefsdir != NULL) strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir)); CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User")); CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group")); } while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) switch (opt) { case 'v': verbose++; break; case 'f': fg = 1; break; case 'p': strlcpy(pipefsdir, optarg, sizeof(pipefsdir)); break; case 'd': case 'U': case 'G': errx(1, "the -d, -U, and -G options have been removed;" " please use the configuration file instead."); case 'C': serverstart = 0; break; case 'S': clientstart = 0; break; default: break; } if (!serverstart && !clientstart) errx(1, "it is illegal to specify both -C and -S"); strncat(pipefsdir, "/nfs", sizeof(pipefsdir)); if ((pw = getpwnam(nobodyuser)) == NULL) errx(1, "Could not find user \"%s\"", nobodyuser); nobodyuid = pw->pw_uid; if ((gr = getgrnam(nobodygroup)) == NULL) errx(1, "Could not find group \"%s\"", nobodygroup); nobodygid = gr->gr_gid; #ifdef HAVE_NFS4_SET_DEBUG nfs4_set_debug(verbose, xlog_warn); #endif if (conf_path == NULL) conf_path = _PATH_IDMAPDCONF; if (nfs4_init_name_mapping(conf_path)) errx(1, "Unable to create name to user id mappings."); if (!fg) mydaemon(0, 0); event_init(); if (verbose > 0) xlog_warn("Expiration time is %d seconds.", cache_entry_expiration); if (serverstart) { nfsdret = nfsdopen(); if (nfsdret == 0) { ret = flush_nfsd_idmap_cache(); if (ret) xlog_err("main: Failed to flush nfsd idmap cache\n: %s", strerror(errno)); } } if (clientstart) { struct timeval now = { .tv_sec = 0, .tv_usec = 0, }; if (cache_entry_expiration != DEFAULT_IDMAP_CACHE_EXPIRY) { int timeout_fd, len; char timeout_buf[12]; if ((timeout_fd = open(CLIENT_CACHE_TIMEOUT_FILE, O_RDWR)) == -1) { xlog_warn("Unable to open '%s' to set " "client cache expiration time " "to %d seconds\n", CLIENT_CACHE_TIMEOUT_FILE, cache_entry_expiration); } else { len = snprintf(timeout_buf, sizeof(timeout_buf), "%d", cache_entry_expiration); if ((write(timeout_fd, timeout_buf, len)) != len) xlog_warn("Error writing '%s' to " "'%s' to set client " "cache expiration time\n", timeout_buf, CLIENT_CACHE_TIMEOUT_FILE); close(timeout_fd); } } if ((fd = open(pipefsdir, O_RDONLY)) == -1) xlog_err("main: open(%s): %s", pipefsdir, strerror(errno)); if (fcntl(fd, F_SETSIG, SIGUSR1) == -1) xlog_err("main: fcntl(%s): %s", pipefsdir, strerror(errno)); if (fcntl(fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MODIFY | DN_MULTISHOT) == -1) { xlog_err("main: fcntl(%s): %s", pipefsdir, strerror(errno)); if (errno == EINVAL) xlog_err("main: Possibly no Dnotify support in kernel."); } TAILQ_INIT(&icq); /* These events are persistent */ signal_set(&rootdirev, SIGUSR1, dirscancb, &icq); signal_add(&rootdirev, NULL); signal_set(&clntdirev, SIGUSR2, clntscancb, &icq); signal_add(&clntdirev, NULL); signal_set(&svrdirev, SIGHUP, svrreopen, NULL); signal_add(&svrdirev, NULL); /* Fetch current state */ /* (Delay till start of event_dispatch to avoid possibly losing * a SIGUSR1 between here and the call to event_dispatch().) */ evtimer_set(&initialize, dirscancb, &icq); evtimer_add(&initialize, &now); } if (nfsdret != 0 && fd == 0) xlog_err("main: Neither NFS client nor NFSd found"); release_parent(); if (event_dispatch() < 0) xlog_err("main: event_dispatch returns errno %d (%s)", errno, strerror(errno)); /* NOTREACHED */ return 1; } static void dirscancb(int UNUSED(fd), short UNUSED(which), void *data) { int nent, i; struct dirent **ents; struct idmap_client *ic, *nextic; char path[PATH_MAX]; struct idmap_clientq *icq = data; nent = scandir(pipefsdir, &ents, NULL, alphasort); if (nent == -1) { xlog_warn("dirscancb: scandir(%s): %s", pipefsdir, strerror(errno)); return; } for (i = 0; i < nent; i++) { if (ents[i]->d_reclen > 4 && strncmp(ents[i]->d_name, "clnt", 4) == 0) { TAILQ_FOREACH(ic, icq, ic_next) if (strcmp(ents[i]->d_name + 4, ic->ic_clid) == 0) break; if (ic != NULL) goto next; if ((ic = calloc(1, sizeof(*ic))) == NULL) goto out; strlcpy(ic->ic_clid, ents[i]->d_name + 4, sizeof(ic->ic_clid)); path[0] = '\0'; snprintf(path, sizeof(path), "%s/%s", pipefsdir, ents[i]->d_name); if ((ic->ic_dirfd = open(path, O_RDONLY, 0)) == -1) { xlog_warn("dirscancb: open(%s): %s", path, strerror(errno)); free(ic); goto out; } strlcat(path, "/idmap", sizeof(path)); strlcpy(ic->ic_path, path, sizeof(ic->ic_path)); if (verbose > 0) xlog_warn("New client: %s", ic->ic_clid); if (nfsopen(ic) == -1) { close(ic->ic_dirfd); free(ic); goto out; } ic->ic_id = "Client"; TAILQ_INSERT_TAIL(icq, ic, ic_next); next: ic->ic_scanned = 1; } } ic = TAILQ_FIRST(icq); while(ic != NULL) { nextic=TAILQ_NEXT(ic, ic_next); if (!ic->ic_scanned) { event_del(&ic->ic_event); close(ic->ic_fd); close(ic->ic_dirfd); TAILQ_REMOVE(icq, ic, ic_next); if (verbose > 0) { xlog_warn("Stale client: %s", ic->ic_clid); xlog_warn("\t-> closed %s", ic->ic_path); } free(ic); } else ic->ic_scanned = 0; ic = nextic; } out: for (i = 0; i < nent; i++) free(ents[i]); free(ents); return; } static void svrreopen(int UNUSED(fd), short UNUSED(which), void *UNUSED(data)) { nfsdreopen(); } static void clntscancb(int UNUSED(fd), short UNUSED(which), void *data) { struct idmap_clientq *icq = data; struct idmap_client *ic; TAILQ_FOREACH(ic, icq, ic_next) if (ic->ic_fd == -1 && nfsopen(ic) == -1) { close(ic->ic_dirfd); TAILQ_REMOVE(icq, ic, ic_next); free(ic); } } static void nfsdcb(int UNUSED(fd), short which, void *data) { struct idmap_client *ic = data; struct idmap_msg im; u_char buf[IDMAP_MAXMSGSZ + 1]; size_t len; ssize_t bsiz; char *bp, typebuf[IDMAP_MAXMSGSZ], buf1[IDMAP_MAXMSGSZ], authbuf[IDMAP_MAXMSGSZ], *p; unsigned long tmp; if (which != EV_READ) goto out; if ((len = read(ic->ic_fd, buf, sizeof(buf))) <= 0) { xlog_warn("nfsdcb: read(%s) failed: errno %d (%s)", ic->ic_path, len?errno:0, len?strerror(errno):"End of File"); nfsdreopen_one(ic); return; } /* Get rid of newline and terminate buffer*/ buf[len - 1] = '\0'; bp = (char *)buf; memset(&im, 0, sizeof(im)); /* Authentication name -- ignored for now*/ if (getfield(&bp, authbuf, sizeof(authbuf)) == -1) { xlog_warn("nfsdcb: bad authentication name in upcall\n"); goto out; } if (getfield(&bp, typebuf, sizeof(typebuf)) == -1) { xlog_warn("nfsdcb: bad type in upcall\n"); goto out; } if (verbose > 0) xlog_warn("nfsdcb: authbuf=%s authtype=%s", authbuf, typebuf); im.im_type = strcmp(typebuf, "user") == 0 ? IDMAP_TYPE_USER : IDMAP_TYPE_GROUP; switch (ic->ic_which) { case IC_NAMEID: im.im_conv = IDMAP_CONV_NAMETOID; if (getfield(&bp, im.im_name, sizeof(im.im_name)) == -1) { xlog_warn("nfsdcb: bad name in upcall\n"); goto out; } break; case IC_IDNAME: im.im_conv = IDMAP_CONV_IDTONAME; if (getfield(&bp, buf1, sizeof(buf1)) == -1) { xlog_warn("nfsdcb: bad id in upcall\n"); goto out; } tmp = strtoul(buf1, (char **)NULL, 10); im.im_id = (u_int32_t)tmp; if ((tmp == ULONG_MAX && errno == ERANGE) || (unsigned long)im.im_id != tmp) { xlog_warn("nfsdcb: id '%s' too big!\n", buf1); goto out; } break; default: xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which); goto out; } imconv(ic, &im); buf[0] = '\0'; bp = (char *)buf; bsiz = sizeof(buf); /* Authentication name */ addfield(&bp, &bsiz, authbuf); switch (ic->ic_which) { case IC_NAMEID: /* Type */ p = im.im_type == IDMAP_TYPE_USER ? "user" : "group"; addfield(&bp, &bsiz, p); /* Name */ addfield(&bp, &bsiz, im.im_name); /* expiry */ snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + cache_entry_expiration); addfield(&bp, &bsiz, buf1); /* Note that we don't want to write the id if the mapping * failed; instead, by leaving it off, we write a negative * cache entry which will result in an error returned to * the client. We don't want a chown or setacl referring * to an unknown user to result in giving permissions to * "nobody"! */ if (im.im_status == IDMAP_STATUS_SUCCESS) { /* ID */ snprintf(buf1, sizeof(buf1), "%u", im.im_id); addfield(&bp, &bsiz, buf1); } //if (bsiz == sizeof(buf)) /* XXX */ bp[-1] = '\n'; break; case IC_IDNAME: /* Type */ p = im.im_type == IDMAP_TYPE_USER ? "user" : "group"; addfield(&bp, &bsiz, p); /* ID */ snprintf(buf1, sizeof(buf1), "%u", im.im_id); addfield(&bp, &bsiz, buf1); /* expiry */ snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + cache_entry_expiration); addfield(&bp, &bsiz, buf1); /* Note we're ignoring the status field in this case; we'll * just map to nobody instead. */ /* Name */ addfield(&bp, &bsiz, im.im_name); bp[-1] = '\n'; break; default: xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which); goto out; } bsiz = sizeof(buf) - bsiz; if (atomicio((void*)write, ic->ic_fd, buf, bsiz) != bsiz) xlog_warn("nfsdcb: write(%s) failed: errno %d (%s)", ic->ic_path, errno, strerror(errno)); out: event_add(&ic->ic_event, NULL); } static void imconv(struct idmap_client *ic, struct idmap_msg *im) { u_int32_t len; switch (im->im_conv) { case IDMAP_CONV_IDTONAME: idtonameres(im); if (verbose > 1) xlog_warn("%s %s: (%s) id \"%d\" -> name \"%s\"", ic->ic_id, ic->ic_clid, im->im_type == IDMAP_TYPE_USER ? "user" : "group", im->im_id, im->im_name); break; case IDMAP_CONV_NAMETOID: len = strnlen(im->im_name, IDMAP_NAMESZ - 1); /* Check for NULL termination just to be careful */ if (im->im_name[len+1] != '\0') return; nametoidres(im); if (verbose > 1) xlog_warn("%s %s: (%s) name \"%s\" -> id \"%d\"", ic->ic_id, ic->ic_clid, im->im_type == IDMAP_TYPE_USER ? "user" : "group", im->im_name, im->im_id); break; default: xlog_warn("imconv: Invalid conversion type (%d) in message", im->im_conv); im->im_status |= IDMAP_STATUS_INVALIDMSG; break; } } static void nfscb(int UNUSED(fd), short which, void *data) { struct idmap_client *ic = data; struct idmap_msg im; if (which != EV_READ) goto out; if (atomicio(read, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) { if (verbose > 0) xlog_warn("nfscb: read(%s): %s", ic->ic_path, strerror(errno)); if (errno == EPIPE) return; goto out; } imconv(ic, &im); /* XXX: I don't like ignoring this error in the id->name case, * but we've never returned it, and I need to check that the client * can handle it gracefully before starting to return it now. */ if (im.im_status == IDMAP_STATUS_LOOKUPFAIL) im.im_status = IDMAP_STATUS_SUCCESS; if (atomicio((void*)write, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) xlog_warn("nfscb: write(%s): %s", ic->ic_path, strerror(errno)); out: event_add(&ic->ic_event, NULL); } static void nfsdreopen_one(struct idmap_client *ic) { int fd; if (verbose > 0) xlog_warn("ReOpening %s", ic->ic_path); if ((fd = open(ic->ic_path, O_RDWR, 0)) != -1) { if ((ic->ic_event.ev_flags & EVLIST_INIT)) event_del(&ic->ic_event); if (ic->ic_fd != -1) close(ic->ic_fd); ic->ic_event.ev_fd = ic->ic_fd = fd; event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic); event_add(&ic->ic_event, NULL); } else { xlog_warn("nfsdreopen: Opening '%s' failed: errno %d (%s)", ic->ic_path, errno, strerror(errno)); } } static void nfsdreopen() { nfsdreopen_one(&nfsd_ic[IC_NAMEID]); nfsdreopen_one(&nfsd_ic[IC_IDNAME]); return; } static int nfsdopen(void) { return ((nfsdopenone(&nfsd_ic[IC_NAMEID]) == 0 && nfsdopenone(&nfsd_ic[IC_IDNAME]) == 0) ? 0 : -1); } static int nfsdopenone(struct idmap_client *ic) { if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) { if (verbose > 0) xlog_warn("nfsdopenone: Opening %s failed: " "errno %d (%s)", ic->ic_path, errno, strerror(errno)); return (-1); } event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic); event_add(&ic->ic_event, NULL); if (verbose > 0) xlog_warn("Opened %s", ic->ic_path); return (0); } static int nfsopen(struct idmap_client *ic) { if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) { switch (errno) { case ENOENT: fcntl(ic->ic_dirfd, F_SETSIG, SIGUSR2); fcntl(ic->ic_dirfd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT); break; default: xlog_warn("nfsopen: open(%s): %s", ic->ic_path, strerror(errno)); return (-1); } } else { event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfscb, ic); event_add(&ic->ic_event, NULL); fcntl(ic->ic_dirfd, F_NOTIFY, 0); fcntl(ic->ic_dirfd, F_SETSIG, 0); if (verbose > 0) xlog_warn("Opened %s", ic->ic_path); } return (0); } static void idtonameres(struct idmap_msg *im) { char domain[NFS4_MAX_DOMAIN_LEN]; int ret = 0; ret = nfs4_get_default_domain(NULL, domain, sizeof(domain)); switch (im->im_type) { case IDMAP_TYPE_USER: ret = nfs4_uid_to_name(im->im_id, domain, im->im_name, sizeof(im->im_name)); if (ret) { if (strlen(nobodyuser) < sizeof(im->im_name)) strcpy(im->im_name, nobodyuser); else strcpy(im->im_name, NFS4NOBODY_USER); } break; case IDMAP_TYPE_GROUP: ret = nfs4_gid_to_name(im->im_id, domain, im->im_name, sizeof(im->im_name)); if (ret) { if (strlen(nobodygroup) < sizeof(im->im_name)) strcpy(im->im_name, nobodygroup); else strcpy(im->im_name, NFS4NOBODY_GROUP); } break; } if (ret) im->im_status = IDMAP_STATUS_LOOKUPFAIL; else im->im_status = IDMAP_STATUS_SUCCESS; } static void nametoidres(struct idmap_msg *im) { uid_t uid; gid_t gid; int ret = 0; /* XXX: move nobody stuff to library calls * (nfs4_get_nobody_user(domain), nfs4_get_nobody_group(domain)) */ im->im_status = IDMAP_STATUS_SUCCESS; switch (im->im_type) { case IDMAP_TYPE_USER: ret = nfs4_name_to_uid(im->im_name, &uid); im->im_id = (u_int32_t) uid; if (ret) { im->im_status = IDMAP_STATUS_LOOKUPFAIL; im->im_id = nobodyuid; } return; case IDMAP_TYPE_GROUP: ret = nfs4_name_to_gid(im->im_name, &gid); im->im_id = (u_int32_t) gid; if (ret) { im->im_status = IDMAP_STATUS_LOOKUPFAIL; im->im_id = nobodygid; } return; } } static int addfield(char **bpp, ssize_t *bsizp, char *fld) { char ch, *bp = *bpp; ssize_t bsiz = *bsizp; while ((ch = *fld++) != '\0' && bsiz > 0) { switch(ch) { case ' ': case '\t': case '\n': case '\\': if (bsiz >= 4) { bp += snprintf(bp, bsiz, "\\%03o", ch); bsiz -= 4; } break; default: *bp++ = ch; bsiz--; break; } } if (bsiz < 1 || ch != '\0') return (-1); *bp++ = ' '; bsiz--; *bpp = bp; *bsizp = bsiz; return (0); } static int getfield(char **bpp, char *fld, size_t fldsz) { char *bp; int val, n; while ((bp = strsep(bpp, " ")) != NULL && bp[0] == '\0') ; if (bp == NULL || bp[0] == '\0' || bp[0] == '\n') return (-1); while (*bp != '\0' && fldsz > 1) { if (*bp == '\\') { if ((n = sscanf(bp, "\\%03o", &val)) != 1) return (-1); if (val > UCHAR_MAX) return (-1); *fld++ = val; bp += 4; } else { *fld++ = *bp; bp++; } fldsz--; } if (*bp != '\0') return (-1); *fld = '\0'; return (0); } /* * mydaemon creates a pipe between the partent and child * process. The parent process will wait until the * child dies or writes a '1' on the pipe signaling * that it started successfully. */ int pipefds[2] = { -1, -1}; void mydaemon(int nochdir, int noclose) { int pid, status, tempfd; if (pipe(pipefds) < 0) err(1, "mydaemon: pipe() failed: errno %d", errno); if ((pid = fork ()) < 0) err(1, "mydaemon: fork() failed: errno %d", errno); if (pid != 0) { /* * Parent. Wait for status from child. */ close(pipefds[1]); if (read(pipefds[0], &status, 1) != 1) exit(1); exit (0); } /* Child. */ close(pipefds[0]); setsid (); if (nochdir == 0) { if (chdir ("/") == -1) err(1, "mydaemon: chdir() failed: errno %d", errno); } while (pipefds[1] <= 2) { pipefds[1] = dup(pipefds[1]); if (pipefds[1] < 0) err(1, "mydaemon: dup() failed: errno %d", errno); } if (noclose == 0) { tempfd = open("/dev/null", O_RDWR); if (tempfd < 0) tempfd = open("/", O_RDONLY); if (tempfd >= 0) { dup2(tempfd, 0); dup2(tempfd, 1); dup2(tempfd, 2); close(tempfd); } else { err(1, "mydaemon: can't open /dev/null: errno %d", errno); exit(1); } } return; } void release_parent(void) { int status; if (pipefds[1] > 0) { if (write(pipefds[1], &status, 1) != 1) { err(1, "Writing to parent pipe failed: errno %d (%s)\n", errno, strerror(errno)); } close(pipefds[1]); pipefds[1] = -1; } }