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