4
* Module for Linux automountd to access a NIS+ automount map
10
#include <sys/types.h>
15
#include <rpcsvc/nis.h>
18
#include "automount.h"
21
#define MAPFMT_DEFAULT "sun"
23
#define MODPREFIX "lookup(nisplus): "
25
struct lookup_context {
26
const char *domainname;
28
struct parse_mod *parse;
31
int lookup_version = AUTOFS_LOOKUP_VERSION; /* Required by protocol */
33
int lookup_init(const char *mapfmt, int argc, const char *const *argv, void **context)
35
struct lookup_context *ctxt;
36
char buf[MAX_ERR_BUF];
40
ctxt = malloc(sizeof(struct lookup_context));
42
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
43
logerr(MODPREFIX "%s", estr);
49
logmsg(MODPREFIX "No map name");
52
ctxt->mapname = argv[0];
55
* nis_local_directory () returns a pointer to a static buffer.
56
* We don't need to copy or free it.
58
ctxt->domainname = nis_local_directory();
59
if (!ctxt->domainname) {
61
logmsg(MODPREFIX "NIS+ domain not set");
66
mapfmt = MAPFMT_DEFAULT;
68
ctxt->parse = open_parse(mapfmt, MODPREFIX, argc - 1, argv + 1);
71
logerr(MODPREFIX "failed to open parse context");
79
int lookup_read_master(struct master *master, time_t age, void *context)
81
struct lookup_context *ctxt = (struct lookup_context *) context;
82
unsigned int timeout = master->default_timeout;
83
unsigned int logging = master->default_logging;
84
unsigned int logopt = master->logopt;
88
unsigned int current, result_count;
91
char buf[MAX_ERR_BUF];
94
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
95
tablename = malloc(strlen(ctxt->mapname) + strlen(ctxt->domainname) + 20);
97
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
98
logerr(MODPREFIX "malloc: %s", estr);
99
pthread_setcancelstate(cur_state, NULL);
100
return NSS_STATUS_UNAVAIL;
102
sprintf(tablename, "%s.org_dir.%s", ctxt->mapname, ctxt->domainname);
104
/* check that the table exists */
105
result = nis_lookup(tablename, FOLLOW_PATH | FOLLOW_LINKS);
106
if (result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS) {
107
nis_freeresult(result);
109
MODPREFIX "couldn't locate nis+ table %s", ctxt->mapname);
111
pthread_setcancelstate(cur_state, NULL);
112
return NSS_STATUS_NOTFOUND;
115
sprintf(tablename, "[],%s.org_dir.%s", ctxt->mapname, ctxt->domainname);
117
result = nis_list(tablename, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
118
if (result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS) {
119
nis_freeresult(result);
121
MODPREFIX "couldn't enumrate nis+ map %s", ctxt->mapname);
123
pthread_setcancelstate(cur_state, NULL);
124
return NSS_STATUS_UNAVAIL;
128
result_count = NIS_RES_NUMOBJ(result);
130
while (result_count--) {
131
this = &result->objects.objects_val[current++];
132
path = ENTRY_VAL(this, 0);
134
* Ignore keys beginning with '+' as plus map
135
* inclusion is only valid in file maps.
140
ent = ENTRY_VAL(this, 1);
142
len = ENTRY_LEN(this, 0) + 1 + ENTRY_LEN(this, 1) + 2;
143
buffer = malloc(len);
145
logerr(MODPREFIX "could not malloc parse buffer");
148
memset(buffer, 0, len);
150
strcat(buffer, path);
154
master_parse_entry(buffer, timeout, logging, age);
159
nis_freeresult(result);
161
pthread_setcancelstate(cur_state, NULL);
163
return NSS_STATUS_SUCCESS;
166
int lookup_read_map(struct autofs_point *ap, time_t age, void *context)
168
struct lookup_context *ctxt = (struct lookup_context *) context;
169
struct map_source *source;
170
struct mapent_cache *mc;
174
unsigned int current, result_count;
176
char buf[MAX_ERR_BUF];
179
source = ap->entry->current;
180
ap->entry->current = NULL;
181
master_source_current_signal(ap->entry);
184
* If we don't need to create directories then there's no use
185
* reading the map. We always need to read the whole map for
186
* direct mounts in order to mount the triggers.
188
if (!(ap->flags & MOUNT_FLAG_GHOST) && ap->type != LKP_DIRECT)
189
return NSS_STATUS_SUCCESS;
193
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
194
tablename = malloc(strlen(ctxt->mapname) + strlen(ctxt->domainname) + 20);
196
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
197
logerr(MODPREFIX "malloc: %s", estr);
198
pthread_setcancelstate(cur_state, NULL);
199
return NSS_STATUS_UNAVAIL;
201
sprintf(tablename, "%s.org_dir.%s", ctxt->mapname, ctxt->domainname);
203
/* check that the table exists */
204
result = nis_lookup(tablename, FOLLOW_PATH | FOLLOW_LINKS);
205
if (result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS) {
206
nis_freeresult(result);
208
MODPREFIX "couldn't locate nis+ table %s", ctxt->mapname);
210
pthread_setcancelstate(cur_state, NULL);
211
return NSS_STATUS_NOTFOUND;
214
sprintf(tablename, "[],%s.org_dir.%s", ctxt->mapname, ctxt->domainname);
216
result = nis_list(tablename, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
217
if (result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS) {
218
nis_freeresult(result);
220
MODPREFIX "couldn't enumrate nis+ map %s", ctxt->mapname);
222
pthread_setcancelstate(cur_state, NULL);
223
return NSS_STATUS_UNAVAIL;
227
result_count = NIS_RES_NUMOBJ(result);
229
while (result_count--) {
233
this = &result->objects.objects_val[current++];
234
key = ENTRY_VAL(this, 0);
235
len = ENTRY_LEN(this, 0);
238
* Ignore keys beginning with '+' as plus map
239
* inclusion is only valid in file maps.
244
s_key = sanitize_path(key, len, ap->type, ap->logopt);
248
mapent = ENTRY_VAL(this, 1);
251
cache_update(mc, source, s_key, mapent, age);
257
nis_freeresult(result);
262
pthread_setcancelstate(cur_state, NULL);
264
return NSS_STATUS_SUCCESS;
267
static int lookup_one(struct autofs_point *ap,
268
const char *key, int key_len,
269
struct lookup_context *ctxt)
271
struct map_source *source;
272
struct mapent_cache *mc;
277
time_t age = time(NULL);
279
char buf[MAX_ERR_BUF];
281
source = ap->entry->current;
282
ap->entry->current = NULL;
283
master_source_current_signal(ap->entry);
287
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
288
tablename = malloc(strlen(key) + strlen(ctxt->mapname) +
289
strlen(ctxt->domainname) + 20);
291
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
292
logerr(MODPREFIX "malloc: %s", estr);
293
pthread_setcancelstate(cur_state, NULL);
296
sprintf(tablename, "[key=%s],%s.org_dir.%s", key, ctxt->mapname,
299
result = nis_list(tablename, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
300
if (result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS) {
301
nis_error rs = result->status;
302
nis_freeresult(result);
304
pthread_setcancelstate(cur_state, NULL);
305
if (rs == NIS_NOTFOUND ||
306
rs == NIS_S_NOTFOUND ||
314
this = NIS_RES_OBJECT(result);
315
mapent = ENTRY_VAL(this, 1);
317
ret = cache_update(mc, source, key, mapent, age);
320
nis_freeresult(result);
322
pthread_setcancelstate(cur_state, NULL);
327
static int lookup_wild(struct autofs_point *ap, struct lookup_context *ctxt)
329
struct map_source *source;
330
struct mapent_cache *mc;
335
time_t age = time(NULL);
337
char buf[MAX_ERR_BUF];
339
source = ap->entry->current;
340
ap->entry->current = NULL;
341
master_source_current_signal(ap->entry);
345
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
346
tablename = malloc(strlen(ctxt->mapname) + strlen(ctxt->domainname) + 20);
348
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
349
logerr(MODPREFIX "malloc: %s", estr);
350
pthread_setcancelstate(cur_state, NULL);
353
sprintf(tablename, "[key=*],%s.org_dir.%s", ctxt->mapname,
356
result = nis_list(tablename, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
357
if (result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS) {
358
nis_error rs = result->status;
359
nis_freeresult(result);
361
pthread_setcancelstate(cur_state, NULL);
362
if (rs == NIS_NOTFOUND ||
363
rs == NIS_S_NOTFOUND ||
370
this = NIS_RES_OBJECT(result);
371
mapent = ENTRY_VAL(this, 1);
373
ret = cache_update(mc, source, "*", mapent, age);
376
nis_freeresult(result);
378
pthread_setcancelstate(cur_state, NULL);
383
static int check_map_indirect(struct autofs_point *ap,
384
char *key, int key_len,
385
struct lookup_context *ctxt)
387
struct map_source *source;
388
struct mapent_cache *mc;
389
struct mapent *me, *exists;
390
time_t now = time(NULL);
394
source = ap->entry->current;
395
ap->entry->current = NULL;
396
master_source_current_signal(ap->entry);
400
master_source_current_wait(ap->entry);
401
ap->entry->current = source;
403
/* check map and if change is detected re-read map */
404
ret = lookup_one(ap, key, key_len, ctxt);
406
return NSS_STATUS_NOTFOUND;
410
* If the server is down and the entry exists in the cache
411
* and belongs to this map return success and use the entry.
413
exists = cache_lookup(mc, key);
414
if (exists && exists->source == source)
415
return NSS_STATUS_SUCCESS;
418
MODPREFIX "lookup for %s failed: %s",
419
key, nis_sperrno(-ret));
421
return NSS_STATUS_UNAVAIL;
425
t_last_read = ap->exp_runfreq + 1;
426
me = cache_lookup_first(mc);
428
if (me->source == source) {
429
t_last_read = now - me->age;
432
me = cache_lookup_next(mc, me);
434
exists = cache_lookup_distinct(mc, key);
435
/* Not found in the map but found in the cache */
436
if (exists && exists->source == source && ret & CHE_MISSING) {
437
if (exists->mapent) {
438
free(exists->mapent);
439
exists->mapent = NULL;
446
if (t_last_read > ap->exp_runfreq && ret & CHE_UPDATED)
449
if (ret == CHE_MISSING) {
450
int wild = CHE_MISSING;
453
master_source_current_wait(ap->entry);
454
ap->entry->current = source;
456
wild = lookup_wild(ap, ctxt);
458
* Check for map change and update as needed for
459
* following cache lookup.
462
we = cache_lookup_distinct(mc, "*");
464
/* Wildcard entry existed and is now gone */
465
if (we->source == source && wild & CHE_MISSING) {
466
cache_delete(mc, "*");
470
/* Wildcard not in map but now is */
471
if (wild & (CHE_OK | CHE_UPDATED))
476
if (wild & (CHE_UPDATED | CHE_OK))
477
return NSS_STATUS_SUCCESS;
480
if (ret == CHE_MISSING)
481
return NSS_STATUS_NOTFOUND;
483
return NSS_STATUS_SUCCESS;
486
int lookup_mount(struct autofs_point *ap, const char *name, int name_len, void *context)
488
struct lookup_context *ctxt = (struct lookup_context *) context;
489
struct map_source *source;
490
struct mapent_cache *mc;
491
char key[KEY_MAX_LEN + 1];
499
source = ap->entry->current;
500
ap->entry->current = NULL;
501
master_source_current_signal(ap->entry);
505
debug(ap->logopt, MODPREFIX "looking up %s", name);
507
key_len = snprintf(key, KEY_MAX_LEN + 1, "%s", name);
508
if (key_len > KEY_MAX_LEN)
509
return NSS_STATUS_NOTFOUND;
511
/* Check if we recorded a mount fail for this key anywhere */
512
me = lookup_source_mapent(ap, key, LKP_DISTINCT);
514
if (me->status >= time(NULL)) {
515
cache_unlock(me->mc);
516
return NSS_STATUS_NOTFOUND;
518
struct mapent_cache *smc = me->mc;
525
cache_writelock(smc);
526
sme = cache_lookup_distinct(smc, key);
527
/* Negative timeout expired for non-existent entry. */
528
if (sme && !sme->mapent)
529
cache_delete(smc, key);
536
* We can't check the direct mount map as if it's not in
537
* the map cache already we never get a mount lookup, so
538
* we never know about it.
540
if (ap->type == LKP_INDIRECT && *key != '/') {
544
me = cache_lookup_distinct(mc, key);
546
lkp_key = strdup(me->multi->key);
548
lkp_key = strdup(key);
552
return NSS_STATUS_UNKNOWN;
554
master_source_current_wait(ap->entry);
555
ap->entry->current = source;
557
status = check_map_indirect(ap, lkp_key, strlen(lkp_key), ctxt);
563
me = cache_lookup(mc, key);
564
/* Stale mapent => check for entry in alternate source or wildcard */
565
if (me && !me->mapent) {
566
while ((me = cache_lookup_key_next(me)))
567
if (me->source == source)
570
me = cache_lookup_distinct(mc, "*");
572
if (me && (me->source == source || *me->key == '/')) {
573
mapent_len = strlen(me->mapent);
574
mapent = malloc(mapent_len + 1);
575
strcpy(mapent, me->mapent);
580
return NSS_STATUS_TRYAGAIN;
582
master_source_current_wait(ap->entry);
583
ap->entry->current = source;
585
debug(ap->logopt, MODPREFIX "%s -> %s", key, mapent);
586
ret = ctxt->parse->parse_mount(ap, key, key_len,
587
mapent, ctxt->parse->context);
589
time_t now = time(NULL);
593
me = cache_lookup_distinct(mc, key);
595
rv = cache_update(mc, source, key, NULL, now);
596
if (rv != CHE_FAIL) {
597
me = cache_lookup_distinct(mc, key);
598
me->status = time(NULL) + ap->negative_timeout;
602
return NSS_STATUS_TRYAGAIN;
606
return NSS_STATUS_SUCCESS;
609
int lookup_done(void *context)
611
struct lookup_context *ctxt = (struct lookup_context *) context;
612
int rv = close_parse(ctxt->parse);