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