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;
424
pthread_cleanup_push(cache_lock_cleanup, mc);
426
t_last_read = ap->exp_runfreq + 1;
427
me = cache_lookup_first(mc);
429
if (me->source == source) {
430
t_last_read = now - me->age;
433
me = cache_lookup_next(mc, me);
435
exists = cache_lookup_distinct(mc, key);
436
/* Not found in the map but found in the cache */
437
if (exists && exists->source == source && ret & CHE_MISSING) {
438
if (exists->mapent) {
439
free(exists->mapent);
440
exists->mapent = NULL;
445
pthread_cleanup_pop(1);
447
if (t_last_read > ap->exp_runfreq && ret & CHE_UPDATED)
450
if (ret == CHE_MISSING) {
451
int wild = CHE_MISSING;
454
master_source_current_wait(ap->entry);
455
ap->entry->current = source;
457
wild = lookup_wild(ap, ctxt);
459
* Check for map change and update as needed for
460
* following cache lookup.
462
pthread_cleanup_push(cache_lock_cleanup, mc);
464
we = cache_lookup_distinct(mc, "*");
466
/* Wildcard entry existed and is now gone */
467
if (we->source == source && wild & CHE_MISSING) {
468
cache_delete(mc, "*");
472
/* Wildcard not in map but now is */
473
if (wild & (CHE_OK | CHE_UPDATED))
476
pthread_cleanup_pop(1);
478
if (wild & (CHE_UPDATED | CHE_OK))
479
return NSS_STATUS_SUCCESS;
482
if (ret == CHE_MISSING)
483
return NSS_STATUS_NOTFOUND;
485
return NSS_STATUS_SUCCESS;
488
int lookup_mount(struct autofs_point *ap, const char *name, int name_len, void *context)
490
struct lookup_context *ctxt = (struct lookup_context *) context;
491
struct map_source *source;
492
struct mapent_cache *mc;
493
char key[KEY_MAX_LEN + 1];
501
source = ap->entry->current;
502
ap->entry->current = NULL;
503
master_source_current_signal(ap->entry);
507
debug(ap->logopt, MODPREFIX "looking up %s", name);
509
key_len = snprintf(key, KEY_MAX_LEN + 1, "%s", name);
510
if (key_len > KEY_MAX_LEN)
511
return NSS_STATUS_NOTFOUND;
513
/* Check if we recorded a mount fail for this key anywhere */
514
me = lookup_source_mapent(ap, key, LKP_DISTINCT);
516
if (me->status >= time(NULL)) {
517
cache_unlock(me->mc);
518
return NSS_STATUS_NOTFOUND;
521
/* Negative timeout expired for non-existent entry. */
523
cache_delete(me->mc, key);
525
cache_unlock(me->mc);
529
* We can't check the direct mount map as if it's not in
530
* the map cache already we never get a mount lookup, so
531
* we never know about it.
533
if (ap->type == LKP_INDIRECT && *key != '/') {
537
me = cache_lookup_distinct(mc, key);
539
lkp_key = strdup(me->multi->key);
541
lkp_key = strdup(key);
545
return NSS_STATUS_UNKNOWN;
547
master_source_current_wait(ap->entry);
548
ap->entry->current = source;
550
status = check_map_indirect(ap, lkp_key, strlen(lkp_key), ctxt);
556
me = cache_lookup(mc, key);
557
/* Stale mapent => check for entry in alternate source or wildcard */
558
if (me && !me->mapent) {
559
while ((me = cache_lookup_key_next(me)))
560
if (me->source == source)
563
me = cache_lookup_distinct(mc, "*");
565
if (me && (me->source == source || *me->key == '/')) {
566
mapent_len = strlen(me->mapent);
567
mapent = malloc(mapent_len + 1);
568
strcpy(mapent, me->mapent);
573
return NSS_STATUS_TRYAGAIN;
575
master_source_current_wait(ap->entry);
576
ap->entry->current = source;
578
debug(ap->logopt, MODPREFIX "%s -> %s", key, mapent);
579
ret = ctxt->parse->parse_mount(ap, key, key_len,
580
mapent, ctxt->parse->context);
582
time_t now = time(NULL);
586
me = cache_lookup_distinct(mc, key);
588
rv = cache_update(mc, source, key, NULL, now);
589
if (rv != CHE_FAIL) {
590
me = cache_lookup_distinct(mc, key);
591
me->status = time(NULL) + ap->negative_timeout;
595
return NSS_STATUS_TRYAGAIN;
599
return NSS_STATUS_SUCCESS;
602
int lookup_done(void *context)
604
struct lookup_context *ctxt = (struct lookup_context *) context;
605
int rv = close_parse(ctxt->parse);