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 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 struct convert_struct {
277         char                    *name;
278         lnd_nid2hostname_t      *nid2name;
279 };
280
281 static struct convert_struct converter[] = {
282         [0]             = { "UNUSED0",  NULL},
283         [QSWLND]        = { "QSWLND",   external_nid2hostname},
284         [SOCKLND]       = { "SOCKLND",  ipv4_nid2hostname },
285         [GMLND]         = { "GMLND",    external_nid2hostname},
286         [PTLLND]        = { "PTLLND",   external_nid2hostname },
287         [O2IBLND]       = { "O2IBLND",  ipv4_nid2hostname },
288         [CIBLND]        = { "CIBLND",   external_nid2hostname },
289         [OPENIBLND]     = { "OPENIBLND",external_nid2hostname },
290         [IIBLND]        = { "IIBLND",   external_nid2hostname },
291         [LOLND]         = { "LOLND",    lolnd_nid2hostname },
292         [RALND]         = { "RALND",    external_nid2hostname },
293         [VIBLND]        = { "VIBLND",   external_nid2hostname },
294         [MXLND]         = { "MXLND",    external_nid2hostname },
295 };
296
297 #define LND_MAX         (sizeof(converter) / sizeof(converter[0]))
298
299 int lnet_nid2hostname(lnet_nid_t nid, char *buf, int buflen)
300 {
301         uint32_t lnd, net, addr;
302
303         addr = LNET_NIDADDR(nid);
304         net = LNET_NIDNET(nid);
305         lnd = LNET_NETTYP(net);
306
307         if (lnd >= LND_MAX) {
308                 printerr(0, "ERROR: Unrecognized LND %u\n", lnd);
309                 return -1;
310         }
311
312         if (converter[lnd].nid2name == NULL) {
313                 printerr(0, "ERROR: %s converter not ready\n",
314                         converter[lnd].name);
315                 return -1;
316         }
317
318         return converter[lnd].nid2name(converter[lnd].name, net, addr,
319                                        buf, buflen);
320 }
321
322
323 /****************************************
324  * lnet support routine                 *
325  * (from lnet/libcfs/nidstrings.c       *
326  ****************************************/
327
328 #define LNET_NIDSTR_SIZE   32      /* size of each one (see below for usage) */
329
330 static int  libcfs_lo_str2addr(char *str, int nob, uint32_t *addr);
331 static void libcfs_ip_addr2str(uint32_t addr, char *str);
332 static int  libcfs_ip_str2addr(char *str, int nob, uint32_t *addr);
333 static void libcfs_decnum_addr2str(uint32_t addr, char *str);
334 static void libcfs_hexnum_addr2str(uint32_t addr, char *str);
335 static int  libcfs_num_str2addr(char *str, int nob, uint32_t *addr);
336
337 struct netstrfns {
338         int          nf_type;
339         char        *nf_name;
340         char        *nf_modname;
341         void       (*nf_addr2str)(uint32_t addr, char *str);
342         int        (*nf_str2addr)(char *str, int nob, uint32_t *addr);
343 };
344
345 static struct netstrfns  libcfs_netstrfns[] = {
346         {/* .nf_type      */  LOLND,
347          /* .nf_name      */  "lo",
348          /* .nf_modname   */  "klolnd",
349          /* .nf_addr2str  */  libcfs_decnum_addr2str,
350          /* .nf_str2addr  */  libcfs_lo_str2addr},
351         {/* .nf_type      */  SOCKLND,
352          /* .nf_name      */  "tcp",
353          /* .nf_modname   */  "ksocklnd",
354          /* .nf_addr2str  */  libcfs_ip_addr2str,
355          /* .nf_str2addr  */  libcfs_ip_str2addr},
356         {/* .nf_type      */  O2IBLND,
357          /* .nf_name      */  "o2ib",
358          /* .nf_modname   */  "ko2iblnd",
359          /* .nf_addr2str  */  libcfs_ip_addr2str,
360          /* .nf_str2addr  */  libcfs_ip_str2addr},
361         {/* .nf_type      */  CIBLND,
362          /* .nf_name      */  "cib",
363          /* .nf_modname   */  "kciblnd",
364          /* .nf_addr2str  */  libcfs_ip_addr2str,
365          /* .nf_str2addr  */  libcfs_ip_str2addr},
366         {/* .nf_type      */  OPENIBLND,
367          /* .nf_name      */  "openib",
368          /* .nf_modname   */  "kopeniblnd",
369          /* .nf_addr2str  */  libcfs_ip_addr2str,
370          /* .nf_str2addr  */  libcfs_ip_str2addr},
371         {/* .nf_type      */  IIBLND,
372          /* .nf_name      */  "iib",
373          /* .nf_modname   */  "kiiblnd",
374          /* .nf_addr2str  */  libcfs_ip_addr2str,
375          /* .nf_str2addr  */  libcfs_ip_str2addr},
376         {/* .nf_type      */  VIBLND,
377          /* .nf_name      */  "vib",
378          /* .nf_modname   */  "kviblnd",
379          /* .nf_addr2str  */  libcfs_ip_addr2str,
380          /* .nf_str2addr  */  libcfs_ip_str2addr},
381         {/* .nf_type      */  RALND,
382          /* .nf_name      */  "ra",
383          /* .nf_modname   */  "kralnd",
384          /* .nf_addr2str  */  libcfs_ip_addr2str,
385          /* .nf_str2addr  */  libcfs_ip_str2addr},
386         {/* .nf_type      */  QSWLND,
387          /* .nf_name      */  "elan",
388          /* .nf_modname   */  "kqswlnd",
389          /* .nf_addr2str  */  libcfs_decnum_addr2str,
390          /* .nf_str2addr  */  libcfs_num_str2addr},
391         {/* .nf_type      */  GMLND,
392          /* .nf_name      */  "gm",
393          /* .nf_modname   */  "kgmlnd",
394          /* .nf_addr2str  */  libcfs_hexnum_addr2str,
395          /* .nf_str2addr  */  libcfs_num_str2addr},
396         {/* .nf_type      */  PTLLND,
397          /* .nf_name      */  "ptl",
398          /* .nf_modname   */  "kptllnd",
399          /* .nf_addr2str  */  libcfs_decnum_addr2str,
400          /* .nf_str2addr  */  libcfs_num_str2addr},
401         /* placeholder for net0 alias.  It MUST BE THE LAST ENTRY */
402         {/* .nf_type      */  -1},
403 };
404
405 const int libcfs_nnetstrfns = sizeof(libcfs_netstrfns)/sizeof(libcfs_netstrfns[0]);
406
407 static int
408 libcfs_lo_str2addr(char *str, int nob, uint32_t *addr)
409 {
410         *addr = 0;
411         return 1;
412 }
413
414 static void
415 libcfs_ip_addr2str(uint32_t addr, char *str)
416 {
417         snprintf(str, LNET_NIDSTR_SIZE, "%u.%u.%u.%u",
418                  (addr >> 24) & 0xff, (addr >> 16) & 0xff,
419                  (addr >> 8) & 0xff, addr & 0xff);
420 }
421
422 /* CAVEAT EMPTOR XscanfX
423  * I use "%n" at the end of a sscanf format to detect trailing junk.  However
424  * sscanf may return immediately if it sees the terminating '0' in a string, so
425  * I initialise the %n variable to the expected length.  If sscanf sets it;
426  * fine, if it doesn't, then the scan ended at the end of the string, which is
427  * fine too :) */
428
429 static int
430 libcfs_ip_str2addr(char *str, int nob, uint32_t *addr)
431 {
432         int   a;
433         int   b;
434         int   c;
435         int   d;
436         int   n = nob;                          /* XscanfX */
437
438         /* numeric IP? */
439         if (sscanf(str, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n) >= 4 &&
440             n == nob &&
441             (a & ~0xff) == 0 && (b & ~0xff) == 0 &&
442             (c & ~0xff) == 0 && (d & ~0xff) == 0) {
443                 *addr = ((a<<24)|(b<<16)|(c<<8)|d);
444                 return 1;
445         }
446
447 #ifdef HAVE_GETHOSTBYNAME
448         /* known hostname? */
449         if (('a' <= str[0] && str[0] <= 'z') ||
450             ('A' <= str[0] && str[0] <= 'Z')) {
451                 char *tmp;
452
453                 tmp = malloc(nob + 1);
454                 if (tmp != NULL) {
455                         struct hostent *he;
456
457                         memcpy(tmp, str, nob);
458                         tmp[nob] = 0;
459
460                         he = gethostbyname(tmp);
461
462                         free(tmp);
463                         tmp = NULL;
464
465                         if (he != NULL) {
466                                 uint32_t ip = *(uint32_t *)he->h_addr;
467
468                                 *addr = ntohl(ip);
469                                 return 1;
470                         }
471                 }
472         }
473 #endif
474         return 0;
475 }
476
477 static void
478 libcfs_decnum_addr2str(uint32_t addr, char *str)
479 {
480         snprintf(str, LNET_NIDSTR_SIZE, "%u", addr);
481 }
482
483 static void
484 libcfs_hexnum_addr2str(uint32_t addr, char *str)
485 {
486         snprintf(str, LNET_NIDSTR_SIZE, "0x%x", addr);
487 }
488
489 static int
490 libcfs_num_str2addr(char *str, int nob, uint32_t *addr)
491 {
492         int     n;
493
494         n = nob;
495         if (sscanf(str, "0x%x%n", addr, &n) >= 1 && n == nob)
496                 return 1;
497
498         n = nob;
499         if (sscanf(str, "0X%x%n", addr, &n) >= 1 && n == nob)
500                 return 1;
501
502         n = nob;
503         if (sscanf(str, "%u%n", addr, &n) >= 1 && n == nob)
504                 return 1;
505         
506         return 0;
507 }
508
509 static struct netstrfns *
510 libcfs_lnd2netstrfns(int lnd)
511 {
512         int    i;
513
514         if (lnd >= 0)
515                 for (i = 0; i < libcfs_nnetstrfns; i++)
516                         if (lnd == libcfs_netstrfns[i].nf_type)
517                                 return &libcfs_netstrfns[i];
518
519         return NULL;
520 }
521
522 static struct netstrfns *
523 libcfs_str2net_internal(char *str, uint32_t *net)
524 {
525         struct netstrfns *nf;
526         int               nob;
527         int               netnum;
528         int               i;
529
530         for (i = 0; i < libcfs_nnetstrfns; i++) {
531                 nf = &libcfs_netstrfns[i];
532                 if (nf->nf_type >= 0 &&
533                     !strncmp(str, nf->nf_name, strlen(nf->nf_name)))
534                         break;
535         }
536
537         if (i == libcfs_nnetstrfns)
538                 return NULL;
539
540         nob = strlen(nf->nf_name);
541
542         if (strlen(str) == (unsigned int)nob) {
543                 netnum = 0;
544         } else {
545                 if (nf->nf_type == LOLND) /* net number not allowed */
546                         return NULL;
547
548                 str += nob;
549                 i = strlen(str);
550                 if (sscanf(str, "%u%n", &netnum, &i) < 1 ||
551                     i != (int)strlen(str))
552                         return NULL;
553         }
554
555         *net = LNET_MKNET(nf->nf_type, netnum);
556         return nf;
557 }
558
559 /* FIXME
560  * this is duplication in libcfs, but somehow doesn't make into libcfs.a.
561  * remove it when libcfs get fixed.
562  */
563 lnet_nid_t
564 libcfs_str2nid(const char *str)
565 {
566         const char       *sep = strchr(str, '@');
567         struct netstrfns *nf;
568         uint32_t          net;
569         uint32_t          addr;
570
571         if (sep != NULL) {
572                 nf = libcfs_str2net_internal(sep + 1, &net);
573                 if (nf == NULL)
574                         return LNET_NID_ANY;
575         } else {
576                 sep = str + strlen(str);
577                 net = LNET_MKNET(SOCKLND, 0);
578                 nf = libcfs_lnd2netstrfns(SOCKLND);
579                 if (!nf)
580                         return LNET_NID_ANY;
581         }
582
583         if (!nf->nf_str2addr(str, sep - str, &addr))
584                 return LNET_NID_ANY;
585
586         return LNET_MKNID(net, addr);
587 }
588
589 /****************************************
590  * user mapping database handling       *
591  * (very rudiment)                      *
592  ****************************************/
593
594 #define MAPPING_GROW_SIZE       512
595 #define MAX_LINE_LEN            256
596
597 struct user_map_item {
598         char        *principal; /* NULL means match all */
599         lnet_nid_t   nid;
600         uid_t        uid;
601 };
602
603 struct user_mapping {
604         int                   nitems;
605         struct user_map_item *items;
606 };
607
608 static struct user_mapping mapping = {0, NULL};
609 /* FIXME to be finished: monitor change of mapping database */
610 static int mapping_mtime = 0;
611
612 void cleanup_mapping(void)
613 {
614         if (mapping.items) {
615                 for (; mapping.nitems > 0; mapping.nitems--)
616                         if (mapping.items[mapping.nitems-1].principal)
617                                 free(mapping.items[mapping.nitems-1].principal);
618
619                 free(mapping.items);
620                 mapping.items = NULL;
621         }
622 }
623
624 static int grow_mapping(int nitems)
625 {
626         struct user_map_item *new;
627         int oldsize, newsize;
628
629         oldsize = (mapping.nitems * sizeof(struct user_map_item) +
630                    MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
631         newsize = (nitems * sizeof(struct user_map_item) +
632                    MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
633         while (newsize <= oldsize)
634                 return 0;
635
636         newsize *= MAPPING_GROW_SIZE;
637         new = malloc(newsize);
638         if (!new) {
639                 printerr(0, "can't alloc mapping size %d\n", newsize);
640                 return -1;
641         }
642
643         if (mapping.items) {
644                 memcpy(new, mapping.items,
645                        mapping.nitems * sizeof(struct user_map_item));
646                 free(mapping.items);
647         }
648         mapping.items = new;
649         return 0;
650 }
651
652 uid_t parse_uid(char *uidstr)
653 {
654         struct passwd *pw;
655         char *p = NULL;
656         long uid;
657
658         pw = getpwnam(uidstr);
659         if (pw)
660                 return pw->pw_uid;
661
662         uid = strtol(uidstr, &p, 0);
663         if (*p == '\0')
664                 return (uid_t) uid;
665
666         return -1;
667 }
668
669 static int read_mapping_db(void)
670 {
671         char princ[MAX_LINE_LEN];
672         char nid_str[MAX_LINE_LEN];
673         char dest[MAX_LINE_LEN];
674         char linebuf[MAX_LINE_LEN];
675         char *line;
676         lnet_nid_t nid;
677         uid_t dest_uid;
678         FILE *f;
679
680         /* cleanup old mappings */
681         cleanup_mapping();
682
683         f = fopen(MAPPING_DATABASE_FILE, "r");
684         if (!f) {
685                 printerr(0, "can't open mapping database: %s\n",
686                          MAPPING_DATABASE_FILE);
687                 return -1;
688         }
689
690         while ((line = fgets(linebuf, MAX_LINE_LEN, f)) != NULL) {
691                 char *name;
692
693                 if (strlen(line) >= MAX_LINE_LEN) {
694                         printerr(0, "invalid mapping db: line too long (%d)\n",
695                                  strlen(line));
696                         continue;
697                 }
698
699                 if (sscanf(line, "%s %s %s", princ, nid_str, dest) != 3) {
700                         printerr(0, "mapping db: syntax error\n");
701                         continue;
702                 }
703
704                 if (!strcmp(princ, "*")) {
705                         name = NULL;
706                 } else {
707                         name = strdup(princ);
708                         if (!name) {
709                                 printerr(0, "fail to dup str %s\n", princ);
710                                 continue;
711                         }
712                 }
713
714                 if (!strcmp(nid_str, "*")) {
715                         nid = LNET_NID_ANY;
716                 } else {
717                         nid = libcfs_str2nid(nid_str);
718                         if (nid == LNET_NID_ANY) {
719                                 printerr(0, "fail to parse nid %s\n", nid_str);
720                                 if (name)
721                                 free(name);
722                                 continue;
723                         }
724                 }
725
726                 dest_uid = parse_uid(dest);
727                 if (dest_uid == -1) {
728                         printerr(0, "no valid user: %s\n", dest);
729                         if (name)
730                         free(name);
731                         continue;
732                 }
733
734                 if (grow_mapping(mapping.nitems + 1)) {
735                         printerr(0, "fail to grow mapping to %d\n",
736                                  mapping.nitems + 1);
737                         if (name)
738                         free(name);
739                         fclose(f);
740                         return -1;
741                 }
742
743                 mapping.items[mapping.nitems].principal = name;
744                 mapping.items[mapping.nitems].nid = nid;
745                 mapping.items[mapping.nitems].uid = dest_uid;
746                 mapping.nitems++;
747                 printerr(1, "add mapping: %s(%s/0x%llx) ==> %d\n",
748                          name, nid_str, nid, dest_uid);
749         }
750
751         fclose(f);
752         return 0;
753 }
754
755 static inline int mapping_changed(void)
756 {
757         struct stat st;
758
759         if (stat(MAPPING_DATABASE_FILE, &st) == -1) {
760                 /* stat failed, treat it like doesn't exist or be removed */
761                 if (mapping_mtime == 0) {
762                         return 0;
763                 } else {
764                         printerr(0, "Warning: stat %s failed: %s\n",
765                                  MAPPING_DATABASE_FILE, strerror(errno));
766
767                         mapping_mtime = 0;
768                         return 1;
769                 }
770         }
771
772         if (st.st_mtime != mapping_mtime) {
773                 mapping_mtime = st.st_mtime;
774                 return 1;
775         }
776
777         return 0;
778 }
779
780 int lookup_mapping(char *princ, lnet_nid_t nid, uid_t *uid)
781 {
782         int n;
783
784         *uid = -1;
785
786         /* FIXME race condition here */
787         if (mapping_changed()) {
788                 if (read_mapping_db())
789                         printerr(0, "all remote users will be denied\n");
790         }
791
792         for (n = 0; n < mapping.nitems; n++) {
793                 struct user_map_item *entry = &mapping.items[n];
794
795                 if (entry->nid != LNET_NID_ANY && entry->nid != nid)
796                         continue;
797                 if (!entry->principal || !strcasecmp(entry->principal, princ)) {
798                         printerr(1, "found mapping: %s ==> %d\n",
799                                  princ, entry->uid);
800                         *uid = entry->uid;
801                         return 0;
802                 }
803         }
804
805         printerr(2, "no mapping for %s/%#Lx\n", princ, nid);
806         return -1;
807 }