Whamcloud - gitweb
branch: HEAD
[fs/lustre-release.git] / lustre / utils / gss / lsupport.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Copyright (c) 2005 Cluster File Systems, Inc.
5  *
6  *   This file is part of Lustre, http://www.lustre.org.
7  *
8  *   Lustre is free software; you can redistribute it and/or
9  *   modify it under the terms of version 2 of the GNU General Public
10  *   License as published by the Free Software Foundation.
11  *
12  *   Lustre is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with Lustre; if not, write to the Free Software
19  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 #ifndef _GNU_SOURCE
23 #define _GNU_SOURCE
24 #endif
25 #include "config.h"
26 #include <sys/param.h>
27 #include <sys/utsname.h>
28 #include <sys/stat.h>
29 #include <sys/socket.h>
30 #include <arpa/inet.h>
31 #include <sys/types.h>
32 #include <sys/ipc.h>
33 #include <sys/sem.h>
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <pwd.h>
38 #include <grp.h>
39 #include <string.h>
40 #include <dirent.h>
41 #include <poll.h>
42 #include <fcntl.h>
43 #include <signal.h>
44 #include <unistd.h>
45 #include <errno.h>
46 #include <assert.h>
47 #ifdef HAVE_GETHOSTBYNAME
48 # include <netdb.h>
49 #endif
50
51 #ifdef _NEW_BUILD_
52 # include "lgss_utils.h"
53 #else
54 # include "err_util.h"
55 # include "gssd.h"
56 #endif
57 #include "lsupport.h"
58
59 /****************************************
60  * exclusive startup                    *
61  ****************************************/
62
63 static struct __sem_s {
64         char           *name;
65         key_t           sem_key;
66         int             sem_id;
67 } sems[2] = {
68         [GSSD_CLI] = { "client",  0x3a92d473, 0 },
69         [GSSD_SVC] = { "server",  0x3b92d473, 0 },
70 };
71
72 void gssd_init_unique(int type)
73 {
74         struct __sem_s *sem = &sems[type];
75         struct sembuf   sembuf;
76
77         assert(type == GSSD_CLI || type == GSSD_SVC);
78
79 again:
80         sem->sem_id = semget(sem->sem_key, 1, IPC_CREAT | IPC_EXCL | 0700);
81         if (sem->sem_id == -1) {
82                 if (errno != EEXIST) {
83                         printerr(0, "Create sem: %s\n", strerror(errno));
84                         exit(-1);
85                 }
86
87                 /* already exist. Note there's still a small window racing
88                  * with other processes, due to the stupid semaphore semantics.
89                  */
90                 sem->sem_id = semget(sem->sem_key, 0, 0700);
91                 if (sem->sem_id == -1) {
92                         if (errno == ENOENT) {
93                                 printerr(0, "another instance just exit, "
94                                          "try again\n");
95                                 goto again;
96                         }
97
98                         printerr(0, "Obtain sem: %s\n", strerror(errno));
99                         exit(-1);
100                 }
101         } else {
102                 int val = 1;
103
104                 if (semctl(sem->sem_id, 0, SETVAL, val) == -1) {
105                         printerr(0, "Initialize sem: %s\n",
106                                  strerror(errno));
107                         exit(-1);
108                 }
109         }
110
111         sembuf.sem_num = 0;
112         sembuf.sem_op = -1;
113         sembuf.sem_flg = IPC_NOWAIT | SEM_UNDO;
114
115         if (semop(sem->sem_id, &sembuf, 1) != 0) {
116                 if (errno == EAGAIN) {
117                         printerr(0, "Another instance is running, exit\n");
118                         exit(0);
119                 }
120                 printerr(0, "Grab sem: %s\n", strerror(errno));
121                 exit(0);
122         }
123
124         printerr(2, "Successfully created %s global identity\n", sem->name);
125 }
126
127 void gssd_exit_unique(int type)
128 {
129         assert(type == GSSD_CLI || type == GSSD_SVC);
130
131         /*
132          * do nothing. we can't remove the sem here, otherwise the race
133          * window would be much bigger. So it's sad we have to leave the
134          * sem in the system forever.
135          */
136 }
137
138 /****************************************
139  * client side resolvation:             *
140  *    lnd/netid/nid => hostname         *
141  ****************************************/
142
143 char gethostname_ex[PATH_MAX] = GSSD_DEFAULT_GETHOSTNAME_EX;
144
145 typedef int lnd_nid2hostname_t(char *lnd, uint32_t net, uint32_t addr,
146                                char *buf, int buflen);
147
148 /* FIXME what about IPv6? */
149 static
150 int ipv4_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
151                       char *buf, int buflen)
152 {
153         struct hostent  *ent;
154
155         addr = htonl(addr);
156         ent = gethostbyaddr(&addr, sizeof(addr), AF_INET);
157         if (!ent) {
158                 printerr(0, "%s: can't resolve 0x%x\n", lnd, addr);
159                 return -1;
160         }
161         if (strlen(ent->h_name) >= buflen) {
162                 printerr(0, "%s: name too long: %s\n", lnd, ent->h_name);
163                 return -1;
164         }
165         strcpy(buf, ent->h_name);
166
167         printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
168                  lnd, net, addr, buf);
169         return 0;
170 }
171
172 static
173 int lolnd_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
174                        char *buf, int buflen)
175 {
176         struct utsname   uts;
177         struct hostent  *ent;
178
179         if (addr) {
180                 printerr(0, "%s: addr is 0x%x, we expect 0\n", lnd, addr);
181                 return -1;
182         }
183
184         if (uname(&uts)) {
185                 printerr(0, "%s: failed obtain local machine name\n", lnd);
186                 return -1;
187         }
188
189         ent = gethostbyname(uts.nodename);
190         if (!ent) {
191                 printerr(0, "%s: failed obtain canonical name of %s\n",
192                          lnd, uts.nodename);
193                 return -1;
194         }
195
196         if (strlen(ent->h_name) >= buflen) {
197                 printerr(0, "%s: name too long: %s\n", lnd, ent->h_name);
198                 return -1;
199         }
200         strcpy(buf, ent->h_name);
201
202         printerr(3, "%s: addr 0x%x => %s\n", lnd, addr, buf);
203         return 0;
204 }
205
206 static int is_space(char c)
207 {
208         return (c == ' ' || c == '\t' || c == '\n');
209 }
210
211 static
212 int external_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
213                           char *namebuf, int namebuflen)
214 {
215         const int bufsize = PATH_MAX + 256;
216         char buf[bufsize], *head, *tail;
217         FILE *fghn;
218
219         sprintf(buf, "%s %s 0x%x 0x%x", gethostname_ex, lnd, net, addr);
220         printerr(2, "cmd: %s\n", buf);
221
222         fghn = popen(buf, "r");
223         if (fghn == NULL) {
224                 printerr(0, "failed to call %s\n", gethostname_ex);
225                 return -1;
226         }
227
228         head = fgets(buf, bufsize, fghn);
229         if (head == NULL) {
230                 printerr(0, "can't read from %s\n", gethostname_ex);
231                 return -1;
232         }
233         if (pclose(fghn) == -1)
234                 printerr(1, "pclose failed, continue\n");
235
236         /* trim head/tail space */
237         while (is_space(*head))
238                 head++;
239
240         tail = head + strlen(head);
241         if (tail <= head) {
242                 printerr(0, "no output from %s\n", gethostname_ex);
243                 return -1;
244         }
245         while (is_space(*(tail - 1)))
246                 tail--;
247         if (tail <= head) {
248                 printerr(0, "output are all space from %s\n", gethostname_ex);
249                 return -1;
250         }
251         *tail = '\0';
252
253         /* start with '@' means error msg */
254         if (head[0] == '@') {
255                 printerr(0, "error from %s: %s\n", gethostname_ex, &head[1]);
256                 return -1;
257         }
258
259         if (tail - head > namebuflen) {
260                 printerr(0, "external hostname too long: %s\n", head);
261                 return -1;
262         }
263
264         printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
265                  lnd, net, addr, head);
266         strcpy(namebuf, head);
267         return 0;
268 }
269
270 static struct {
271         char                    *name;
272         lnd_nid2hostname_t      *nid2name;
273 } converter[LND_ENUM_END_MARKER] = {
274         {"UNUSED0",     NULL},
275         [QSWLND]        = { "QSWLND",   external_nid2hostname},
276         [SOCKLND]       = { "SOCKLND",  ipv4_nid2hostname },
277         [GMLND]         = { "GMLND",    external_nid2hostname},
278         [PTLLND]        = { "PTLLND",   external_nid2hostname },
279         [O2IBLND]       = { "O2IBLND",  ipv4_nid2hostname },
280         [CIBLND]        = { "CIBLND",   external_nid2hostname },
281         [OPENIBLND]     = { "OPENIBLND",external_nid2hostname },
282         [IIBLND]        = { "IIBLND",   external_nid2hostname },
283         [LOLND]         = { "LOLND",    lolnd_nid2hostname },
284         [RALND]         = { "RALND",    external_nid2hostname },
285         [VIBLND]        = { "VIBLND",   external_nid2hostname },
286 };
287
288 int lnet_nid2hostname(lnet_nid_t nid, char *buf, int buflen)
289 {
290         uint32_t lnd, net, addr;
291
292         addr = LNET_NIDADDR(nid);
293         net = LNET_NIDNET(nid);
294         lnd = LNET_NETTYP(net);
295
296         if (lnd >= LND_ENUM_END_MARKER) {
297                 printerr(0, "ERROR: Unrecognized LND %u\n", lnd);
298                 return -1;
299         }
300
301         if (converter[lnd].nid2name == NULL) {
302                 printerr(0, "ERROR: %s converter not ready\n",
303                         converter[lnd].name);
304                 return -1;
305         }
306
307         return converter[lnd].nid2name(converter[lnd].name, net, addr,
308                                        buf, buflen);
309 }
310
311
312 /****************************************
313  * lnet support routine                 *
314  * (from lnet/libcfs/nidstrings.c       *
315  ****************************************/
316
317 #define LNET_NIDSTR_SIZE   32      /* size of each one (see below for usage) */
318
319 static int  libcfs_lo_str2addr(char *str, int nob, uint32_t *addr);
320 static void libcfs_ip_addr2str(uint32_t addr, char *str);
321 static int  libcfs_ip_str2addr(char *str, int nob, uint32_t *addr);
322 static void libcfs_decnum_addr2str(uint32_t addr, char *str);
323 static void libcfs_hexnum_addr2str(uint32_t addr, char *str);
324 static int  libcfs_num_str2addr(char *str, int nob, uint32_t *addr);
325
326 struct netstrfns {
327         int          nf_type;
328         char        *nf_name;
329         char        *nf_modname;
330         void       (*nf_addr2str)(uint32_t addr, char *str);
331         int        (*nf_str2addr)(char *str, int nob, uint32_t *addr);
332 };
333
334 static struct netstrfns  libcfs_netstrfns[] = {
335         {/* .nf_type      */  LOLND,
336          /* .nf_name      */  "lo",
337          /* .nf_modname   */  "klolnd",
338          /* .nf_addr2str  */  libcfs_decnum_addr2str,
339          /* .nf_str2addr  */  libcfs_lo_str2addr},
340         {/* .nf_type      */  SOCKLND,
341          /* .nf_name      */  "tcp",
342          /* .nf_modname   */  "ksocklnd",
343          /* .nf_addr2str  */  libcfs_ip_addr2str,
344          /* .nf_str2addr  */  libcfs_ip_str2addr},
345         {/* .nf_type      */  O2IBLND,
346          /* .nf_name      */  "o2ib",
347          /* .nf_modname   */  "ko2iblnd",
348          /* .nf_addr2str  */  libcfs_ip_addr2str,
349          /* .nf_str2addr  */  libcfs_ip_str2addr},
350         {/* .nf_type      */  CIBLND,
351          /* .nf_name      */  "cib",
352          /* .nf_modname   */  "kciblnd",
353          /* .nf_addr2str  */  libcfs_ip_addr2str,
354          /* .nf_str2addr  */  libcfs_ip_str2addr},
355         {/* .nf_type      */  OPENIBLND,
356          /* .nf_name      */  "openib",
357          /* .nf_modname   */  "kopeniblnd",
358          /* .nf_addr2str  */  libcfs_ip_addr2str,
359          /* .nf_str2addr  */  libcfs_ip_str2addr},
360         {/* .nf_type      */  IIBLND,
361          /* .nf_name      */  "iib",
362          /* .nf_modname   */  "kiiblnd",
363          /* .nf_addr2str  */  libcfs_ip_addr2str,
364          /* .nf_str2addr  */  libcfs_ip_str2addr},
365         {/* .nf_type      */  VIBLND,
366          /* .nf_name      */  "vib",
367          /* .nf_modname   */  "kviblnd",
368          /* .nf_addr2str  */  libcfs_ip_addr2str,
369          /* .nf_str2addr  */  libcfs_ip_str2addr},
370         {/* .nf_type      */  RALND,
371          /* .nf_name      */  "ra",
372          /* .nf_modname   */  "kralnd",
373          /* .nf_addr2str  */  libcfs_ip_addr2str,
374          /* .nf_str2addr  */  libcfs_ip_str2addr},
375         {/* .nf_type      */  QSWLND,
376          /* .nf_name      */  "elan",
377          /* .nf_modname   */  "kqswlnd",
378          /* .nf_addr2str  */  libcfs_decnum_addr2str,
379          /* .nf_str2addr  */  libcfs_num_str2addr},
380         {/* .nf_type      */  GMLND,
381          /* .nf_name      */  "gm",
382          /* .nf_modname   */  "kgmlnd",
383          /* .nf_addr2str  */  libcfs_hexnum_addr2str,
384          /* .nf_str2addr  */  libcfs_num_str2addr},
385         {/* .nf_type      */  PTLLND,
386          /* .nf_name      */  "ptl",
387          /* .nf_modname   */  "kptllnd",
388          /* .nf_addr2str  */  libcfs_decnum_addr2str,
389          /* .nf_str2addr  */  libcfs_num_str2addr},
390         /* placeholder for net0 alias.  It MUST BE THE LAST ENTRY */
391         {/* .nf_type      */  -1},
392 };
393
394 const int libcfs_nnetstrfns = sizeof(libcfs_netstrfns)/sizeof(libcfs_netstrfns[0]);
395
396 static int
397 libcfs_lo_str2addr(char *str, int nob, uint32_t *addr)
398 {
399         *addr = 0;
400         return 1;
401 }
402
403 static void
404 libcfs_ip_addr2str(uint32_t addr, char *str)
405 {
406         snprintf(str, LNET_NIDSTR_SIZE, "%u.%u.%u.%u",
407                  (addr >> 24) & 0xff, (addr >> 16) & 0xff,
408                  (addr >> 8) & 0xff, addr & 0xff);
409 }
410
411 /* CAVEAT EMPTOR XscanfX
412  * I use "%n" at the end of a sscanf format to detect trailing junk.  However
413  * sscanf may return immediately if it sees the terminating '0' in a string, so
414  * I initialise the %n variable to the expected length.  If sscanf sets it;
415  * fine, if it doesn't, then the scan ended at the end of the string, which is
416  * fine too :) */
417
418 static int
419 libcfs_ip_str2addr(char *str, int nob, uint32_t *addr)
420 {
421         int   a;
422         int   b;
423         int   c;
424         int   d;
425         int   n = nob;                          /* XscanfX */
426
427         /* numeric IP? */
428         if (sscanf(str, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n) >= 4 &&
429             n == nob &&
430             (a & ~0xff) == 0 && (b & ~0xff) == 0 &&
431             (c & ~0xff) == 0 && (d & ~0xff) == 0) {
432                 *addr = ((a<<24)|(b<<16)|(c<<8)|d);
433                 return 1;
434         }
435
436 #ifdef HAVE_GETHOSTBYNAME
437         /* known hostname? */
438         if (('a' <= str[0] && str[0] <= 'z') ||
439             ('A' <= str[0] && str[0] <= 'Z')) {
440                 char *tmp;
441
442                 tmp = malloc(nob + 1);
443                 if (tmp != NULL) {
444                         struct hostent *he;
445
446                         memcpy(tmp, str, nob);
447                         tmp[nob] = 0;
448
449                         he = gethostbyname(tmp);
450
451                         free(tmp);
452                         tmp = NULL;
453
454                         if (he != NULL) {
455                                 uint32_t ip = *(uint32_t *)he->h_addr;
456
457                                 *addr = ntohl(ip);
458                                 return 1;
459                         }
460                 }
461         }
462 #endif
463         return 0;
464 }
465
466 static void
467 libcfs_decnum_addr2str(uint32_t addr, char *str)
468 {
469         snprintf(str, LNET_NIDSTR_SIZE, "%u", addr);
470 }
471
472 static void
473 libcfs_hexnum_addr2str(uint32_t addr, char *str)
474 {
475         snprintf(str, LNET_NIDSTR_SIZE, "0x%x", addr);
476 }
477
478 static int
479 libcfs_num_str2addr(char *str, int nob, uint32_t *addr)
480 {
481         int     n;
482
483         n = nob;
484         if (sscanf(str, "0x%x%n", addr, &n) >= 1 && n == nob)
485                 return 1;
486
487         n = nob;
488         if (sscanf(str, "0X%x%n", addr, &n) >= 1 && n == nob)
489                 return 1;
490
491         n = nob;
492         if (sscanf(str, "%u%n", addr, &n) >= 1 && n == nob)
493                 return 1;
494         
495         return 0;
496 }
497
498 static struct netstrfns *
499 libcfs_lnd2netstrfns(int lnd)
500 {
501         int    i;
502
503         if (lnd >= 0)
504                 for (i = 0; i < libcfs_nnetstrfns; i++)
505                         if (lnd == libcfs_netstrfns[i].nf_type)
506                                 return &libcfs_netstrfns[i];
507
508         return NULL;
509 }
510
511 static struct netstrfns *
512 libcfs_str2net_internal(char *str, uint32_t *net)
513 {
514         struct netstrfns *nf;
515         int               nob;
516         int               netnum;
517         int               i;
518
519         for (i = 0; i < libcfs_nnetstrfns; i++) {
520                 nf = &libcfs_netstrfns[i];
521                 if (nf->nf_type >= 0 &&
522                     !strncmp(str, nf->nf_name, strlen(nf->nf_name)))
523                         break;
524         }
525
526         if (i == libcfs_nnetstrfns)
527                 return NULL;
528
529         nob = strlen(nf->nf_name);
530
531         if (strlen(str) == (unsigned int)nob) {
532                 netnum = 0;
533         } else {
534                 if (nf->nf_type == LOLND) /* net number not allowed */
535                         return NULL;
536
537                 str += nob;
538                 i = strlen(str);
539                 if (sscanf(str, "%u%n", &netnum, &i) < 1 ||
540                     i != (int)strlen(str))
541                         return NULL;
542         }
543
544         *net = LNET_MKNET(nf->nf_type, netnum);
545         return nf;
546 }
547
548 lnet_nid_t
549 libcfs_str2nid(char *str)
550 {
551         char             *sep = strchr(str, '@');
552         struct netstrfns *nf;
553         uint32_t             net;
554         uint32_t             addr;
555
556         if (sep != NULL) {
557                 nf = libcfs_str2net_internal(sep + 1, &net);
558                 if (nf == NULL)
559                         return LNET_NID_ANY;
560         } else {
561                 sep = str + strlen(str);
562                 net = LNET_MKNET(SOCKLND, 0);
563                 nf = libcfs_lnd2netstrfns(SOCKLND);
564                 if (!nf)
565                         return LNET_NID_ANY;
566         }
567
568         if (!nf->nf_str2addr(str, sep - str, &addr))
569                 return LNET_NID_ANY;
570
571         return LNET_MKNID(net, addr);
572 }
573
574 /****************************************
575  * user mapping database handling       *
576  * (very rudiment)                      *
577  ****************************************/
578
579 #define MAPPING_GROW_SIZE       512
580 #define MAX_LINE_LEN            256
581
582 struct user_map_item {
583         char        *principal; /* NULL means match all, will cause multi->single mapped, FORBID */
584         lnet_nid_t   nid;
585         uid_t        uid;
586 };
587
588 struct user_mapping {
589         int                   nitems;
590         struct user_map_item *items;
591 };
592
593 static struct user_mapping mapping = {0, NULL};
594 /* FIXME to be finished: monitor change of mapping database */
595 static int mapping_mtime = 0;
596
597 void cleanup_mapping(void)
598 {
599         if (mapping.items) {
600                 for (; mapping.nitems > 0; mapping.nitems--)
601                         free(mapping.items[mapping.nitems - 1].principal);
602                 free(mapping.items);
603                 mapping.items = NULL;
604         }
605 }
606
607 static int grow_mapping(int nitems)
608 {
609         struct user_map_item *new;
610         int oldsize, newsize;
611
612         oldsize = (mapping.nitems * sizeof(struct user_map_item) +
613                    MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
614         newsize = (nitems * sizeof(struct user_map_item) +
615                    MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
616         while (newsize <= oldsize)
617                 return 0;
618
619         newsize *= MAPPING_GROW_SIZE;
620         new = malloc(newsize);
621         if (!new) {
622                 printerr(0, "can't alloc mapping size %d\n", newsize);
623                 return -1;
624         }
625
626         if (mapping.items) {
627                 memcpy(new, mapping.items, mapping.nitems * sizeof(struct user_map_item));
628                 free(mapping.items);
629         }
630         mapping.items = new;
631         return 0;
632 }
633
634 uid_t parse_uid(char *uidstr)
635 {
636         struct passwd *pw;
637         char *p = NULL;
638         long uid;
639
640         pw = getpwnam(uidstr);
641         if (pw)
642                 return pw->pw_uid;
643
644         uid = strtol(uidstr, &p, 0);
645         if (*p == '\0')
646                 return (uid_t) uid;
647
648         return -1;
649 }
650
651 static int read_mapping_db(void)
652 {
653         char princ[MAX_LINE_LEN];
654         char nid_str[MAX_LINE_LEN];
655         char dest[MAX_LINE_LEN];
656         char linebuf[MAX_LINE_LEN];
657         char *line;
658         lnet_nid_t nid;
659         uid_t dest_uid;
660         FILE *f;
661
662         /* cleanup old mappings */
663         cleanup_mapping();
664
665         f = fopen(MAPPING_DATABASE_FILE, "r");
666         if (!f) {
667                 printerr(0, "can't open mapping database: %s\n",
668                          MAPPING_DATABASE_FILE);
669                 return -1;
670         }
671
672         while ((line = fgets(linebuf, MAX_LINE_LEN, f)) != NULL) {
673                 char *name;
674
675                 if (strlen(line) >= MAX_LINE_LEN) {
676                         printerr(0, "invalid mapping db: line too long (%d)\n",
677                                  strlen(line));
678                         continue;
679                 }
680
681                 if (sscanf(line, "%s %s %s", princ, nid_str, dest) != 3) {
682                         printerr(0, "mapping db: syntax error\n");
683                         continue;
684                 }
685
686                 if (!strcmp(princ, "*")) {
687                         printerr(0, "NOT permit \"*\" princ, it will cause multi->single mapped\n");
688                         continue;
689                 } else {
690                         name = strdup(princ);
691                         if (!name) {
692                                 printerr(0, "fail to dup str %s\n", princ);
693                                 continue;
694                         }
695                 }
696
697                 if (!strcmp(nid_str, "*")) {
698                         nid = LNET_NID_ANY;
699                 } else {
700                         nid = libcfs_str2nid(nid_str);
701                         if (nid == LNET_NID_ANY) {
702                                 printerr(0, "fail to parse nid %s\n", nid_str);
703                                 free(name);
704                                 continue;
705                         }
706                 }
707
708                 dest_uid = parse_uid(dest);
709                 if (dest_uid == -1) {
710                         printerr(0, "no valid user: %s\n", dest);
711                         free(name);
712                         continue;
713                 }
714
715                 if (grow_mapping(mapping.nitems + 1)) {
716                         printerr(0, "fail to grow mapping to %d\n",
717                                  mapping.nitems + 1);
718                         free(name);
719                         fclose(f);
720                         return -1;
721                 }
722
723                 mapping.items[mapping.nitems].principal = name;
724                 mapping.items[mapping.nitems].nid = nid;
725                 mapping.items[mapping.nitems].uid = dest_uid;
726                 mapping.nitems++;
727                 printerr(1, "add mapping: %s(%s/0x%llx) ==> %d\n",
728                          name, nid_str, nid, dest_uid);
729         }
730
731         fclose(f);
732         return 0;
733 }
734
735 static inline int mapping_changed(void)
736 {
737         struct stat st;
738
739         if (stat(MAPPING_DATABASE_FILE, &st) == -1) {
740                 /* stat failed, treat it like doesn't exist or be removed */
741                 if (mapping_mtime == 0) {
742                         return 0;
743                 } else {
744                         printerr(0, "Warning: stat %s failed: %s\n",
745                                  MAPPING_DATABASE_FILE, strerror(errno));
746
747                         mapping_mtime = 0;
748                         return 1;
749                 }
750         }
751
752         if (st.st_mtime != mapping_mtime) {
753                 mapping_mtime = st.st_mtime;
754                 return 1;
755         }
756
757         return 0;
758 }
759
760 int lookup_mapping(char *princ, lnet_nid_t nid, uid_t *uid)
761 {
762         int n;
763
764         *uid = -1;
765
766         /* FIXME race condition here */
767         if (mapping_changed()) {
768                 if (read_mapping_db())
769                         printerr(0, "all remote users will be denied\n");
770         }
771
772         for (n = 0; n < mapping.nitems; n++) {
773                 struct user_map_item *entry = &mapping.items[n];
774
775                 if (entry->nid != LNET_NID_ANY && entry->nid != nid)
776                         continue;
777                 if (!strcasecmp(entry->principal, princ)) {
778                         printerr(1, "found mapping: %s ==> %d\n",
779                                  princ, entry->uid);
780                         *uid = entry->uid;
781                         return 0;
782                 }
783         }
784
785         printerr(2, "no mapping for %s/%#Lx\n", princ, nid);
786         return -1;
787 }