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