#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>
#include <DirectoryService/DirectoryService.h>
#include "automount.h"
#include "automount_ds.h"
typedef int (*attr_callback_fn)(char *attrval, unsigned long attrval_len,
void *udata);
typedef enum {
DS_CB_KEEPGOING,
DS_CB_DONE,
DS_CB_REJECTED,
DS_CB_ERROR
} callback_ret_t;
typedef callback_ret_t (*callback_fn)(char *key, unsigned long key_len,
char *contents, unsigned long contents_len, void *udata);
static callback_ret_t mastermap_callback(char *key, unsigned long key_len,
char *contents, unsigned long contents_len, void *udata);
static callback_ret_t directmap_callback(char *key, unsigned long key_len,
char *contents, unsigned long contents_len, void *udata);
static callback_ret_t match_callback(char *key, unsigned long key_len,
char *contents, unsigned long contents_len, void *udata);
static callback_ret_t readdir_callback(char *key, unsigned long key_len,
char *contents, unsigned long contents_len, void *udata);
struct match_cbdata {
char *map;
char *key;
char **ds_line;
int *ds_len;
};
struct loadmaster_cbdata {
char *defopts;
char **stack;
char ***stkptr;
};
struct loaddirect_cbdata {
char *opts;
char *localmap;
char **stack;
char ***stkptr;
};
struct dir_cbdata {
struct dir_entry **list;
struct dir_entry *last;
int error;
};
static int ds_match(char *map, char *key, char **ds_line, int *ds_len);
static int ds_search(char *attr_to_match, char *value_to_match,
callback_fn callback, void *udata);
void
init_ds(__unused char **stack, __unused char ***stkptr)
{
}
int
getmapent_ds(char *key, char *map, struct mapline *ml,
__unused char **stack, __unused char ***stkptr,
bool_t *iswildcard, __unused bool_t isrestricted)
{
char *ds_line = NULL;
char *lp;
int ds_len, len;
int nserr;
if (trace > 1)
trace_prt(1, "getmapent_ds called\n");
if (trace > 1) {
trace_prt(1, "getmapent_ds: key=[ %s ]\n", key);
}
if (iswildcard)
*iswildcard = FALSE;
nserr = ds_match(map, key, &ds_line, &ds_len);
if (nserr) {
if (nserr == __NSW_NOTFOUND) {
if ((nserr = ds_match(map, "*", &ds_line,
&ds_len)))
goto done;
else {
if (iswildcard)
*iswildcard = TRUE;
}
} else
goto done;
}
if ((lp = strchr(ds_line, '#')) != NULL)
*lp = '\0';
len = strlen(ds_line);
if (len == 0) {
nserr = __NSW_NOTFOUND;
goto done;
}
lp = &ds_line[len - 1];
while (lp > ds_line && isspace(*lp))
*lp-- = '\0';
if (lp == ds_line) {
nserr = __NSW_NOTFOUND;
goto done;
}
(void) strncpy(ml->linebuf, ds_line, LINESZ);
unquote(ml->linebuf, ml->lineqbuf);
nserr = __NSW_SUCCESS;
done:
if (ds_line)
free((char *)ds_line);
if (trace > 1)
trace_prt(1, "getmapent_ds: exiting ...\n");
return (nserr);
}
static callback_ret_t
match_callback(char *key, unsigned long key_len, char *contents,
unsigned long contents_len, void *udata)
{
struct match_cbdata *temp = (struct match_cbdata *)udata;
char **ds_line = temp->ds_line;
int *ds_len = temp->ds_len;
if (trace > 1)
trace_prt(1, " match_callback called: key %.*s, contents %.*s\n",
(int)key_len, key, (int)contents_len, contents);
*ds_len = contents_len + 1;
if (*ds_len > LINESZ) {
pr_msg(
"DS map %s, entry for %.*s"
" is too long %d chars (max %d)",
temp->map, (int)key_len, key, *ds_len, LINESZ);
return (DS_CB_REJECTED);
}
*ds_line = (char *)malloc(*ds_len);
if (*ds_line == NULL) {
pr_msg("match_callback: malloc failed");
return (DS_CB_ERROR);
}
(void) memcpy(*ds_line, contents, contents_len);
(*ds_line)[contents_len] = '\0';
if (trace > 1)
trace_prt(1, " match_callback: found: %s\n", *ds_line);
return (DS_CB_DONE);
}
static int
ds_match(char *map, char *key, char **ds_line, int *ds_len)
{
int ret;
char *pattern;
struct match_cbdata cbdata;
if (trace > 1) {
trace_prt(1, "ds_match called\n");
trace_prt(1, "ds_match: key =[ %s ]\n", key);
}
if (asprintf(&pattern, "%s,automountMapName=%s", key, map) == -1) {
pr_msg("ds_match: malloc failed");
ret = __NSW_UNAVAIL;
goto done;
}
if (trace > 1)
trace_prt(1, " ds_match: Searching for %s\n", pattern);
cbdata.map = map;
cbdata.key = key;
cbdata.ds_line = ds_line;
cbdata.ds_len = ds_len;
ret = ds_search(kDSNAttrRecordName, pattern, match_callback,
(void *) &cbdata);
free(pattern);
if (trace > 1) {
if (ret == __NSW_NOTFOUND)
trace_prt(1, " ds_match: no entries found\n");
else if (ret != __NSW_UNAVAIL)
trace_prt(1,
" ds_match: ds_search FAILED\n", ret);
else
trace_prt(1, " ds_match: ds_search OK\n");
}
if (verbose) {
if (ret == __NSW_NOTFOUND)
pr_msg("ds_search failed");
}
done:
return (ret);
}
int
loadmaster_ds(char *mapname, char *defopts, char **stack, char ***stkptr)
{
int res;
struct loadmaster_cbdata master_cbdata;
if (trace > 1)
trace_prt(1, "loadmaster_ds called\n");
master_cbdata.defopts = defopts;
master_cbdata.stack = stack;
master_cbdata.stkptr = stkptr;
if (trace > 1)
trace_prt(1, "loadmaster_ds: Requesting list in %s\n",
mapname);
res = ds_search(kDS1AttrMetaAutomountMap, mapname,
mastermap_callback, (void *) &master_cbdata);
if (trace > 1)
trace_prt(1,
"loadmaster_ds: ds_search just returned: %d\n",
res);
return (res);
}
int
loaddirect_ds(char *nsmap, char *localmap, char *opts,
char **stack, char ***stkptr)
{
struct loaddirect_cbdata direct_cbdata;
if (trace > 1) {
trace_prt(1, "loaddirect_ds called\n");
}
if (trace > 1)
trace_prt(1, "loaddirect_ds: Requesting list for %s in %s\n",
localmap, nsmap);
direct_cbdata.opts = opts;
direct_cbdata.localmap = localmap;
direct_cbdata.stack = stack;
direct_cbdata.stkptr = stkptr;
return (ds_search(kDS1AttrMetaAutomountMap, nsmap,
directmap_callback, (void *) &direct_cbdata));
}
static callback_ret_t
mastermap_callback(char *key, unsigned long key_len, char *contents,
unsigned long contents_len, void *udata)
{
char *pmap, *opts;
char dir[LINESZ], map[LINESZ], qbuff[LINESZ];
struct loadmaster_cbdata *temp = (struct loadmaster_cbdata *)udata;
char *defopts = temp->defopts;
char **stack = temp->stack;
char ***stkptr = temp->stkptr;
int i;
if (trace > 1)
trace_prt(1, " mastermap_callback called: key %.*s, contents %.*s\n",
(int)key_len, key, (int)contents_len, contents);
if (key_len >= LINESZ || contents_len >= LINESZ)
return (DS_CB_KEEPGOING);
if (key_len < 2 || contents_len < 2)
return (DS_CB_KEEPGOING);
i = contents_len;
while (i > 0 && isspace((unsigned char)*contents)) {
contents++;
i--;
}
if (*contents == '\0')
return (DS_CB_KEEPGOING);
if (isspace((unsigned char)*key) || *key == '#')
return (DS_CB_KEEPGOING);
(void) strncpy(dir, key, key_len);
dir[key_len] = '\0';
if (trace > 1)
trace_prt(1, "mastermap_callback: dir= [ %s ]\n", dir);
for (i = 0; i < LINESZ; i++)
qbuff[i] = ' ';
if (macro_expand("", dir, qbuff, sizeof (dir))) {
pr_msg(
"%s in Directory Services map: entry too long (max %d chars)",
dir, sizeof (dir) - 1);
return (DS_CB_KEEPGOING);
}
(void) strncpy(map, contents, contents_len);
map[contents_len] = '\0';
if (trace > 1)
trace_prt(1, "mastermap_callback: map= [ %s ]\n", map);
if (macro_expand("", map, qbuff, sizeof (map))) {
pr_msg(
"%s in Directory Services map: entry too long (max %d chars)",
map, sizeof (map) - 1);
return (DS_CB_KEEPGOING);
}
pmap = map;
while (*pmap && isspace(*pmap))
pmap++;
opts = pmap;
while (*opts && !isspace(*opts))
opts++;
if (*opts) {
*opts++ = '\0';
while (*opts && isspace(*opts))
opts++;
if (*opts == '-')
opts++;
else
opts = defopts;
}
if (strcspn(opts, " \t") == strlen(opts)) {
if (trace > 1)
trace_prt(1,
"mastermap_callback: dir=[ %s ], pmap=[ %s ]\n",
dir, pmap);
dirinit(dir, pmap, opts, 0, stack, stkptr);
} else {
pr_msg(
"Warning: invalid entry for %s in Directory Services ignored.\n",
dir);
}
if (trace > 1)
trace_prt(1, "mastermap_callback exiting...\n");
return (DS_CB_KEEPGOING);
}
static callback_ret_t
directmap_callback(char *key, unsigned long key_len, __unused char *contents,
__unused unsigned long contents_len, void *udata)
{
char dir[MAXFILENAMELEN+1];
struct loaddirect_cbdata *temp = (struct loaddirect_cbdata *)udata;
char *opts = temp->opts;
char *localmap = temp->localmap;
char **stack = temp->stack;
char ***stkptr = temp->stkptr;
if (trace > 1)
trace_prt(1, " directmap_callback called: key %.*s\n",
(int)key_len, key);
if (key_len > MAXFILENAMELEN || key_len < 2)
return (DS_CB_KEEPGOING);
if (isspace((unsigned char)*key) || *key == '#')
return (DS_CB_KEEPGOING);
(void) strncpy(dir, key, key_len);
dir[key_len] = '\0';
dirinit(dir, localmap, opts, 1, stack, stkptr);
return (DS_CB_KEEPGOING);
}
int
getmapkeys_ds(char *nsmap, struct dir_entry **list, int *error,
int *cache_time, __unused char **stack, __unused char ***stkptr)
{
int res;
struct dir_cbdata readdir_cbdata;
if (trace > 1)
trace_prt(1, "getmapkeys_ds called\n");
*cache_time = RDDIR_CACHE_TIME;
*error = 0;
if (trace > 1)
trace_prt(1, "getmapkeys_ds: Requesting list in %s\n",
nsmap);
readdir_cbdata.list = list;
readdir_cbdata.last = NULL;
res = ds_search(kDS1AttrMetaAutomountMap, nsmap, readdir_callback,
(void *) &readdir_cbdata);
if (trace > 1)
trace_prt(1, " getmapkeys_ds: ds_search returned %d\n",
res);
if (readdir_cbdata.error)
*error = readdir_cbdata.error;
if (res != __NSW_SUCCESS) {
if (*error == 0)
*error = EIO;
}
return (res);
}
static callback_ret_t
readdir_callback(char *inkey, unsigned long inkeylen, __unused char *contents,
__unused unsigned long contents_len, void *udata)
{
struct dir_cbdata *temp = (struct dir_cbdata *)udata;
struct dir_entry **list = temp->list;
struct dir_entry *last = temp->last;
char key[MAXFILENAMELEN+1];
if (trace > 1)
trace_prt(1, " readdir_callback called: key %.*s\n",
(int)inkeylen, key);
if (inkeylen > MAXFILENAMELEN)
return (DS_CB_KEEPGOING);
if (inkeylen == 0 || isspace((unsigned char)*inkey) || *inkey == '#')
return (DS_CB_KEEPGOING);
strncpy(key, inkey, inkeylen);
key[inkeylen] = '\0';
if (key[0] == '*' && key[1] == '\0')
return (DS_CB_KEEPGOING);
if (add_dir_entry(key, list, &last)) {
temp->error = ENOMEM;
return (DS_CB_ERROR);
}
temp->last = last;
temp->error = 0;
if (trace > 1)
trace_prt(1, "readdir_callback returning DS_CB_KEEPGOING...\n");
return (DS_CB_KEEPGOING);
}
static callback_ret_t
ds_process_record_attributes(tDirReference session, tDirNodeReference node_ref,
tDataBufferPtr buffer, tAttributeListRef attr_list_ref,
tRecordEntryPtr entry, callback_fn callback, void *udata)
{
unsigned long i;
tAttributeValueListRef key_value_list_ref = 0;
tAttributeEntry *key_attr_entry_p = NULL;
tAttributeValueEntry *key_value_entry_p = NULL;
char *key;
unsigned long key_len;
tAttributeValueListRef contents_value_list_ref = 0;
tAttributeEntry *contents_attr_entry_p = NULL;
tAttributeValueEntry *contents_value_entry_p = NULL;
char *contents;
unsigned long contents_len;
callback_ret_t ret;
if (trace > 1) {
trace_prt(1,
"ds_process_record_attributes: entry->fRecordAttributeCount=[ %d ]\n",
entry->fRecordAttributeCount);
}
for (i = 1; i <= entry->fRecordAttributeCount; i++) {
tAttributeValueListRef value_list_ref;
tAttributeEntry *attr_entry_p;
char *attrname;
tAttributeValueEntry *value_entry_p;
tDirStatus status;
status = dsGetAttributeEntry(node_ref, buffer, attr_list_ref,
i, &value_list_ref, &attr_entry_p);
if (status != eDSNoErr) {
pr_msg("ds_process_record_attributes: dsGetAttributeEntry failed: %s (%d)",
dsCopyDirStatusName(status), status);
return (DS_CB_ERROR);
}
attrname = attr_entry_p->fAttributeSignature.fBufferData;
if (trace > 1)
trace_prt(1,
"ds_process_record_attributes: attrname=[ %s ]\n",
attrname);
if (strcmp(attrname, kDSNAttrRecordName) == 0) {
status = dsGetAttributeValue(node_ref, buffer, 1,
value_list_ref, &value_entry_p);
if (status != eDSNoErr) {
pr_msg("ds_process_record_attributes: dsGetAttributeValue failed: %s (%d)",
dsCopyDirStatusName(status), status);
dsDeallocAttributeEntry(session, attr_entry_p);
dsCloseAttributeValueList(value_list_ref);
return (DS_CB_ERROR);
}
key_value_entry_p = value_entry_p;
key_attr_entry_p = attr_entry_p;
key_value_list_ref = value_list_ref;
} else if (strcmp(attrname, kDSNAttrAutomountInformation) == 0) {
status = dsGetAttributeValue(node_ref, buffer, 1,
value_list_ref, &value_entry_p);
if (status != eDSNoErr) {
pr_msg("ds_process_record_attributes: dsGetAttributeValue failed: %s (%d)",
dsCopyDirStatusName(status), status);
dsDeallocAttributeEntry(session, attr_entry_p);
dsCloseAttributeValueList(value_list_ref);
return (DS_CB_ERROR);
}
contents_value_entry_p = value_entry_p;
contents_attr_entry_p = attr_entry_p;
contents_value_list_ref = value_list_ref;
} else {
dsDeallocAttributeEntry(session, attr_entry_p);
dsCloseAttributeValueList(value_list_ref);
}
}
if (key_value_entry_p != NULL && contents_value_entry_p != NULL) {
key = key_value_entry_p->fAttributeValueData.fBufferData;
key_len = key_value_entry_p->fAttributeValueData.fBufferLength;
contents = contents_value_entry_p->fAttributeValueData.fBufferData;
contents_len = contents_value_entry_p->fAttributeValueData.fBufferLength;
ret = (*callback)(key, key_len, contents, contents_len, udata);
} else {
ret = DS_CB_REJECTED;
}
if (key_value_entry_p != NULL) {
dsDeallocAttributeValueEntry(session, key_value_entry_p);
dsDeallocAttributeEntry(session, key_attr_entry_p);
dsCloseAttributeValueList(key_value_list_ref);
}
if (contents_value_entry_p != NULL) {
dsDeallocAttributeValueEntry(session, contents_value_entry_p);
dsDeallocAttributeEntry(session, contents_attr_entry_p);
dsCloseAttributeValueList(contents_value_list_ref);
}
return (ret);
}
int
ds_get_root_level_node(tDirReference session, tDirNodeReference *node_refp)
{
static unsigned long dir_node_bufsize = 2*1024;
tDataBufferPtr buffer;
tDirStatus status;
unsigned long num_results;
tContextData context;
tDataListPtr node_path;
for (;;) {
buffer = dsDataBufferAllocate(session, dir_node_bufsize);
if (buffer == NULL) {
pr_msg("ds_get_search_node: malloc failed");
return (__NSW_UNAVAIL);
}
num_results = 1;
context = NULL;
status = dsFindDirNodes(session, buffer, NULL,
eDSSearchNodeName, &num_results, &context);
if (status != eDSBufferTooSmall) {
break;
}
dsDataBufferDeAllocate(session, buffer);
dir_node_bufsize = 2*dir_node_bufsize;
}
if (status != eDSNoErr) {
dsDataBufferDeAllocate(session, buffer);
pr_msg(
"ds_get_search_node: can't find default search node: %s (%d)",
dsCopyDirStatusName(status), status);
return (__NSW_UNAVAIL);
}
status = dsGetDirNodeName(session, buffer, 1, &node_path);
dsDataBufferDeAllocate(session, buffer);
if (status != eDSNoErr) {
pr_msg(
"ds_get_search_node: can't get reference to default search node: %s (%d)",
dsCopyDirStatusName(status), status);
return (__NSW_UNAVAIL);
}
status = dsOpenDirNode(session, node_path, node_refp);
dsDataListDeallocate(session, node_path);
free(node_path);
if (status != eDSNoErr) {
pr_msg(
"ds_get_search_node: can't open root level node for search: %s (%d)",
dsCopyDirStatusName(status), status);
return (__NSW_UNAVAIL);
}
return (__NSW_SUCCESS);
}
static int
ds_search(char *attr_to_match, char *value_to_match, callback_fn callback,
void *udata)
{
int ret;
tDirReference session;
tDirStatus status;
tDirNodeReference node_ref;
int dir_node_open = 0;
tDataNodePtr pattern_data_node = NULL;
tDataListPtr record_type = NULL;
tDataNodePtr match_type = NULL;
tDataListPtr requested_attributes = NULL;
unsigned long num_results;
unsigned long i;
tContextData context;
tAttributeListRef attr_list_ref;
tRecordEntryPtr record_entry_p;
callback_ret_t callback_ret;
static unsigned long attr_bufsize = 2*1024;
tDataBufferPtr buffer = NULL;
if (dsOpenDirService(&session) != eDSNoErr)
return (__NSW_UNAVAIL);
ret = ds_get_root_level_node(session, &node_ref);
if (ret != __NSW_SUCCESS)
goto done;
dir_node_open = 1;
pattern_data_node = dsDataNodeAllocateString(session, value_to_match);
if (pattern_data_node == NULL) {
pr_msg("ds_search: malloc failed");
ret = __NSW_UNAVAIL;
goto done;
}
record_type = dsBuildListFromStrings(session,
kDSStdRecordTypeAutomount, NULL);
if (record_type == NULL) {
pr_msg(
"ds_search: can't build record type list: %s (%d)",
dsCopyDirStatusName(status), status);
ret = __NSW_UNAVAIL;
goto done;
}
match_type = dsDataNodeAllocateString(session, attr_to_match);
if (match_type == NULL) {
pr_msg("ds_search: malloc failed");
ret = __NSW_UNAVAIL;
goto done;
}
requested_attributes = dsBuildListFromStrings(session,
kDSNAttrRecordName, kDSNAttrAutomountInformation, NULL);
if (requested_attributes == NULL) {
pr_msg("ds_search: malloc failed");
ret = __NSW_UNAVAIL;
goto done;
}
ret = __NSW_NOTFOUND;
num_results = 0;
context = NULL;
do {
for (;;) {
buffer = dsDataBufferAllocate(session, attr_bufsize);
if (buffer == NULL) {
pr_msg("ds_match: malloc failed");
ret = __NSW_UNAVAIL;
goto done;
}
status = dsDoAttributeValueSearchWithData(node_ref,
buffer, record_type, match_type, eDSExact,
pattern_data_node, requested_attributes, FALSE,
&num_results, &context);
if (status != eDSBufferTooSmall) {
break;
}
dsDataBufferDeAllocate(session, buffer);
buffer = NULL;
attr_bufsize = 2*attr_bufsize;
}
if (status != eDSNoErr) {
pr_msg("ds_search: can't get record list: %s (%d)",
dsCopyDirStatusName(status), status);
ret = __NSW_UNAVAIL;
goto done;
}
for (i = 1; i <= num_results; i++) {
status = dsGetRecordEntry(node_ref, buffer, i,
&attr_list_ref, &record_entry_p);
if (status != eDSNoErr) {
pr_msg("ds_search: can't get record entry: %s (%d)",
dsCopyDirStatusName(status), status);
if (context != NULL) {
dsReleaseContinueData(session, context);
context = NULL;
}
ret = __NSW_UNAVAIL;
break;
}
callback_ret = ds_process_record_attributes(session,
node_ref, buffer, attr_list_ref, record_entry_p,
callback, udata);
dsCloseAttributeList(attr_list_ref);
dsDeallocRecordEntry(session, record_entry_p);
if (callback_ret == DS_CB_KEEPGOING) {
ret = __NSW_SUCCESS;
} else if (callback_ret == DS_CB_DONE) {
ret = __NSW_SUCCESS;
if (context != NULL) {
dsReleaseContinueData(session, context);
context = NULL;
}
break;
} else if (callback_ret == DS_CB_ERROR) {
ret = __NSW_UNAVAIL;
if (context != NULL) {
dsReleaseContinueData(session, context);
context = NULL;
}
break;
}
}
} while (context != NULL);
done:
if (buffer != NULL)
dsDataBufferDeAllocate(session, buffer);
if (requested_attributes != NULL) {
dsDataListDeallocate(session, requested_attributes);
free(requested_attributes);
}
if (match_type != NULL)
dsDataNodeDeAllocate(session, match_type);
if (record_type != NULL) {
dsDataListDeallocate(session, record_type);
free(record_type);
}
if (pattern_data_node != NULL)
dsDataNodeDeAllocate(session, pattern_data_node);
if (dir_node_open)
dsCloseDirNode(node_ref);
dsCloseDirService(session);
return (ret);
}