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