1
/* ----------------------------------------------------------------------- *
3
* lookup_yp.c - module for Linux automountd to access a YP (NIS)
6
* Copyright 1997 Transmeta Corporation - All Rights Reserved
7
* Copyright 2001-2003 Ian Kent <raven@themaw.net>
9
* This program is free software; you can redistribute it and/or modify
10
* it under the terms of the GNU General Public License as published by
11
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
12
* USA; either version 2 of the License, or (at your option) any later
13
* version; incorporated herein by reference.
15
* ----------------------------------------------------------------------- */
22
#include <sys/param.h>
23
#include <sys/types.h>
27
#include <rpcsvc/yp_prot.h>
28
#include <rpcsvc/ypclnt.h>
31
#include "automount.h"
34
#define MAPFMT_DEFAULT "sun"
36
#define MODPREFIX "lookup(yp): "
38
struct lookup_context {
39
const char *domainname;
42
struct parse_mod *parse;
45
struct callback_master_data {
52
struct callback_data {
53
struct autofs_point *ap;
54
struct map_source *source;
59
int lookup_version = AUTOFS_LOOKUP_VERSION; /* Required by protocol */
61
static unsigned int get_map_order(const char *domain, const char *map)
63
char key[] = "YP_LAST_MODIFIED";
64
int key_len = strlen(key);
71
mapname = alloca(strlen(map) + 1);
77
err = yp_match(domain, mapname, key, key_len, &order, &order_len);
78
if (err != YPERR_SUCCESS) {
79
if (err == YPERR_MAP) {
82
while ((usc = strchr(mapname, '_')))
85
err = yp_match(domain, mapname,
86
key, key_len, &order, &order_len);
88
if (err != YPERR_SUCCESS)
91
last_changed = atol(order);
94
return (unsigned int) last_changed;
99
last_changed = atol(order);
102
return (unsigned int) last_changed;
105
int lookup_init(const char *mapfmt, int argc, const char *const *argv, void **context)
107
struct lookup_context *ctxt;
108
char buf[MAX_ERR_BUF];
113
ctxt = malloc(sizeof(struct lookup_context));
115
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
116
logerr(MODPREFIX "%s", estr);
119
memset(ctxt, 0, sizeof(struct lookup_context));
123
logerr(MODPREFIX "no map name");
126
ctxt->mapname = argv[0];
128
/* This should, but doesn't, take a const char ** */
129
err = yp_get_default_domain((char **) &ctxt->domainname);
131
size_t len = strlen(ctxt->mapname);
132
char *name = alloca(len + 1);
133
memcpy(name, ctxt->mapname, len);
136
logerr(MODPREFIX "map %s: %s", name, yperr_string(err));
140
ctxt->order = get_map_order(ctxt->domainname, ctxt->mapname);
143
mapfmt = MAPFMT_DEFAULT;
145
ctxt->parse = open_parse(mapfmt, MODPREFIX, argc - 1, argv + 1);
148
logmsg(MODPREFIX "failed to open parse context");
156
int yp_all_master_callback(int status, char *ypkey, int ypkeylen,
157
char *val, int vallen, char *ypcb_data)
159
struct callback_master_data *cbdata =
160
(struct callback_master_data *) ypcb_data;
161
unsigned int timeout = cbdata->timeout;
162
unsigned int logging = cbdata->logging;
163
unsigned int logopt = cbdata->logopt;
164
time_t age = cbdata->age;
168
if (status != YP_TRUE)
171
/* Ignore zero length and single non-printable char keys */
172
if (ypkeylen == 0 || (ypkeylen == 1 && !isprint(*ypkey))) {
173
warn(logopt, MODPREFIX
174
"ignoring invalid map entry, zero length or "
175
"single character non-printable key");
180
* Ignore keys beginning with '+' as plus map
181
* inclusion is only valid in file maps.
186
*(ypkey + ypkeylen) = '\0';
187
*(val + vallen) = '\0';
189
len = ypkeylen + 1 + vallen + 2;
191
buffer = alloca(len);
193
error(logopt, MODPREFIX "could not malloc parse buffer");
196
memset(buffer, 0, len);
198
strcat(buffer, ypkey);
202
master_parse_entry(buffer, timeout, logging, age);
207
int lookup_read_master(struct master *master, time_t age, void *context)
209
struct lookup_context *ctxt = (struct lookup_context *) context;
210
struct ypall_callback ypcb;
211
struct callback_master_data ypcb_data;
212
unsigned int logging = master->default_logging;
213
unsigned int logopt = master->logopt;
217
mapname = alloca(strlen(ctxt->mapname) + 1);
221
strcpy(mapname, ctxt->mapname);
223
ypcb_data.timeout = master->default_timeout;
224
ypcb_data.logging = logging;
225
ypcb_data.logopt = logopt;
228
ypcb.foreach = yp_all_master_callback;
229
ypcb.data = (char *) &ypcb_data;
231
err = yp_all((char *) ctxt->domainname, mapname, &ypcb);
233
if (err != YPERR_SUCCESS) {
234
if (err == YPERR_MAP) {
237
while ((usc = strchr(mapname, '_')))
240
err = yp_all((char *) ctxt->domainname, mapname, &ypcb);
243
if (err == YPERR_SUCCESS)
244
return NSS_STATUS_SUCCESS;
247
MODPREFIX "read of master map %s failed: %s",
248
mapname, yperr_string(err));
250
if (err == YPERR_PMAP || err == YPERR_YPSERV)
251
return NSS_STATUS_UNAVAIL;
253
return NSS_STATUS_NOTFOUND;
256
return NSS_STATUS_SUCCESS;
259
int yp_all_callback(int status, char *ypkey, int ypkeylen,
260
char *val, int vallen, char *ypcb_data)
262
struct callback_data *cbdata = (struct callback_data *) ypcb_data;
263
struct autofs_point *ap = cbdata->ap;
264
struct map_source *source = cbdata->source;
265
struct mapent_cache *mc = source->mc;
266
unsigned int logopt = cbdata->logopt;
267
time_t age = cbdata->age;
271
if (status != YP_TRUE)
274
/* Ignore zero length and single non-printable char keys */
275
if (ypkeylen == 0 || (ypkeylen == 1 && !isprint(*ypkey))) {
276
warn(logopt, MODPREFIX
277
"ignoring invalid map entry, zero length or "
278
"single character non-printable key");
283
* Ignore keys beginning with '+' as plus map
284
* inclusion is only valid in file maps.
289
key = sanitize_path(ypkey, ypkeylen, ap->type, ap->logopt);
291
error(logopt, MODPREFIX "invalid path %s", ypkey);
295
mapent = alloca(vallen + 1);
296
strncpy(mapent, val, vallen);
297
*(mapent + vallen) = '\0';
300
ret = cache_update(mc, source, key, mapent, age);
311
int lookup_read_map(struct autofs_point *ap, time_t age, void *context)
313
struct lookup_context *ctxt = (struct lookup_context *) context;
314
struct ypall_callback ypcb;
315
struct callback_data ypcb_data;
316
unsigned int logopt = ap->logopt;
317
struct map_source *source;
321
source = ap->entry->current;
322
ap->entry->current = NULL;
323
master_source_current_signal(ap->entry);
326
* If we don't need to create directories then there's no use
327
* reading the map. We always need to read the whole map for
328
* direct mounts in order to mount the triggers.
330
if (!(ap->flags & MOUNT_FLAG_GHOST) && ap->type != LKP_DIRECT)
331
return NSS_STATUS_SUCCESS;
334
ypcb_data.source = source;
335
ypcb_data.logopt = logopt;
338
ypcb.foreach = yp_all_callback;
339
ypcb.data = (char *) &ypcb_data;
341
mapname = alloca(strlen(ctxt->mapname) + 1);
343
return NSS_STATUS_UNKNOWN;
345
strcpy(mapname, ctxt->mapname);
347
err = yp_all((char *) ctxt->domainname, mapname, &ypcb);
349
if (err != YPERR_SUCCESS) {
350
if (err == YPERR_MAP) {
353
while ((usc = strchr(mapname, '_')))
356
err = yp_all((char *) ctxt->domainname, mapname, &ypcb);
359
if (err == YPERR_SUCCESS)
360
return NSS_STATUS_SUCCESS;
363
MODPREFIX "read of map %s failed: %s",
364
ap->path, yperr_string(err));
366
if (err == YPERR_PMAP || err == YPERR_YPSERV)
367
return NSS_STATUS_UNAVAIL;
369
return NSS_STATUS_NOTFOUND;
374
return NSS_STATUS_SUCCESS;
377
static int lookup_one(struct autofs_point *ap,
378
const char *key, int key_len,
379
struct lookup_context *ctxt)
381
struct map_source *source;
382
struct mapent_cache *mc;
386
time_t age = time(NULL);
389
source = ap->entry->current;
390
ap->entry->current = NULL;
391
master_source_current_signal(ap->entry);
395
mapname = alloca(strlen(ctxt->mapname) + 1);
399
strcpy(mapname, ctxt->mapname);
402
* For reasons unknown, the standard YP definitions doesn't
403
* define input strings as const char *. However, my
404
* understanding is that they will not be modified by the
407
ret = yp_match((char *) ctxt->domainname, mapname,
408
(char *) key, key_len, &mapent, &mapent_len);
410
if (ret != YPERR_SUCCESS) {
411
if (ret == YPERR_MAP) {
414
while ((usc = strchr(mapname, '_')))
417
ret = yp_match((char *) ctxt->domainname,
418
mapname, key, key_len, &mapent, &mapent_len);
421
if (ret != YPERR_SUCCESS) {
422
if (ret == YPERR_KEY)
430
ret = cache_update(mc, source, key, mapent, age);
437
static int lookup_wild(struct autofs_point *ap, struct lookup_context *ctxt)
439
struct map_source *source;
440
struct mapent_cache *mc;
444
time_t age = time(NULL);
447
source = ap->entry->current;
448
ap->entry->current = NULL;
449
master_source_current_signal(ap->entry);
453
mapname = alloca(strlen(ctxt->mapname) + 1);
457
strcpy(mapname, ctxt->mapname);
459
ret = yp_match((char *) ctxt->domainname,
460
mapname, "*", 1, &mapent, &mapent_len);
462
if (ret != YPERR_SUCCESS) {
463
if (ret == YPERR_MAP) {
466
while ((usc = strchr(mapname, '_')))
469
ret = yp_match((char *) ctxt->domainname,
470
mapname, "*", 1, &mapent, &mapent_len);
473
if (ret != YPERR_SUCCESS) {
474
if (ret == YPERR_KEY)
482
ret = cache_update(mc, source, "*", mapent, age);
489
static int check_map_indirect(struct autofs_point *ap,
490
char *key, int key_len,
491
struct lookup_context *ctxt)
493
struct map_source *source;
494
struct mapent_cache *mc;
495
struct mapent *exists;
496
unsigned int map_order;
499
source = ap->entry->current;
500
ap->entry->current = NULL;
501
master_source_current_signal(ap->entry);
505
master_source_current_wait(ap->entry);
506
ap->entry->current = source;
508
/* check map and if change is detected re-read map */
509
ret = lookup_one(ap, key, key_len, ctxt);
511
return NSS_STATUS_NOTFOUND;
515
* If the server is down and the entry exists in the cache
516
* and belongs to this map return success and use the entry.
518
exists = cache_lookup(mc, key);
519
if (exists && exists->source == source)
520
return NSS_STATUS_SUCCESS;
523
MODPREFIX "lookup for %s failed: %s",
524
key, yperr_string(-ret));
526
return NSS_STATUS_UNAVAIL;
529
/* Only read map if it has been modified */
530
map_order = get_map_order(ctxt->domainname, ctxt->mapname);
531
if (map_order > ctxt->order) {
532
ctxt->order = map_order;
537
exists = cache_lookup_distinct(mc, key);
538
/* Not found in the map but found in the cache */
539
if (exists && exists->source == source && ret & CHE_MISSING) {
540
if (exists->mapent) {
541
free(exists->mapent);
542
exists->mapent = NULL;
549
if (ret == CHE_MISSING) {
551
int wild = CHE_MISSING;
553
master_source_current_wait(ap->entry);
554
ap->entry->current = source;
556
wild = lookup_wild(ap, ctxt);
558
* Check for map change and update as needed for
559
* following cache lookup.
562
we = cache_lookup_distinct(mc, "*");
564
/* Wildcard entry existed and is now gone */
565
if (we->source == source && (wild & CHE_MISSING)) {
566
cache_delete(mc, "*");
570
/* Wildcard not in map but now is */
571
if (wild & (CHE_OK | CHE_UPDATED))
576
if (wild & (CHE_OK | CHE_UPDATED))
577
return NSS_STATUS_SUCCESS;
580
if (ret == CHE_MISSING)
581
return NSS_STATUS_NOTFOUND;
583
return NSS_STATUS_SUCCESS;
586
int lookup_mount(struct autofs_point *ap, const char *name, int name_len, void *context)
588
struct lookup_context *ctxt = (struct lookup_context *) context;
589
struct map_source *source;
590
struct mapent_cache *mc;
591
char key[KEY_MAX_LEN + 1];
599
source = ap->entry->current;
600
ap->entry->current = NULL;
601
master_source_current_signal(ap->entry);
605
debug(ap->logopt, MODPREFIX "looking up %s", name);
607
key_len = snprintf(key, KEY_MAX_LEN + 1, "%s", name);
608
if (key_len > KEY_MAX_LEN)
609
return NSS_STATUS_NOTFOUND;
611
/* Check if we recorded a mount fail for this key anywhere */
612
me = lookup_source_mapent(ap, key, LKP_DISTINCT);
614
if (me->status >= time(NULL)) {
615
cache_unlock(me->mc);
616
return NSS_STATUS_NOTFOUND;
618
struct mapent_cache *smc = me->mc;
625
cache_writelock(smc);
626
sme = cache_lookup_distinct(smc, key);
627
/* Negative timeout expired for non-existent entry. */
628
if (sme && !sme->mapent)
629
cache_delete(smc, key);
636
* We can't check the direct mount map as if it's not in
637
* the map cache already we never get a mount lookup, so
638
* we never know about it.
640
if (ap->type == LKP_INDIRECT && *key != '/') {
644
me = cache_lookup_distinct(mc, key);
646
lkp_key = strdup(me->multi->key);
648
lkp_key = strdup(key);
652
return NSS_STATUS_UNKNOWN;
654
master_source_current_wait(ap->entry);
655
ap->entry->current = source;
657
status = check_map_indirect(ap, lkp_key, strlen(lkp_key), ctxt);
664
me = cache_lookup(mc, key);
665
/* Stale mapent => check for entry in alternate source or wildcard */
666
if (me && !me->mapent) {
667
while ((me = cache_lookup_key_next(me)))
668
if (me->source == source)
671
me = cache_lookup_distinct(mc, "*");
673
if (me && (me->source == source || *me->key == '/')) {
674
mapent_len = strlen(me->mapent);
675
mapent = alloca(mapent_len + 1);
676
strcpy(mapent, me->mapent);
681
master_source_current_wait(ap->entry);
682
ap->entry->current = source;
684
debug(ap->logopt, MODPREFIX "%s -> %s", key, mapent);
685
ret = ctxt->parse->parse_mount(ap, key, key_len,
686
mapent, ctxt->parse->context);
688
time_t now = time(NULL);
692
me = cache_lookup_distinct(mc, key);
694
rv = cache_update(mc, source, key, NULL, now);
695
if (rv != CHE_FAIL) {
696
me = cache_lookup_distinct(mc, key);
697
me->status = now + ap->negative_timeout;
704
return NSS_STATUS_TRYAGAIN;
706
return NSS_STATUS_SUCCESS;
709
int lookup_done(void *context)
711
struct lookup_context *ctxt = (struct lookup_context *) context;
712
int rv = close_parse(ctxt->parse);