blob: 573abaaead3153817fec3667169d6267c8bf1df2 [file] [log] [blame]
/*
* idmapd.c
*
* Userland daemon for idmap.
*
* Copyright (c) 2002 The Regents of the University of Michigan.
* All rights reserved.
*
* Marius Aamodt Eriksen <marius@umich.edu>
*
* 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 <sys/types.h>
#include <sys/time.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <time.h>
#include "nfs_idmap.h"
#include <err.h>
#include <errno.h>
#include <event.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <pwd.h>
#include <grp.h>
#include <limits.h>
#include <ctype.h>
#include <nfsidmap.h>
#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_IDNAME, "Server", "", IC_IDNAME_CHAN, -1, -1, 0},
{IC_NAMEID, "Server", "", IC_NAMEID_CHAN, -1, -1, 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 validateascii(char *, u_int32_t);
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);
size_t strlcat(char *, const char *, size_t);
size_t strlcpy(char *, const char *, size_t);
ssize_t atomicio(ssize_t (*f) (int, void*, size_t),
int, void *, size_t);
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)) != 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 fd, short 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 fd, short which, void *data)
{
nfsdreopen();
}
static void
clntscancb(int fd, short 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 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)
{
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:
if (validateascii(im->im_name, sizeof(im->im_name)) == -1) {
im->im_status |= IDMAP_STATUS_INVALIDMSG;
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 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_SETSIG, 0);
fcntl(ic->ic_dirfd, F_NOTIFY, 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
validateascii(char *string, u_int32_t len)
{
int i;
for (i = 0; i < len; i++) {
if (string[i] == '\0')
break;
if (string[i] & 0x80)
return (-1);
}
if ((i >= len) || string[i] != '\0')
return (-1);
return (i + 1);
}
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;
u_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 > (char)-1)
return (-1);
*fld++ = (char)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;
}
}