]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/idmapd/idmapd.c
further idmapd update
[nfs-utils.git] / utils / idmapd / idmapd.c
1 /*
2  *  idmapd.c
3  *
4  *  Userland daemon for idmap.
5  *
6  *  Copyright (c) 2002 The Regents of the University of Michigan.
7  *  All rights reserved.
8  *
9  *  Marius Aamodt Eriksen <marius@umich.edu>
10  *
11  *  Redistribution and use in source and binary forms, with or without
12  *  modification, are permitted provided that the following conditions
13  *  are met:
14  *
15  *  1. Redistributions of source code must retain the above copyright
16  *     notice, this list of conditions and the following disclaimer.
17  *  2. Redistributions in binary form must reproduce the above copyright
18  *     notice, this list of conditions and the following disclaimer in the
19  *     documentation and/or other materials provided with the distribution.
20  *  3. Neither the name of the University nor the names of its
21  *     contributors may be used to endorse or promote products derived
22  *     from this software without specific prior written permission.
23  *
24  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
25  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
31  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36
37 #include <sys/types.h>
38 #include <sys/time.h>
39 #include <sys/poll.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <time.h>
43
44 #include "nfs_idmap.h"
45
46 #include <err.h>
47 #include <errno.h>
48 #include <event.h>
49 #include <fcntl.h>
50 #include <dirent.h>
51 #include <unistd.h>
52 #include <netdb.h>
53 #include <signal.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <pwd.h>
58 #include <grp.h>
59 #include <limits.h>
60 #include <ctype.h>
61 #include <nfsidmap.h>
62
63 #ifdef HAVE_CONFIG_H
64 #include "config.h"
65 #endif /* HAVE_CONFIG_H */
66
67 #include "cfg.h"
68 #include "queue.h"
69 #include "nfslib.h"
70
71 #ifndef PIPEFS_DIR
72 #define PIPEFS_DIR  "/var/lib/nfs/rpc_pipefs/"
73 #endif
74
75 #ifndef NFSD_DIR
76 #define NFSD_DIR  "/proc/net/rpc"
77 #endif
78
79 #ifndef NFS4NOBODY_USER
80 #define NFS4NOBODY_USER "nobody"
81 #endif
82
83 #ifndef NFS4NOBODY_GROUP
84 #define NFS4NOBODY_GROUP "nobody"
85 #endif
86
87 /* From Niels */
88 #define CONF_SAVE(w, f) do {                    \
89         char *p = f;                            \
90         if (p != NULL)                          \
91                 (w) = p;                        \
92 } while (0)
93
94 #define IC_IDNAME 1
95 #define IC_NAMEID 2
96 struct idmap_client {
97         int                        ic_fd;
98         int                        ic_dirfd;
99         char                       ic_clid[30];
100         char                       ic_path[PATH_MAX];
101         int                        ic_scanned;
102         struct event               ic_event;
103         char                      *ic_id;
104         short                      ic_which;
105         TAILQ_ENTRY(idmap_client)  ic_next;
106 };
107
108 TAILQ_HEAD(idmap_clientq, idmap_client);
109
110 static void dirscancb(int, short, void *);
111 static void clntscancb(int, short, void *);
112 static void svrreopen(int, short, void *);
113 static int  nfsopen(struct idmap_client *);
114 static void nfscb(int, short, void *);
115 static void nfsdcb(int, short, void *);
116 static int  validateascii(char *, u_int32_t);
117 static int  addfield(char **, ssize_t *, char *);
118 static int  getfield(char **, char *, size_t);
119
120 static void imconv(struct idmap_client *, struct idmap_msg *);
121 static void idtonameres(struct idmap_msg *);
122 static void nametoidres(struct idmap_msg *);
123
124 static int nfsdopen(char *);
125 static int nfsdopenone(struct idmap_client *, short, char *);
126 static void nfsdreopen(void);
127
128 size_t  strlcat(char *, const char *, size_t);
129 size_t  strlcpy(char *, const char *, size_t);
130 ssize_t atomicio(ssize_t (*)(), int, void *, size_t);
131 void    mydaemon(int, int);
132 void    release_parent();
133
134 static int verbose = 0;
135 static char domain[512];
136 static char pipefsdir[PATH_MAX];
137 static char *nobodyuser, *nobodygroup;
138 static uid_t nobodyuid;
139 static gid_t nobodygid;
140 static struct idmap_client nfsd_ic[2];
141
142 /* Used by cfg.c */
143 char *conf_path;
144
145 int
146 main(int argc, char **argv)
147 {
148         int fd = 0, opt, fg = 0, nfsdret = -1;
149         struct idmap_clientq icq;
150         struct event rootdirev, clntdirev, svrdirev;
151         struct event initialize;
152         struct passwd *pw;
153         struct group *gr;
154         struct stat sb;
155         char *xpipefsdir = NULL;
156         char *xdomain = NULL;
157         int serverstart = 1, clientstart = 1;
158
159         conf_path = _PATH_IDMAPDCONF;
160         nobodyuser = NFS4NOBODY_USER;
161         nobodygroup = NFS4NOBODY_GROUP;
162         strlcpy(pipefsdir, PIPEFS_DIR, sizeof(pipefsdir));
163
164 #define GETOPTSTR "vfd:p:U:G:c:CS"
165         opterr=0; /* Turn off error messages */
166         while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) {
167                 if (opt == 'c')
168                         conf_path = optarg;
169                 if (opt == '?') {
170                         if (strchr(GETOPTSTR, optopt))
171                                 errx(1, "'-%c' option requires an argument.", optopt);
172                         else
173                                 errx(1, "'-%c' is an invalid argument.", optopt);
174                 }
175         }
176         optind = 1;
177
178         if (stat(conf_path, &sb) == -1 && (errno == ENOENT || errno == EACCES)) {
179                 warn("Skipping configuration file \"%s\"", conf_path);
180         } else {
181                 conf_init();
182                 verbose = conf_get_num("General", "Verbosity", 0);
183                 CONF_SAVE(xpipefsdir, conf_get_str("General", "Pipefs-Directory"));
184                 CONF_SAVE(xdomain, conf_get_str("General", "Domain"));
185                 if (xpipefsdir != NULL)
186                         strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir));
187                 if (xdomain != NULL)
188                         strlcpy(domain, xdomain, sizeof(domain));
189                 CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User"));
190                 CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group"));
191         }
192
193         while ((opt = getopt(argc, argv, GETOPTSTR)) != -1)
194                 switch (opt) {
195                 case 'v':
196                         verbose++;
197                         break;
198                 case 'f':
199                         fg = 1;
200                         break;
201                 case 'p':
202                         strlcpy(pipefsdir, optarg, sizeof(pipefsdir));
203                         break;
204                 case 'd':
205                 case 'U':
206                 case 'G':
207                         errx(1, "the -d, -U, and -G options have been removed;"
208                                 " please use the configuration file instead.");
209                 case 'C':
210                         serverstart = 0;
211                         break;
212                 case 'S':
213                         clientstart = 0;
214                         break;
215                 default:
216                         break;
217                 }
218
219         if (!serverstart && !clientstart)
220                 errx(1, "it is illegal to specify both -C and -S");
221
222         strncat(pipefsdir, "/nfs", sizeof(pipefsdir));
223
224         if (domain[0] == '\0') {
225                 struct hostent *he;
226                 char hname[64], *c;
227
228                 if (gethostname(hname, sizeof(hname)) == -1)
229                         errx(1, "Error getting hostname");
230
231                 if ((he = gethostbyname(hname)) == NULL)
232                         errx(1, "Error resolving hostname: %s", hname);
233
234                 if ((c = strchr(he->h_name, '.')) == NULL || *++c == '\0')
235                         errx(1, "Error resolving domain, "
236                             "please use the -d switch");
237
238                 strlcpy(domain, c, sizeof(domain));
239         }
240
241         if ((pw = getpwnam(nobodyuser)) == NULL)
242                 errx(1, "Could not find user \"%s\"", nobodyuser);
243         nobodyuid = pw->pw_uid;
244
245         if ((gr = getgrnam(nobodygroup)) == NULL)
246                 errx(1, "Could not find group \"%s\"", nobodygroup);
247         nobodygid = gr->gr_gid;
248
249         if (strlen(domain) == 0)
250                 errx(1, "Invalid domain; please specify with -d switch");
251
252         if (verbose > 2)
253                 warnx("Using domain \"%s\"", domain);
254
255         if (!fg)
256                 mydaemon(0, 0);
257
258         event_init();
259
260         if (serverstart)
261                 nfsdret = nfsdopen(NFSD_DIR);
262
263         if (clientstart) {
264                 struct timeval now = {
265                         .tv_sec = 0,
266                         .tv_usec = 0,
267                 };
268
269                 if ((fd = open(pipefsdir, O_RDONLY)) == -1)
270                         err(1, "open(%s)", pipefsdir);
271
272                 if (fcntl(fd, F_SETSIG, SIGUSR1) == -1)
273                         err(1, "fcntl(%s)", pipefsdir);
274                 if (fcntl(fd, F_NOTIFY,
275                         DN_CREATE | DN_DELETE | DN_MODIFY | DN_MULTISHOT) == -1)
276                         err(1, "fcntl(%s)", pipefsdir);
277
278                 TAILQ_INIT(&icq);
279
280                 /* These events are persistent */
281                 signal_set(&rootdirev, SIGUSR1, dirscancb, &icq);
282                 signal_add(&rootdirev, NULL);
283                 signal_set(&clntdirev, SIGUSR2, clntscancb, &icq);
284                 signal_add(&clntdirev, NULL);
285                 signal_set(&svrdirev, SIGHUP, svrreopen, NULL);
286                 signal_add(&svrdirev, NULL);
287
288                 /* Fetch current state */
289                 /* (Delay till start of event_dispatch to avoid possibly losing
290                  * a SIGUSR1 between here and the call to event_dispatch().) */
291                 evtimer_set(&initialize, dirscancb, &icq);
292                 evtimer_add(&initialize, &now);
293         }
294
295         if (nfsdret != 0 && fd == 0)
296                 errx(1, "Neither NFS client nor NFSd found");
297
298         release_parent();
299
300         if (event_dispatch() < 0)
301                 errx(1, "event_dispatch: returns errno %d (%s)", errno, strerror(errno));
302         /* NOTREACHED */
303         return 1;
304 }
305
306 static void
307 dirscancb(int fd, short which, void *data)
308 {
309         int nent, i;
310         struct dirent **ents;
311         struct idmap_client *ic;
312         char path[PATH_MAX];
313         struct idmap_clientq *icq = data;
314
315         nent = scandir(pipefsdir, &ents, NULL, alphasort);
316         if (nent == -1) {
317                 warn("scandir(%s)", pipefsdir);
318                 return;
319         }
320
321         for (i = 0;  i < nent; i++) {
322                 if (ents[i]->d_reclen > 4 &&
323                     strncmp(ents[i]->d_name, "clnt", 4) == 0) {
324                         TAILQ_FOREACH(ic, icq, ic_next)
325                             if (strcmp(ents[i]->d_name + 4, ic->ic_clid) == 0)
326                                     break;
327                         if (ic != NULL)
328                                 goto next;
329
330                         if ((ic = calloc(1, sizeof(*ic))) == NULL)
331                                 return;
332                         strlcpy(ic->ic_clid, ents[i]->d_name + 4,
333                             sizeof(ic->ic_clid));
334                         path[0] = '\0';
335                         snprintf(path, sizeof(path), "%s/%s",
336                             pipefsdir, ents[i]->d_name);
337
338                         if ((ic->ic_dirfd = open(path, O_RDONLY, 0)) == -1) {
339                                 warn("open(%s)", path);
340                                 free(ic);
341                                 return;
342                         }
343
344                         strlcat(path, "/idmap", sizeof(path));
345                         strlcpy(ic->ic_path, path, sizeof(ic->ic_path));
346
347                         if (verbose > 0)
348                                 warnx("New client: %s", ic->ic_clid);
349
350                         if (nfsopen(ic) == -1) {
351                                 close(ic->ic_dirfd);
352                                 free(ic);
353                                 return;
354                         }
355
356                         ic->ic_id = "Client";
357
358                         TAILQ_INSERT_TAIL(icq, ic, ic_next);
359
360                 next:
361                         ic->ic_scanned = 1;
362                 }
363         }
364
365         TAILQ_FOREACH(ic, icq, ic_next) {
366                 if (!ic->ic_scanned) {
367                         event_del(&ic->ic_event);
368                         close(ic->ic_fd);
369                         close(ic->ic_dirfd);
370                         TAILQ_REMOVE(icq, ic, ic_next);
371                         if (verbose > 0) {
372                                 warnx("Stale client: %s", ic->ic_clid);
373                                 warnx("\t-> closed %s", ic->ic_path);
374                         }
375                         free(ic);
376                 } else
377                         ic->ic_scanned = 0;
378         }
379         return;
380 }
381
382 static void
383 svrreopen(int fd, short which, void *data)
384 {
385         nfsdreopen();
386 }
387
388 static void
389 clntscancb(int fd, short which, void *data)
390 {
391         struct idmap_clientq *icq = data;
392         struct idmap_client *ic;
393
394         TAILQ_FOREACH(ic, icq, ic_next)
395                 if (ic->ic_fd == -1 && nfsopen(ic) == -1) {
396                         close(ic->ic_dirfd);
397                         TAILQ_REMOVE(icq, ic, ic_next);
398                         free(ic);
399                 }
400 }
401
402 static void
403 nfsdcb(int fd, short which, void *data)
404 {
405         struct idmap_client *ic = data;
406         struct idmap_msg im;
407         u_char buf[IDMAP_MAXMSGSZ + 1];
408         size_t len, bsiz;
409         char *bp, typebuf[IDMAP_MAXMSGSZ],
410                 buf1[IDMAP_MAXMSGSZ], authbuf[IDMAP_MAXMSGSZ], *p;
411
412         if (which != EV_READ)
413                 goto out;
414
415         if ((len = read(ic->ic_fd, buf, sizeof(buf))) == -1) {
416                 warnx("nfsdcb: read(%s) failed: errno %d (%s)",
417                         ic->ic_path, errno, strerror(errno));
418                 goto out;
419         }
420
421         /* Get rid of newline and terminate buffer*/
422         buf[len - 1] = '\0';
423         bp = buf;
424
425         memset(&im, 0, sizeof(im));
426
427         /* Authentication name -- ignored for now*/
428         if (getfield(&bp, authbuf, sizeof(authbuf)) == -1) {
429                 warnx("nfsdcb: bad authentication name in upcall\n");
430                 return;
431         }
432         if (getfield(&bp, typebuf, sizeof(typebuf)) == -1) {
433                 warnx("nfsdcb: bad type in upcall\n");
434                 return;
435         }
436         if (verbose > 0)
437                 warnx("nfsdcb: authbuf=%s authtype=%s", authbuf, typebuf);
438
439         im.im_type = strcmp(typebuf, "user") == 0 ?
440                 IDMAP_TYPE_USER : IDMAP_TYPE_GROUP;
441
442         switch (ic->ic_which) {
443         case IC_NAMEID:
444                 im.im_conv = IDMAP_CONV_NAMETOID;
445                 if (getfield(&bp, im.im_name, sizeof(im.im_name)) == -1) {
446                         warnx("nfsdcb: bad name in upcall\n");
447                         return;
448                 }
449                 break;
450         case IC_IDNAME:
451                 im.im_conv = IDMAP_CONV_IDTONAME;
452                 if (getfield(&bp, buf1, sizeof(buf1)) == -1) {
453                         warnx("nfsdcb: bad id in upcall\n");
454                         return;
455                 }
456                 if ((im.im_id = strtoul(buf1, (char **)NULL, 10)) == ULONG_MAX &&
457                     errno == ERANGE) {
458                         warnx("nfsdcb: id '%s' too big!\n", buf1);
459                         return;
460                 }
461
462                 break;
463         default:
464                 warnx("Unknown which type %d", ic->ic_which);
465                 return;
466         }
467
468         imconv(ic, &im);
469
470         buf[0] = '\0';
471         bp = buf;
472         bsiz = sizeof(buf);
473
474         /* Authentication name */
475         addfield(&bp, &bsiz, authbuf);
476
477         switch (ic->ic_which) {
478         case IC_NAMEID:
479                 /* Type */
480                 p = im.im_type == IDMAP_TYPE_USER ? "user" : "group";
481                 addfield(&bp, &bsiz, p);
482                 /* Name */
483                 addfield(&bp, &bsiz, im.im_name);
484 #define NFSD_EXPIRY 300 /* seconds */
485                 /* expiry */
486                 snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + NFSD_EXPIRY);
487                 addfield(&bp, &bsiz, buf1);
488                 /* ID */
489                 snprintf(buf1, sizeof(buf1), "%u", im.im_id);
490                 addfield(&bp, &bsiz, buf1);
491
492                 //if (bsiz == sizeof(buf)) /* XXX */
493
494                 bp[-1] = '\n';
495
496                 break;
497         case IC_IDNAME:
498                 /* Type */
499                 p = im.im_type == IDMAP_TYPE_USER ? "user" : "group";
500                 addfield(&bp, &bsiz, p);
501                 /* ID */
502                 snprintf(buf1, sizeof(buf1), "%u", im.im_id);
503                 addfield(&bp, &bsiz, buf1);
504                 /* expiry */
505                 snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + NFSD_EXPIRY);
506                 addfield(&bp, &bsiz, buf1);
507                 /* Name */
508                 addfield(&bp, &bsiz, im.im_name);
509
510                 bp[-1] = '\n';
511
512                 break;
513         default:
514                 warnx("Unknown which type %d", ic->ic_which);
515                 return;
516         }
517
518         bsiz = sizeof(buf) - bsiz;
519
520         if (atomicio(write, ic->ic_fd, buf, bsiz) != bsiz)
521                 warnx("nfsdcb: write(%s) failed: errno %d (%s)",
522                         ic->ic_path, errno, strerror(errno));
523
524 out:
525         event_add(&ic->ic_event, NULL);
526 }
527
528 static void
529 imconv(struct idmap_client *ic, struct idmap_msg *im)
530 {
531         switch (im->im_conv) {
532         case IDMAP_CONV_IDTONAME:
533                 idtonameres(im);
534                 if (verbose > 1)
535                         warnx("%s %s: (%s) id \"%d\" -> name \"%s\"",
536                             ic->ic_id, ic->ic_clid,
537                             im->im_type == IDMAP_TYPE_USER ? "user" : "group",
538                             im->im_id, im->im_name);
539                 break;
540         case IDMAP_CONV_NAMETOID:
541                 if (validateascii(im->im_name, sizeof(im->im_name)) == -1) {
542                         im->im_status |= IDMAP_STATUS_INVALIDMSG;
543                         return;
544                 }
545                 nametoidres(im);
546                 if (verbose > 1)
547                         warnx("%s %s: (%s) name \"%s\" -> id \"%d\"",
548                             ic->ic_id, ic->ic_clid,
549                             im->im_type == IDMAP_TYPE_USER ? "user" : "group",
550                             im->im_name, im->im_id);
551                 break;
552         default:
553                 warnx("Invalid conversion type (%d) in message", im->im_conv);
554                 im->im_status |= IDMAP_STATUS_INVALIDMSG;
555                 break;
556         }
557 }
558
559 static void
560 nfscb(int fd, short which, void *data)
561 {
562         struct idmap_client *ic = data;
563         struct idmap_msg im;
564
565         if (which != EV_READ)
566                 goto out;
567
568         if (atomicio(read, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) {
569                 if (verbose > 0)
570                         warn("read(%s)", ic->ic_path);
571                 if (errno == EPIPE)
572                         return;
573                 goto out;
574         }
575
576         imconv(ic, &im);
577
578         if (atomicio(write, ic->ic_fd, &im, sizeof(im)) != sizeof(im))
579                 warn("write(%s)", ic->ic_path);
580 out:
581         event_add(&ic->ic_event, NULL);
582 }
583
584 static void
585 nfsdreopen_one(struct idmap_client *ic)
586 {
587         int fd;
588
589         if (verbose > 0)
590                 warnx("ReOpening %s", ic->ic_path);
591         if ((fd = open(ic->ic_path, O_RDWR, 0)) != -1) {
592                 if (ic->ic_fd != -1)
593                         close(ic->ic_fd);
594                 ic->ic_event.ev_fd = ic->ic_fd = fd;
595                 if ((ic->ic_event.ev_flags & EVLIST_INIT) == 0) {
596                         event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic);
597                         event_add(&ic->ic_event, NULL);
598                 }
599         } else {
600                 warnx("nfsdreopen: Opening '%s' failed: errno %d (%s)",
601                         ic->ic_path, errno, strerror(errno));
602         }
603 }
604
605 /*
606  * Note: nfsdreopen assumes nfsdopen has already been called
607  */
608 static void
609 nfsdreopen()
610 {
611         nfsdreopen_one(&nfsd_ic[IC_NAMEID]);
612         nfsdreopen_one(&nfsd_ic[IC_IDNAME]);
613         return;
614 }
615
616 static int
617 nfsdopen(char *path)
618 {
619         return ((nfsdopenone(&nfsd_ic[0], IC_NAMEID, path) == 0 &&
620                     nfsdopenone(&nfsd_ic[1], IC_IDNAME, path) == 0) ? 0 : -1);
621 }
622
623 static int
624 nfsdopenone(struct idmap_client *ic, short which, char *path)
625 {
626         char *whichstr;
627
628         whichstr = which == IC_IDNAME ? "idtoname" : "nametoid";
629         snprintf(ic->ic_path, sizeof(ic->ic_path),
630                 "%s/nfs4.%s/channel", path, whichstr);
631         if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) {
632                 if (verbose > 0)
633                         warnx("Opening %s failed: errno %d (%s)",
634                                 ic->ic_path, errno, strerror(errno));
635                 return (-1);
636         }
637
638         event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic);
639         event_add(&ic->ic_event, NULL);
640
641         ic->ic_which = which;
642         ic->ic_id = "Server";
643         strlcpy(ic->ic_clid, domain, sizeof(ic->ic_clid));
644
645         if (verbose > 0)
646                 warnx("Opened %s", ic->ic_path);
647
648         return (0);
649 }
650
651 static int
652 nfsopen(struct idmap_client *ic)
653 {
654         if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) {
655                 switch (errno) {
656                 case ENOENT:
657                         fcntl(ic->ic_dirfd, F_SETSIG, SIGUSR2);
658                         fcntl(ic->ic_dirfd, F_NOTIFY,
659                             DN_CREATE | DN_DELETE | DN_MULTISHOT);
660                         break;
661                 default:
662                         warn("open(%s)", ic->ic_path);
663                         return (-1);
664                 }
665         } else {
666                 event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfscb, ic);
667                 event_add(&ic->ic_event, NULL);
668                 fcntl(ic->ic_dirfd, F_SETSIG, 0);
669                 fcntl(ic->ic_dirfd, F_NOTIFY, 0);
670                 if (verbose > 0)
671                         warnx("Opened %s", ic->ic_path);
672         }
673
674         return (0);
675 }
676
677 static int write_name(char *dest, char *localname, char *domain, size_t len)
678 {
679         if (strlen(localname) + 1 + strlen(domain) + 1 > len) {
680                 return -ENOMEM; /* XXX: Is there an -ETOOLONG? */
681         }
682         strcpy(dest, localname);
683         strcat(dest, "@");
684         strcat(dest, domain);
685         return 0;
686 }
687
688 static void
689 idtonameres(struct idmap_msg *im)
690 {
691         int ret = 0;
692
693         switch (im->im_type) {
694         case IDMAP_TYPE_USER:
695                 ret = nfs4_uid_to_name(im->im_id, domain, im->im_name,
696                                 sizeof(im->im_name));
697                 if (ret)
698                         write_name(im->im_name, nobodyuser, domain,
699                                         sizeof(im->im_name));
700                 break;
701         case IDMAP_TYPE_GROUP:
702                 ret = nfs4_gid_to_name(im->im_id, domain, im->im_name,
703                                 sizeof(im->im_name));
704                 if (ret)
705                         write_name(im->im_name, nobodygroup, domain,
706                                         sizeof(im->im_name));
707                 break;
708         }
709         /* XXX Hack? would rather return failure instead of writing nobody
710          * as above, but kernel seems not to deal well with that as of
711          * 2.6.8-rc3. */
712         im->im_status = IDMAP_STATUS_SUCCESS;
713 }
714
715 static void
716 nametoidres(struct idmap_msg *im)
717 {
718         int ret = 0;
719
720         switch (im->im_type) {
721         case IDMAP_TYPE_USER:
722                 ret = nfs4_name_to_uid(im->im_name, &im->im_id);
723                 if (ret)
724                         im->im_id = nobodyuid;
725                 break;
726         case IDMAP_TYPE_GROUP:
727                 ret = nfs4_name_to_gid(im->im_name, &im->im_id);
728                 if (ret)
729                         im->im_id = nobodygid;
730                 break;
731         }
732         /* XXX Hack? would rather return failure instead of writing nobody
733          * as above, but kernel seems not to deal well with that as of
734          * 2.6.8-rc3. */
735         im->im_status = IDMAP_STATUS_SUCCESS;
736 }
737
738 static int
739 validateascii(char *string, u_int32_t len)
740 {
741         int i;
742
743         for (i = 0; i < len; i++) {
744                 if (string[i] == '\0')
745                         break;
746
747                 if (string[i] & 0x80)
748                         return (-1);
749         }
750
751         if (string[i] != '\0')
752                 return (-1);
753
754         return (i + 1);
755 }
756
757 static int
758 addfield(char **bpp, ssize_t *bsizp, char *fld)
759 {
760         char ch, *bp = *bpp;
761         ssize_t bsiz = *bsizp;
762
763         while ((ch = *fld++) != '\0' && bsiz > 0) {
764                 switch(ch) {
765                 case ' ':
766                 case '\t':
767                 case '\n':
768                 case '\\':
769                         if (bsiz >= 4) {
770                                 bp += snprintf(bp, bsiz, "\\%03o", ch);
771                                 bsiz -= 4;
772                         }
773                         break;
774                 default:
775                         *bp++ = ch;
776                         bsiz--;
777                         break;
778                 }
779         }
780
781         if (bsiz < 1 || ch != '\0')
782                 return (-1);
783
784         *bp++ = ' ';
785         bsiz--;
786
787         *bpp = bp;
788         *bsizp = bsiz;
789
790         return (0);
791 }
792
793 static int
794 getfield(char **bpp, char *fld, size_t fldsz)
795 {
796         char *bp;
797         u_int val, n;
798
799         while ((bp = strsep(bpp, " ")) != NULL && bp[0] == '\0')
800                 ;
801
802         if (bp == NULL || bp[0] == '\0' || bp[0] == '\n')
803                 return (-1);
804
805         while (*bp != '\0' && fldsz > 1) {
806                 if (*bp == '\\') {
807                         if ((n = sscanf(bp, "\\%03o", &val)) != 1)
808                                 return (-1);
809                         if (val > (char)-1)
810                                 return (-1);
811                         *fld++ = (char)val;
812                         bp += 4;
813                 } else {
814                         *fld++ = *bp;
815                         bp++;
816                 }
817                 fldsz--;
818         }
819
820         if (*bp != '\0')
821                 return (-1);
822         *fld = '\0';
823
824         return (0);
825 }
826 /*
827  * mydaemon creates a pipe between the partent and child
828  * process. The parent process will wait until the
829  * child dies or writes a '1' on the pipe signaling
830  * that it started successfully.
831  */
832 int pipefds[2] = { -1, -1};
833
834 void
835 mydaemon(int nochdir, int noclose)
836 {
837         int pid, status, tempfd, fdmax, filedes;
838
839         if (pipe(pipefds) < 0)
840                 err(1, "mydaemon: pipe() failed: errno %d (%s)\n", errno, strerror(errno));
841
842         if ((pid = fork ()) < 0)
843                 err(1, "mydaemon: fork() failed: errno %d (%s)\n", errno, strerror(errno));
844
845         if (pid != 0) {
846                 /*
847                  * Parent. Wait for status from child.
848                  */
849                 close(pipefds[1]);
850                 if (read(pipefds[0], &status, 1) != 1)
851                         exit(1);
852                 exit (0);
853         }
854         /* Child.       */
855         close(pipefds[0]);
856         setsid ();
857         if (nochdir == 0) {
858                 if (chdir ("/") == -1)
859                         err(1, "mydaemon: chdir() failed: errno %d (%s)\n", errno, strerror(errno));
860         }
861
862         while (pipefds[1] <= 2) {
863                 pipefds[1] = dup(pipefds[1]);
864                 if (pipefds[1] < 0)
865                         err(1, "mydaemon: dup() failed: errno %d (%s)\n", errno, strerror(errno));
866         }
867
868         if (noclose == 0) {
869                 tempfd = open("/dev/null", O_RDWR);
870                 close(0); dup2(tempfd, 0);
871                 close(1); dup2(tempfd, 1);
872                 close(2); dup2(tempfd, 2);
873                 fdmax = sysconf (_SC_OPEN_MAX);
874                 for (filedes = 3; filedes < fdmax; filedes++)
875                         if (filedes != pipefds[1])
876                                 close (filedes);
877         }
878
879         return;
880 }
881 void
882 release_parent()
883 {
884         int status;
885
886         if (pipefds[1] > 0) {
887                 write(pipefds[1], &status, 1);
888                 close(pipefds[1]);
889                 pipefds[1] = -1;
890         }
891 }