2005-08-26 Kevin Coffman <kwc@citi.umich.edu>
[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 pipefsdir[PATH_MAX];
136 static char *nobodyuser, *nobodygroup;
137 static uid_t nobodyuid;
138 static gid_t nobodygid;
139 static struct idmap_client nfsd_ic[2];
140
141 /* Used by cfg.c */
142 char *conf_path;
143
144 static int
145 flush_nfsd_cache(char *path, time_t now)
146 {
147         int fd;
148         char stime[20];
149
150         sprintf(stime, "%ld\n", now);
151         fd = open(path, O_RDWR);
152         if (fd == -1)
153                 return -1;
154         write(fd, stime, strlen(stime));
155         close(fd);
156         return 0;
157 }
158
159 static int
160 flush_nfsd_idmap_cache(void)
161 {
162         time_t now = time(NULL);
163         int ret;
164
165         ret = flush_nfsd_cache("/proc/net/rpc/nfs4.idtoname/flush", now);
166         if (ret)
167                 return ret;
168         ret = flush_nfsd_cache("/proc/net/rpc/nfs4.nametoid/flush", now);
169         return ret;
170 }
171
172 int
173 main(int argc, char **argv)
174 {
175         int fd = 0, opt, fg = 0, nfsdret = -1;
176         struct idmap_clientq icq;
177         struct event rootdirev, clntdirev, svrdirev;
178         struct event initialize;
179         struct passwd *pw;
180         struct group *gr;
181         struct stat sb;
182         char *xpipefsdir = NULL;
183         int serverstart = 1, clientstart = 1;
184         int ret;
185
186         conf_path = _PATH_IDMAPDCONF;
187         nobodyuser = NFS4NOBODY_USER;
188         nobodygroup = NFS4NOBODY_GROUP;
189         strlcpy(pipefsdir, PIPEFS_DIR, sizeof(pipefsdir));
190
191 #define GETOPTSTR "vfd:p:U:G:c:CS"
192         opterr=0; /* Turn off error messages */
193         while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) {
194                 if (opt == 'c')
195                         conf_path = optarg;
196                 if (opt == '?') {
197                         if (strchr(GETOPTSTR, optopt))
198                                 errx(1, "'-%c' option requires an argument.", optopt);
199                         else
200                                 errx(1, "'-%c' is an invalid argument.", optopt);
201                 }
202         }
203         optind = 1;
204
205         if (stat(conf_path, &sb) == -1 && (errno == ENOENT || errno == EACCES)) {
206                 warn("Skipping configuration file \"%s\"", conf_path);
207         } else {
208                 conf_init();
209                 verbose = conf_get_num("General", "Verbosity", 0);
210                 CONF_SAVE(xpipefsdir, conf_get_str("General", "Pipefs-Directory"));
211                 if (xpipefsdir != NULL)
212                         strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir));
213                 CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User"));
214                 CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group"));
215                 nfs4_init_name_mapping(conf_path);
216         }
217
218         while ((opt = getopt(argc, argv, GETOPTSTR)) != -1)
219                 switch (opt) {
220                 case 'v':
221                         verbose++;
222                         break;
223                 case 'f':
224                         fg = 1;
225                         break;
226                 case 'p':
227                         strlcpy(pipefsdir, optarg, sizeof(pipefsdir));
228                         break;
229                 case 'd':
230                 case 'U':
231                 case 'G':
232                         errx(1, "the -d, -U, and -G options have been removed;"
233                                 " please use the configuration file instead.");
234                 case 'C':
235                         serverstart = 0;
236                         break;
237                 case 'S':
238                         clientstart = 0;
239                         break;
240                 default:
241                         break;
242                 }
243
244         if (!serverstart && !clientstart)
245                 errx(1, "it is illegal to specify both -C and -S");
246
247         strncat(pipefsdir, "/nfs", sizeof(pipefsdir));
248
249         if ((pw = getpwnam(nobodyuser)) == NULL)
250                 errx(1, "Could not find user \"%s\"", nobodyuser);
251         nobodyuid = pw->pw_uid;
252
253         if ((gr = getgrnam(nobodygroup)) == NULL)
254                 errx(1, "Could not find group \"%s\"", nobodygroup);
255         nobodygid = gr->gr_gid;
256
257         if (!fg)
258                 mydaemon(0, 0);
259
260         event_init();
261
262         if (serverstart) {
263                 nfsdret = nfsdopen(NFSD_DIR);
264                 if (nfsdret == 0) {
265                         ret = flush_nfsd_idmap_cache();
266                         if (ret)
267                                 errx(1, "Failed to flush nfsd idmap cache\n");
268                 }
269         }
270
271         if (clientstart) {
272                 struct timeval now = {
273                         .tv_sec = 0,
274                         .tv_usec = 0,
275                 };
276
277                 if ((fd = open(pipefsdir, O_RDONLY)) == -1)
278                         err(1, "open(%s)", pipefsdir);
279
280                 if (fcntl(fd, F_SETSIG, SIGUSR1) == -1)
281                         err(1, "fcntl(%s)", pipefsdir);
282                 if (fcntl(fd, F_NOTIFY,
283                         DN_CREATE | DN_DELETE | DN_MODIFY | DN_MULTISHOT) == -1)
284                         err(1, "fcntl(%s)", pipefsdir);
285
286                 TAILQ_INIT(&icq);
287
288                 /* These events are persistent */
289                 signal_set(&rootdirev, SIGUSR1, dirscancb, &icq);
290                 signal_add(&rootdirev, NULL);
291                 signal_set(&clntdirev, SIGUSR2, clntscancb, &icq);
292                 signal_add(&clntdirev, NULL);
293                 signal_set(&svrdirev, SIGHUP, svrreopen, NULL);
294                 signal_add(&svrdirev, NULL);
295
296                 /* Fetch current state */
297                 /* (Delay till start of event_dispatch to avoid possibly losing
298                  * a SIGUSR1 between here and the call to event_dispatch().) */
299                 evtimer_set(&initialize, dirscancb, &icq);
300                 evtimer_add(&initialize, &now);
301         }
302
303         if (nfsdret != 0 && fd == 0)
304                 errx(1, "Neither NFS client nor NFSd found");
305
306         release_parent();
307
308         if (event_dispatch() < 0)
309                 errx(1, "event_dispatch: returns errno %d (%s)", errno, strerror(errno));
310         /* NOTREACHED */
311         return 1;
312 }
313
314 static void
315 dirscancb(int fd, short which, void *data)
316 {
317         int nent, i;
318         struct dirent **ents;
319         struct idmap_client *ic;
320         char path[PATH_MAX];
321         struct idmap_clientq *icq = data;
322
323         nent = scandir(pipefsdir, &ents, NULL, alphasort);
324         if (nent == -1) {
325                 warn("scandir(%s)", pipefsdir);
326                 return;
327         }
328
329         for (i = 0;  i < nent; i++) {
330                 if (ents[i]->d_reclen > 4 &&
331                     strncmp(ents[i]->d_name, "clnt", 4) == 0) {
332                         TAILQ_FOREACH(ic, icq, ic_next)
333                             if (strcmp(ents[i]->d_name + 4, ic->ic_clid) == 0)
334                                     break;
335                         if (ic != NULL)
336                                 goto next;
337
338                         if ((ic = calloc(1, sizeof(*ic))) == NULL)
339                                 return;
340                         strlcpy(ic->ic_clid, ents[i]->d_name + 4,
341                             sizeof(ic->ic_clid));
342                         path[0] = '\0';
343                         snprintf(path, sizeof(path), "%s/%s",
344                             pipefsdir, ents[i]->d_name);
345
346                         if ((ic->ic_dirfd = open(path, O_RDONLY, 0)) == -1) {
347                                 warn("open(%s)", path);
348                                 free(ic);
349                                 return;
350                         }
351
352                         strlcat(path, "/idmap", sizeof(path));
353                         strlcpy(ic->ic_path, path, sizeof(ic->ic_path));
354
355                         if (verbose > 0)
356                                 warnx("New client: %s", ic->ic_clid);
357
358                         if (nfsopen(ic) == -1) {
359                                 close(ic->ic_dirfd);
360                                 free(ic);
361                                 return;
362                         }
363
364                         ic->ic_id = "Client";
365
366                         TAILQ_INSERT_TAIL(icq, ic, ic_next);
367
368                 next:
369                         ic->ic_scanned = 1;
370                 }
371         }
372
373         TAILQ_FOREACH(ic, icq, ic_next) {
374                 if (!ic->ic_scanned) {
375                         event_del(&ic->ic_event);
376                         close(ic->ic_fd);
377                         close(ic->ic_dirfd);
378                         TAILQ_REMOVE(icq, ic, ic_next);
379                         if (verbose > 0) {
380                                 warnx("Stale client: %s", ic->ic_clid);
381                                 warnx("\t-> closed %s", ic->ic_path);
382                         }
383                         free(ic);
384                 } else
385                         ic->ic_scanned = 0;
386         }
387         return;
388 }
389
390 static void
391 svrreopen(int fd, short which, void *data)
392 {
393         nfsdreopen();
394 }
395
396 static void
397 clntscancb(int fd, short which, void *data)
398 {
399         struct idmap_clientq *icq = data;
400         struct idmap_client *ic;
401
402         TAILQ_FOREACH(ic, icq, ic_next)
403                 if (ic->ic_fd == -1 && nfsopen(ic) == -1) {
404                         close(ic->ic_dirfd);
405                         TAILQ_REMOVE(icq, ic, ic_next);
406                         free(ic);
407                 }
408 }
409
410 static void
411 nfsdcb(int fd, short which, void *data)
412 {
413         struct idmap_client *ic = data;
414         struct idmap_msg im;
415         u_char buf[IDMAP_MAXMSGSZ + 1];
416         size_t len, bsiz;
417         char *bp, typebuf[IDMAP_MAXMSGSZ],
418                 buf1[IDMAP_MAXMSGSZ], authbuf[IDMAP_MAXMSGSZ], *p;
419
420         if (which != EV_READ)
421                 goto out;
422
423         if ((len = read(ic->ic_fd, buf, sizeof(buf))) == -1) {
424                 warnx("nfsdcb: read(%s) failed: errno %d (%s)",
425                         ic->ic_path, errno, strerror(errno));
426                 goto out;
427         }
428
429         /* Get rid of newline and terminate buffer*/
430         buf[len - 1] = '\0';
431         bp = buf;
432
433         memset(&im, 0, sizeof(im));
434
435         /* Authentication name -- ignored for now*/
436         if (getfield(&bp, authbuf, sizeof(authbuf)) == -1) {
437                 warnx("nfsdcb: bad authentication name in upcall\n");
438                 return;
439         }
440         if (getfield(&bp, typebuf, sizeof(typebuf)) == -1) {
441                 warnx("nfsdcb: bad type in upcall\n");
442                 return;
443         }
444         if (verbose > 0)
445                 warnx("nfsdcb: authbuf=%s authtype=%s", authbuf, typebuf);
446
447         im.im_type = strcmp(typebuf, "user") == 0 ?
448                 IDMAP_TYPE_USER : IDMAP_TYPE_GROUP;
449
450         switch (ic->ic_which) {
451         case IC_NAMEID:
452                 im.im_conv = IDMAP_CONV_NAMETOID;
453                 if (getfield(&bp, im.im_name, sizeof(im.im_name)) == -1) {
454                         warnx("nfsdcb: bad name in upcall\n");
455                         return;
456                 }
457                 break;
458         case IC_IDNAME:
459                 im.im_conv = IDMAP_CONV_IDTONAME;
460                 if (getfield(&bp, buf1, sizeof(buf1)) == -1) {
461                         warnx("nfsdcb: bad id in upcall\n");
462                         return;
463                 }
464                 if ((im.im_id = strtoul(buf1, (char **)NULL, 10)) == ULONG_MAX &&
465                     errno == ERANGE) {
466                         warnx("nfsdcb: id '%s' too big!\n", buf1);
467                         return;
468                 }
469
470                 break;
471         default:
472                 warnx("Unknown which type %d", ic->ic_which);
473                 return;
474         }
475
476         imconv(ic, &im);
477
478         buf[0] = '\0';
479         bp = buf;
480         bsiz = sizeof(buf);
481
482         /* Authentication name */
483         addfield(&bp, &bsiz, authbuf);
484
485         switch (ic->ic_which) {
486         case IC_NAMEID:
487                 /* Type */
488                 p = im.im_type == IDMAP_TYPE_USER ? "user" : "group";
489                 addfield(&bp, &bsiz, p);
490                 /* Name */
491                 addfield(&bp, &bsiz, im.im_name);
492 #define NFSD_EXPIRY 300 /* seconds */
493                 /* expiry */
494                 snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + NFSD_EXPIRY);
495                 addfield(&bp, &bsiz, buf1);
496                 /* ID */
497                 snprintf(buf1, sizeof(buf1), "%u", im.im_id);
498                 addfield(&bp, &bsiz, buf1);
499
500                 //if (bsiz == sizeof(buf)) /* XXX */
501
502                 bp[-1] = '\n';
503
504                 break;
505         case IC_IDNAME:
506                 /* Type */
507                 p = im.im_type == IDMAP_TYPE_USER ? "user" : "group";
508                 addfield(&bp, &bsiz, p);
509                 /* ID */
510                 snprintf(buf1, sizeof(buf1), "%u", im.im_id);
511                 addfield(&bp, &bsiz, buf1);
512                 /* expiry */
513                 snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + NFSD_EXPIRY);
514                 addfield(&bp, &bsiz, buf1);
515                 /* Name */
516                 addfield(&bp, &bsiz, im.im_name);
517
518                 bp[-1] = '\n';
519
520                 break;
521         default:
522                 warnx("Unknown which type %d", ic->ic_which);
523                 return;
524         }
525
526         bsiz = sizeof(buf) - bsiz;
527
528         if (atomicio(write, ic->ic_fd, buf, bsiz) != bsiz)
529                 warnx("nfsdcb: write(%s) failed: errno %d (%s)",
530                         ic->ic_path, errno, strerror(errno));
531
532 out:
533         event_add(&ic->ic_event, NULL);
534 }
535
536 static void
537 imconv(struct idmap_client *ic, struct idmap_msg *im)
538 {
539         switch (im->im_conv) {
540         case IDMAP_CONV_IDTONAME:
541                 idtonameres(im);
542                 if (verbose > 1)
543                         warnx("%s %s: (%s) id \"%d\" -> name \"%s\"",
544                             ic->ic_id, ic->ic_clid,
545                             im->im_type == IDMAP_TYPE_USER ? "user" : "group",
546                             im->im_id, im->im_name);
547                 break;
548         case IDMAP_CONV_NAMETOID:
549                 if (validateascii(im->im_name, sizeof(im->im_name)) == -1) {
550                         im->im_status |= IDMAP_STATUS_INVALIDMSG;
551                         return;
552                 }
553                 nametoidres(im);
554                 if (verbose > 1)
555                         warnx("%s %s: (%s) name \"%s\" -> id \"%d\"",
556                             ic->ic_id, ic->ic_clid,
557                             im->im_type == IDMAP_TYPE_USER ? "user" : "group",
558                             im->im_name, im->im_id);
559                 break;
560         default:
561                 warnx("Invalid conversion type (%d) in message", im->im_conv);
562                 im->im_status |= IDMAP_STATUS_INVALIDMSG;
563                 break;
564         }
565 }
566
567 static void
568 nfscb(int fd, short which, void *data)
569 {
570         struct idmap_client *ic = data;
571         struct idmap_msg im;
572
573         if (which != EV_READ)
574                 goto out;
575
576         if (atomicio(read, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) {
577                 if (verbose > 0)
578                         warn("read(%s)", ic->ic_path);
579                 if (errno == EPIPE)
580                         return;
581                 goto out;
582         }
583
584         imconv(ic, &im);
585
586         if (atomicio(write, ic->ic_fd, &im, sizeof(im)) != sizeof(im))
587                 warn("write(%s)", ic->ic_path);
588 out:
589         event_add(&ic->ic_event, NULL);
590 }
591
592 static void
593 nfsdreopen_one(struct idmap_client *ic)
594 {
595         int fd;
596
597         if (verbose > 0)
598                 warnx("ReOpening %s", ic->ic_path);
599         if ((fd = open(ic->ic_path, O_RDWR, 0)) != -1) {
600                 if (ic->ic_fd != -1)
601                         close(ic->ic_fd);
602                 ic->ic_event.ev_fd = ic->ic_fd = fd;
603                 event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic);
604                 event_add(&ic->ic_event, NULL);
605         } else {
606                 warnx("nfsdreopen: Opening '%s' failed: errno %d (%s)",
607                         ic->ic_path, errno, strerror(errno));
608         }
609 }
610
611 /*
612  * Note: nfsdreopen assumes nfsdopen has already been called
613  */
614 static void
615 nfsdreopen()
616 {
617         nfsdreopen_one(&nfsd_ic[IC_NAMEID]);
618         nfsdreopen_one(&nfsd_ic[IC_IDNAME]);
619         return;
620 }
621
622 static int
623 nfsdopen(char *path)
624 {
625         return ((nfsdopenone(&nfsd_ic[0], IC_NAMEID, path) == 0 &&
626                     nfsdopenone(&nfsd_ic[1], IC_IDNAME, path) == 0) ? 0 : -1);
627 }
628
629 static int
630 nfsdopenone(struct idmap_client *ic, short which, char *path)
631 {
632         char *whichstr;
633
634         whichstr = which == IC_IDNAME ? "idtoname" : "nametoid";
635         snprintf(ic->ic_path, sizeof(ic->ic_path),
636                 "%s/nfs4.%s/channel", path, whichstr);
637         if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) {
638                 if (verbose > 0)
639                         warnx("Opening %s failed: errno %d (%s)",
640                                 ic->ic_path, errno, strerror(errno));
641                 return (-1);
642         }
643
644         event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic);
645         event_add(&ic->ic_event, NULL);
646
647         ic->ic_which = which;
648         ic->ic_id = "Server";
649         strlcpy(ic->ic_clid, "Server", strlen("Server"));
650
651         if (verbose > 0)
652                 warnx("Opened %s", ic->ic_path);
653
654         return (0);
655 }
656
657 static int
658 nfsopen(struct idmap_client *ic)
659 {
660         if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) {
661                 switch (errno) {
662                 case ENOENT:
663                         fcntl(ic->ic_dirfd, F_SETSIG, SIGUSR2);
664                         fcntl(ic->ic_dirfd, F_NOTIFY,
665                             DN_CREATE | DN_DELETE | DN_MULTISHOT);
666                         break;
667                 default:
668                         warn("open(%s)", ic->ic_path);
669                         return (-1);
670                 }
671         } else {
672                 event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfscb, ic);
673                 event_add(&ic->ic_event, NULL);
674                 fcntl(ic->ic_dirfd, F_SETSIG, 0);
675                 fcntl(ic->ic_dirfd, F_NOTIFY, 0);
676                 if (verbose > 0)
677                         warnx("Opened %s", ic->ic_path);
678         }
679
680         return (0);
681 }
682
683 static int write_name(char *dest, char *localname, char *domain, size_t len)
684 {
685         if (strlen(localname) + 1 + strlen(domain) + 1 > len) {
686                 return -ENOMEM; /* XXX: Is there an -ETOOLONG? */
687         }
688         strcpy(dest, localname);
689         strcat(dest, "@");
690         strcat(dest, domain);
691         return 0;
692 }
693
694 static void
695 idtonameres(struct idmap_msg *im)
696 {
697         char domain[NFS4_MAX_DOMAIN_LEN];
698         int ret = 0;
699
700         ret = nfs4_get_default_domain(NULL, domain, sizeof(domain));
701         switch (im->im_type) {
702         case IDMAP_TYPE_USER:
703                 ret = nfs4_uid_to_name(im->im_id, domain, im->im_name,
704                                 sizeof(im->im_name));
705                 if (ret)
706                         write_name(im->im_name, nobodyuser, domain,
707                                         sizeof(im->im_name));
708                 break;
709         case IDMAP_TYPE_GROUP:
710                 ret = nfs4_gid_to_name(im->im_id, domain, im->im_name,
711                                 sizeof(im->im_name));
712                 if (ret)
713                         write_name(im->im_name, nobodygroup, domain,
714                                         sizeof(im->im_name));
715                 break;
716         }
717         /* XXX Hack? */
718         im->im_status = IDMAP_STATUS_SUCCESS;
719 }
720
721 static void
722 nametoidres(struct idmap_msg *im)
723 {
724         int ret = 0;
725
726         /* XXX: nobody fallbacks shouldn't always happen:
727          *      server id -> name should be OK
728          *      client name -> id should be OK
729          * but not otherwise */
730         /* XXX: move nobody stuff to library calls
731          * (nfs4_get_nobody_user(domain), nfs4_get_nobody_group(domain)) */
732         /* XXX: should make this call higher up in the call chain (so we'd
733          * have a chance on looking up server/whatever. */
734         switch (im->im_type) {
735         case IDMAP_TYPE_USER:
736                 ret = nfs4_name_to_uid(im->im_name, &im->im_id);
737                 if (ret)
738                         im->im_id = nobodyuid;
739                 break;
740         case IDMAP_TYPE_GROUP:
741                 ret = nfs4_name_to_gid(im->im_name, &im->im_id);
742                 if (ret)
743                         im->im_id = nobodygid;
744                 break;
745         }
746         /* XXX? */
747         im->im_status = IDMAP_STATUS_SUCCESS;
748 }
749
750 static int
751 validateascii(char *string, u_int32_t len)
752 {
753         int i;
754
755         for (i = 0; i < len; i++) {
756                 if (string[i] == '\0')
757                         break;
758
759                 if (string[i] & 0x80)
760                         return (-1);
761         }
762
763         if (string[i] != '\0')
764                 return (-1);
765
766         return (i + 1);
767 }
768
769 static int
770 addfield(char **bpp, ssize_t *bsizp, char *fld)
771 {
772         char ch, *bp = *bpp;
773         ssize_t bsiz = *bsizp;
774
775         while ((ch = *fld++) != '\0' && bsiz > 0) {
776                 switch(ch) {
777                 case ' ':
778                 case '\t':
779                 case '\n':
780                 case '\\':
781                         if (bsiz >= 4) {
782                                 bp += snprintf(bp, bsiz, "\\%03o", ch);
783                                 bsiz -= 4;
784                         }
785                         break;
786                 default:
787                         *bp++ = ch;
788                         bsiz--;
789                         break;
790                 }
791         }
792
793         if (bsiz < 1 || ch != '\0')
794                 return (-1);
795
796         *bp++ = ' ';
797         bsiz--;
798
799         *bpp = bp;
800         *bsizp = bsiz;
801
802         return (0);
803 }
804
805 static int
806 getfield(char **bpp, char *fld, size_t fldsz)
807 {
808         char *bp;
809         u_int val, n;
810
811         while ((bp = strsep(bpp, " ")) != NULL && bp[0] == '\0')
812                 ;
813
814         if (bp == NULL || bp[0] == '\0' || bp[0] == '\n')
815                 return (-1);
816
817         while (*bp != '\0' && fldsz > 1) {
818                 if (*bp == '\\') {
819                         if ((n = sscanf(bp, "\\%03o", &val)) != 1)
820                                 return (-1);
821                         if (val > (char)-1)
822                                 return (-1);
823                         *fld++ = (char)val;
824                         bp += 4;
825                 } else {
826                         *fld++ = *bp;
827                         bp++;
828                 }
829                 fldsz--;
830         }
831
832         if (*bp != '\0')
833                 return (-1);
834         *fld = '\0';
835
836         return (0);
837 }
838 /*
839  * mydaemon creates a pipe between the partent and child
840  * process. The parent process will wait until the
841  * child dies or writes a '1' on the pipe signaling
842  * that it started successfully.
843  */
844 int pipefds[2] = { -1, -1};
845
846 void
847 mydaemon(int nochdir, int noclose)
848 {
849         int pid, status, tempfd, fdmax, filedes;
850
851         if (pipe(pipefds) < 0)
852                 err(1, "mydaemon: pipe() failed: errno %d (%s)\n", errno, strerror(errno));
853
854         if ((pid = fork ()) < 0)
855                 err(1, "mydaemon: fork() failed: errno %d (%s)\n", errno, strerror(errno));
856
857         if (pid != 0) {
858                 /*
859                  * Parent. Wait for status from child.
860                  */
861                 close(pipefds[1]);
862                 if (read(pipefds[0], &status, 1) != 1)
863                         exit(1);
864                 exit (0);
865         }
866         /* Child.       */
867         close(pipefds[0]);
868         setsid ();
869         if (nochdir == 0) {
870                 if (chdir ("/") == -1)
871                         err(1, "mydaemon: chdir() failed: errno %d (%s)\n", errno, strerror(errno));
872         }
873
874         while (pipefds[1] <= 2) {
875                 pipefds[1] = dup(pipefds[1]);
876                 if (pipefds[1] < 0)
877                         err(1, "mydaemon: dup() failed: errno %d (%s)\n", errno, strerror(errno));
878         }
879
880         if (noclose == 0) {
881                 tempfd = open("/dev/null", O_RDWR);
882                 close(0); dup2(tempfd, 0);
883                 close(1); dup2(tempfd, 1);
884                 close(2); dup2(tempfd, 2);
885                 fdmax = sysconf (_SC_OPEN_MAX);
886                 for (filedes = 3; filedes < fdmax; filedes++)
887                         if (filedes != pipefds[1])
888                                 close (filedes);
889         }
890
891         return;
892 }
893 void
894 release_parent()
895 {
896         int status;
897
898         if (pipefds[1] > 0) {
899                 write(pipefds[1], &status, 1);
900                 close(pipefds[1]);
901                 pipefds[1] = -1;
902         }
903 }