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