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