diff options
Diffstat (limited to 'src/responder/autofs/autofssrv_cmd.c')
-rw-r--r-- | src/responder/autofs/autofssrv_cmd.c | 961 |
1 files changed, 961 insertions, 0 deletions
diff --git a/src/responder/autofs/autofssrv_cmd.c b/src/responder/autofs/autofssrv_cmd.c new file mode 100644 index 0000000..7c80909 --- /dev/null +++ b/src/responder/autofs/autofssrv_cmd.c @@ -0,0 +1,961 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2012 Red Hat + + Autofs responder: commands + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <talloc.h> + +#include "util/util.h" +#include "responder/common/responder.h" +#include "responder/common/responder_packet.h" +#include "responder/common/cache_req/cache_req.h" +#include "responder/autofs/autofs_private.h" +#include "db/sysdb.h" +#include "db/sysdb_autofs.h" +#include "confdb/confdb.h" +#include "sss_iface/sss_iface_async.h" +#include "util/sss_ptr_hash.h" + +static int autofs_cmd_send_error(struct autofs_cmd_ctx *cmdctx, int err) +{ + return sss_cmd_send_error(cmdctx->cli_ctx, err); +} + +static int +autofs_cmd_send_empty(struct autofs_cmd_ctx *cmdctx) +{ + return sss_cmd_send_empty(cmdctx->cli_ctx); +} + +static int +autofs_cmd_done(struct autofs_cmd_ctx *cmdctx, int ret) +{ + switch (ret) { + case EOK: + /* all fine, just return here */ + break; + + case ENOENT: + ret = autofs_cmd_send_empty(cmdctx); + if (ret) { + return EFAULT; + } + sss_cmd_done(cmdctx->cli_ctx, cmdctx); + break; + + case EAGAIN: + /* async processing, just return here */ + break; + + case EFAULT: + /* very bad error */ + return EFAULT; + + default: + ret = autofs_cmd_send_error(cmdctx, ret); + if (ret) { + return EFAULT; + } + sss_cmd_done(cmdctx->cli_ctx, cmdctx); + break; + } + + return EOK; +} + +static errno_t +autofs_fill_entry(struct ldb_message *entry, struct sss_packet *packet, size_t *rp) +{ + errno_t ret; + const char *key; + size_t keylen; + const char *value; + size_t valuelen; + uint8_t *body; + size_t blen; + size_t len; + + key = ldb_msg_find_attr_as_string(entry, SYSDB_AUTOFS_ENTRY_KEY, NULL); + value = ldb_msg_find_attr_as_string(entry, SYSDB_AUTOFS_ENTRY_VALUE, NULL); + if (!key || !value) { + DEBUG(SSSDBG_MINOR_FAILURE, "Incomplete entry\n"); + return EINVAL; + } + + keylen = 1 + strlen(key); + valuelen = 1 + strlen(value); + len = sizeof(uint32_t) + sizeof(uint32_t) + keylen + sizeof(uint32_t) + valuelen; + + ret = sss_packet_grow(packet, len); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot grow packet\n"); + return ret; + } + + sss_packet_get_body(packet, &body, &blen); + + SAFEALIGN_SET_UINT32(&body[*rp], len, rp); + SAFEALIGN_SET_UINT32(&body[*rp], keylen, rp); + + if (keylen == 1) { + body[*rp] = '\0'; + } else { + memcpy(&body[*rp], key, keylen); + } + *rp += keylen; + + SAFEALIGN_SET_UINT32(&body[*rp], valuelen, rp); + if (valuelen == 1) { + body[*rp] = '\0'; + } else { + memcpy(&body[*rp], value, valuelen); + } + *rp += valuelen; + + return EOK; +} + +void +autofs_orphan_maps(struct autofs_ctx *autofs_ctx) +{ + /* It will automatically decrease the refcount of enum_ctx through + * delete callback. */ + sss_ptr_hash_delete_all(autofs_ctx->maps, false); +} + +static void +autofs_enumctx_lifetime_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *pvt) +{ + struct autofs_enum_ctx *enum_ctx; + + enum_ctx = talloc_get_type(pvt, struct autofs_enum_ctx); + + /* Remove it from the table. It will automatically decrease the refcount. */ + sss_ptr_hash_delete(enum_ctx->table, enum_ctx->key, false); +} + +static void +autofs_set_enumctx_lifetime(struct autofs_ctx *autofs_ctx, + struct autofs_enum_ctx *enum_ctx, + uint32_t lifetime) +{ + struct timeval tv; + struct tevent_timer *te; + + tv = tevent_timeval_current_ofs(lifetime, 0); + te = tevent_add_timer(autofs_ctx->rctx->ev, enum_ctx, tv, + autofs_enumctx_lifetime_timeout, enum_ctx); + if (te == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Could not set up life timer for autofs maps. " + "Entries may become stale.\n"); + } +} + +static struct autofs_enum_ctx * +autofs_create_enumeration_context(TALLOC_CTX *mem_ctx, + struct autofs_ctx *autofs_ctx, + const char *mapname) +{ + struct autofs_enum_ctx *enum_ctx; + errno_t ret; + + enum_ctx = talloc_zero(mem_ctx, struct autofs_enum_ctx); + if (enum_ctx == NULL) { + return NULL; + } + + enum_ctx->ready = false; + enum_ctx->table = autofs_ctx->maps; + + enum_ctx->key = talloc_strdup(enum_ctx, mapname); + if (enum_ctx->key == NULL) { + talloc_free(enum_ctx); + return NULL; + } + + ret = sss_ptr_hash_add(autofs_ctx->maps, mapname, + enum_ctx, struct autofs_enum_ctx); + if (ret != EOK) { + talloc_free(enum_ctx); + return NULL; + } + + return enum_ctx; +} + +static void +autofs_orphan_master_map(struct autofs_ctx *autofs_ctx, + const char *mapname) +{ + struct sss_domain_info *dom; + errno_t ret; + + if (strcmp(mapname, "auto.master") != 0) { + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Invalidating master map\n"); + + /* Remove and invalidate all maps. */ + autofs_orphan_maps(autofs_ctx); + + DEBUG(SSSDBG_TRACE_FUNC, "Invalidating autofs maps\n"); + for (dom = autofs_ctx->rctx->domains; + dom != NULL; + dom = get_next_domain(dom, SSS_GND_DESCEND)) { + ret = sysdb_invalidate_autofs_maps(dom); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to invalidate maps in " + "%s [%d]: %s\n", dom->name, ret, sss_strerror(ret)); + } + } +} + +struct autofs_setent_state { + struct autofs_ctx *autofs_ctx; + struct autofs_enum_ctx *enum_ctx; +}; + +static void autofs_setent_done(struct tevent_req *subreq); + +static struct tevent_req * +autofs_setent_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct autofs_ctx *autofs_ctx, + const char *mapname) +{ + struct autofs_setent_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct autofs_setent_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n"); + return NULL; + } + + state->autofs_ctx = autofs_ctx; + + /* Lookup current results if available. */ + state->enum_ctx = sss_ptr_hash_lookup(autofs_ctx->maps, mapname, + struct autofs_enum_ctx); + if (state->enum_ctx != NULL) { + if (state->enum_ctx->ready) { + ret = EOK; + goto done; + } + + /* Map is still being created. We will watch the request. */ + ret = setent_add_ref(state, &state->enum_ctx->notify_list, req); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to watch enumeration request " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + ret = EAGAIN; + goto done; + } + + /* Map does not yet exist. Create the enumeration object and fetch data. */ + state->enum_ctx = autofs_create_enumeration_context(state, autofs_ctx, mapname); + if (state->enum_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create enumeration context!\n"); + ret = ENOMEM; + goto done; + } + + subreq = cache_req_autofs_map_entries_send(mem_ctx, ev, autofs_ctx->rctx, + autofs_ctx->rctx->ncache, + 0, NULL, mapname); + if (subreq == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(subreq, autofs_setent_done, req); + + ret = EAGAIN; + +done: + if (ret == EOK) { + tevent_req_done(req); + tevent_req_post(req, ev); + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + tevent_req_post(req, ev); + } + + return req; +} + +static void autofs_setent_done(struct tevent_req *subreq) +{ + struct autofs_setent_state *state; + struct cache_req_result *result; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct autofs_setent_state); + + ret = cache_req_autofs_map_entries_recv(state, subreq, &result); + talloc_zfree(subreq); + + switch (ret) { + case EOK: + state->enum_ctx->found = true; + state->enum_ctx->result = talloc_steal(state->enum_ctx, result); + autofs_set_enumctx_lifetime(state->autofs_ctx, state->enum_ctx, + state->enum_ctx->result->domain->autofsmap_timeout); + break; + case ENOENT: + state->enum_ctx->found = false; + state->enum_ctx->result = NULL; + autofs_set_enumctx_lifetime(state->autofs_ctx, state->enum_ctx, + state->autofs_ctx->neg_timeout); + break; + default: + DEBUG(SSSDBG_OP_FAILURE, "Unable to get map data [%d]: %s\n", + ret, sss_strerror(ret)); + + setent_notify(&state->enum_ctx->notify_list, ret); + talloc_zfree(state->enum_ctx); + tevent_req_error(req, ret); + return; + } + + state->enum_ctx->ready = true; + + /* Make the enumeration context disappear with maps table. */ + talloc_steal(state->autofs_ctx->maps, state->enum_ctx); + + setent_notify_done(&state->enum_ctx->notify_list); + tevent_req_done(req); + return; +} + +static errno_t +autofs_setent_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct autofs_enum_ctx **_enum_ctx) +{ + struct autofs_setent_state *state; + state = tevent_req_data(req, struct autofs_setent_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *_enum_ctx = talloc_reference(mem_ctx, state->enum_ctx); + + return EOK; +} + +static errno_t +autofs_read_setautomntent_input(struct cli_ctx *cli_ctx, + const char **_mapname) +{ + struct cli_protocol *pctx; + uint8_t *body; + size_t blen; + + pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); + + sss_packet_get_body(pctx->creq->in, &body, &blen); + + /* if not terminated fail */ + if (body[blen - 1] != '\0') { + return EINVAL; + } + + /* If the body isn't valid UTF-8, fail */ + if (!sss_utf8_check(body, blen - 1)) { + return EINVAL; + } + + *_mapname = (const char *)body; + + return EOK; +} + +static errno_t +autofs_write_setautomntent_output(struct cli_ctx *cli_ctx, + struct cache_req_result *result) +{ + struct cli_protocol *pctx; + uint8_t *body; + size_t blen; + errno_t ret; + + pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); + + ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in), + &pctx->creq->out); + if (ret != EOK) { + return ret; + } + + if (result == NULL || result->count == 0) { + DEBUG(SSSDBG_TRACE_FUNC, "Map was not found\n"); + return sss_cmd_empty_packet(pctx->creq->out); + } + + DEBUG(SSSDBG_TRACE_FUNC, "Map found\n"); + + ret = sss_packet_grow(pctx->creq->out, 2 * sizeof(uint32_t)); + if (ret != EOK) { + return ret; + } + + sss_packet_get_body(pctx->creq->out, &body, &blen); + + /* Got some results */ + SAFEALIGN_SETMEM_UINT32(body, 1, NULL); + + /* Reserved padding */ + SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); + + return EOK; +} + +static void +sss_autofs_cmd_setautomntent_done(struct tevent_req *req); + +static int +sss_autofs_cmd_setautomntent(struct cli_ctx *cli_ctx) +{ + struct autofs_cmd_ctx *cmd_ctx; + struct autofs_ctx *autofs_ctx; + struct tevent_req *req; + errno_t ret; + + autofs_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct autofs_ctx); + + cmd_ctx = talloc_zero(cli_ctx, struct autofs_cmd_ctx); + if (cmd_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create command context.\n"); + return ENOMEM; + } + + cmd_ctx->cli_ctx = cli_ctx; + cmd_ctx->autofs_ctx = autofs_ctx; + + ret = autofs_read_setautomntent_input(cli_ctx, &cmd_ctx->mapname); + if (ret != EOK) { + goto done; + } + + autofs_orphan_master_map(autofs_ctx, cmd_ctx->mapname); + + DEBUG(SSSDBG_TRACE_FUNC, "Obtaining autofs map %s\n", + cmd_ctx->mapname); + + req = cache_req_autofs_map_by_name_send(cli_ctx, cli_ctx->ev, + autofs_ctx->rctx, + autofs_ctx->rctx->ncache, 0, NULL, + cmd_ctx->mapname); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_autofs_map_by_name_send failed\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(req, sss_autofs_cmd_setautomntent_done, cmd_ctx); + + ret = EOK; + +done: + return autofs_cmd_done(cmd_ctx, ret); +} + +static void +sss_autofs_cmd_setautomntent_done(struct tevent_req *req) +{ + struct cache_req_result *result; + struct autofs_cmd_ctx *cmd_ctx; + errno_t ret; + + cmd_ctx = tevent_req_callback_data(req, struct autofs_cmd_ctx); + + ret = cache_req_autofs_map_by_name_recv(cmd_ctx, req, &result); + talloc_zfree(req); + if (ret != EOK) { + autofs_cmd_done(cmd_ctx, ret); + return; + } + + ret = autofs_write_setautomntent_output(cmd_ctx->cli_ctx, result); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create reply packet " + "[%d]: %s\n", ret, sss_strerror(ret)); + autofs_cmd_done(cmd_ctx, ret); + return; + } + + sss_cmd_done(cmd_ctx->cli_ctx, NULL); +} + +static int +sss_autofs_cmd_endautomntent(struct cli_ctx *client) +{ + struct cli_protocol *pctx; + errno_t ret; + + DEBUG(SSSDBG_TRACE_FUNC, "endautomntent called\n"); + + pctx = talloc_get_type(client->protocol_ctx, struct cli_protocol); + + /* create response packet */ + ret = sss_packet_new(pctx->creq, 0, + sss_packet_get_cmd(pctx->creq->in), + &pctx->creq->out); + + if (ret != EOK) { + return ret; + } + + sss_cmd_done(client, NULL); + return EOK; +} + +static errno_t +autofs_read_getautomntent_input(struct cli_ctx *cli_ctx, + const char **_mapname, + uint32_t *_cursor, + uint32_t *_max_entries) +{ + struct cli_protocol *pctx; + const char *mapname; + uint32_t namelen; + uint8_t *body; + size_t blen; + size_t c = 0; + + pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); + + sss_packet_get_body(pctx->creq->in, &body, &blen); + + SAFEALIGN_COPY_UINT32_CHECK(&namelen, body+c, blen, &c); + if (namelen == 0 || namelen > blen - c) { + return EINVAL; + } + + mapname = (const char *)body + c; + + /* if not null-terminated fail */ + if (mapname[namelen] != '\0') { + return EINVAL; + } + + /* If the name isn't valid UTF-8, fail */ + if (!sss_utf8_check((const uint8_t *)mapname, namelen - 1)) { + return EINVAL; + } + + SAFEALIGN_COPY_UINT32_CHECK(_cursor, body + c + namelen + 1, blen, &c); + SAFEALIGN_COPY_UINT32_CHECK(_max_entries, body + c + namelen + 1, blen, &c); + *_mapname = mapname; + + return EOK; +} + +static errno_t +autofs_write_getautomntent_output(struct cli_ctx *cli_ctx, + struct autofs_enum_ctx *enum_ctx, + uint32_t cursor, + uint32_t max_entries) +{ + struct cli_protocol *pctx; + struct ldb_message **entries; + struct ldb_message *entry; + size_t count; + size_t num_entries; + uint8_t *body; + size_t blen; + size_t rp; + uint32_t i; + uint32_t stop; + uint32_t left; + errno_t ret; + + pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); + + count = enum_ctx->found ? enum_ctx->result->count - 1 : 0; + entries = count > 0 ? enum_ctx->result->msgs + 1 : NULL; + + ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in), + &pctx->creq->out); + if (ret != EOK) { + return ret; + } + + if (!enum_ctx->found || count == 0 || cursor >= count) { + DEBUG(SSSDBG_TRACE_FUNC, "No entries was not found\n"); + return sss_cmd_empty_packet(pctx->creq->out); + } + + /* allocate memory for number of entries in the packet */ + ret = sss_packet_grow(pctx->creq->out, sizeof(uint32_t)); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Cannot grow packet\n"); + return ret; + } + + rp = sizeof(uint32_t); /* We will first write the elements. */ + left = count - cursor; + stop = max_entries < left ? max_entries : left; + + num_entries = 0; + for (i = 0; i < stop; i++) { + entry = entries[cursor]; + cursor++; + + ret = autofs_fill_entry(entry, pctx->creq->out, &rp); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot fill entry %d/%d, skipping\n", i, stop); + continue; + } + num_entries++; + } + + /* packet grows in fill_autofs_entry, body pointer may change, + * thus we have to obtain it here */ + sss_packet_get_body(pctx->creq->out, &body, &blen); + + rp = 0; + SAFEALIGN_SET_UINT32(&body[rp], num_entries, &rp); + + return EOK; +} + +static void +sss_autofs_cmd_getautomntent_done(struct tevent_req *req); + +static int +sss_autofs_cmd_getautomntent(struct cli_ctx *cli_ctx) +{ + struct autofs_cmd_ctx *cmd_ctx; + struct autofs_ctx *autofs_ctx; + struct tevent_req *req; + errno_t ret; + + autofs_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct autofs_ctx); + + cmd_ctx = talloc_zero(cli_ctx, struct autofs_cmd_ctx); + if (cmd_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create command context.\n"); + return ENOMEM; + } + + cmd_ctx->cli_ctx = cli_ctx; + cmd_ctx->autofs_ctx = autofs_ctx; + + ret = autofs_read_getautomntent_input(cli_ctx, &cmd_ctx->mapname, + &cmd_ctx->cursor, + &cmd_ctx->max_entries); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Obtaining enumeration context for %s\n", + cmd_ctx->mapname); + + req = autofs_setent_send(cli_ctx, cli_ctx->ev, autofs_ctx, cmd_ctx->mapname); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "autofs_setent_send failed\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(req, sss_autofs_cmd_getautomntent_done, cmd_ctx); + + ret = EOK; + +done: + return autofs_cmd_done(cmd_ctx, ret); +} + +static void +sss_autofs_cmd_getautomntent_done(struct tevent_req *req) +{ + struct autofs_enum_ctx *enum_ctx; + struct autofs_cmd_ctx *cmd_ctx; + errno_t ret; + + cmd_ctx = tevent_req_callback_data(req, struct autofs_cmd_ctx); + + ret = autofs_setent_recv(cmd_ctx, req, &enum_ctx); + talloc_zfree(req); + if (ret != EOK) { + autofs_cmd_done(cmd_ctx, ret); + return; + } + + ret = autofs_write_getautomntent_output(cmd_ctx->cli_ctx, enum_ctx, + cmd_ctx->cursor, + cmd_ctx->max_entries); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create reply packet " + "[%d]: %s\n", ret, sss_strerror(ret)); + autofs_cmd_done(cmd_ctx, ret); + return; + } + + sss_cmd_done(cmd_ctx->cli_ctx, NULL); +} + +static errno_t +autofs_read_getautomntbyname_input(struct cli_ctx *cli_ctx, + const char **_mapname, + const char **_keyname) +{ + struct cli_protocol *pctx; + const char *mapname; + const char *keyname; + uint32_t namelen; + uint32_t keylen; + uint8_t *body; + size_t blen; + size_t c = 0; + + pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); + + sss_packet_get_body(pctx->creq->in, &body, &blen); + + /* Get map name. */ + SAFEALIGN_COPY_UINT32_CHECK(&namelen, body + c, blen, &c); + if (namelen == 0 || namelen > blen - c) { + return EINVAL; + } + + mapname = (const char *) body + c; + + /* if not null-terminated fail */ + if (mapname[namelen] != '\0') { + return EINVAL; + } + + /* If the name isn't valid UTF-8, fail */ + if (!sss_utf8_check((const uint8_t *)mapname, namelen - 1)) { + return EINVAL; + } + + c += namelen + 1; + + /* Get key name. */ + SAFEALIGN_COPY_UINT32_CHECK(&keylen, body + c, blen, &c); + if (keylen == 0 || keylen > blen - c) { + return EINVAL; + } + + keyname = (const char *) body + c; + + /* if not null-terminated fail */ + if (keyname[keylen] != '\0') { + return EINVAL; + } + + /* If the key isn't valid UTF-8, fail */ + if (!sss_utf8_check((const uint8_t *)keyname, keylen - 1)) { + return EINVAL; + } + + *_mapname = mapname; + *_keyname = keyname; + + return EOK; +} + +static errno_t +autofs_write_getautomntbyname_output(struct cli_ctx *cli_ctx, + struct cache_req_result *result, + const char *keyname) +{ + struct cli_protocol *pctx; + struct ldb_message *entry; + const char *value; + size_t value_len; + size_t len; + uint8_t *body; + size_t blen; + size_t rp; + errno_t ret; + + pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol); + + if (result == NULL || result->count == 0) { + DEBUG(SSSDBG_TRACE_FUNC, "Key [%s] was not found\n", keyname); + return sss_cmd_empty_packet(pctx->creq->out); + } + + DEBUG(SSSDBG_TRACE_INTERNAL, "Found key [%s]\n", keyname); + entry = result->msgs[0]; + + ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in), + &pctx->creq->out); + if (ret != EOK) { + return ret; + } + + value = ldb_msg_find_attr_as_string(entry, SYSDB_AUTOFS_ENTRY_VALUE, NULL); + if (value == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "No entry value found in [%s]\n", keyname); + return EINVAL; + } + + value_len = 1 + strlen(value); + len = sizeof(uint32_t) + sizeof(uint32_t) + value_len; + + ret = sss_packet_grow(pctx->creq->out, len); + if (ret != EOK) { + return ret; + } + + sss_packet_get_body(pctx->creq->out, &body, &blen); + + rp = 0; + SAFEALIGN_SET_UINT32(&body[rp], len, &rp); + + SAFEALIGN_SET_UINT32(&body[rp], value_len, &rp); + if (value_len == 1) { + body[rp] = '\0'; + } else { + memcpy(&body[rp], value, value_len); + } + + return EOK; +} + +static void +sss_autofs_cmd_getautomntbyname_done(struct tevent_req *req); + +static int +sss_autofs_cmd_getautomntbyname(struct cli_ctx *cli_ctx) +{ + struct autofs_cmd_ctx *cmd_ctx; + struct autofs_ctx *autofs_ctx; + struct tevent_req *req; + errno_t ret; + + autofs_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct autofs_ctx); + + cmd_ctx = talloc_zero(cli_ctx, struct autofs_cmd_ctx); + if (cmd_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create command context.\n"); + return ENOMEM; + } + + cmd_ctx->cli_ctx = cli_ctx; + cmd_ctx->autofs_ctx = autofs_ctx; + + ret = autofs_read_getautomntbyname_input(cli_ctx, &cmd_ctx->mapname, + &cmd_ctx->keyname); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, "Obtaining autofs entry %s:%s\n", + cmd_ctx->mapname, cmd_ctx->keyname); + + req = cache_req_autofs_entry_by_name_send(cli_ctx, cli_ctx->ev, + autofs_ctx->rctx, + autofs_ctx->rctx->ncache, 0, NULL, + cmd_ctx->mapname, + cmd_ctx->keyname); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_autofs_entry_by_name_send failed\n"); + ret = ENOMEM; + goto done; + } + + tevent_req_set_callback(req, sss_autofs_cmd_getautomntbyname_done, cmd_ctx); + + ret = EOK; + +done: + return autofs_cmd_done(cmd_ctx, ret); +} + +static void +sss_autofs_cmd_getautomntbyname_done(struct tevent_req *req) +{ + struct cache_req_result *result; + struct autofs_cmd_ctx *cmd_ctx; + errno_t ret; + + cmd_ctx = tevent_req_callback_data(req, struct autofs_cmd_ctx); + + ret = cache_req_autofs_entry_by_name_recv(cmd_ctx, req, &result); + talloc_zfree(req); + if (ret != EOK) { + autofs_cmd_done(cmd_ctx, ret); + return; + } + + ret = autofs_write_getautomntbyname_output(cmd_ctx->cli_ctx, result, + cmd_ctx->keyname); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create reply packet " + "[%d]: %s\n", ret, sss_strerror(ret)); + autofs_cmd_done(cmd_ctx, ret); + return; + } + + sss_cmd_done(cmd_ctx->cli_ctx, NULL); +} + +struct cli_protocol_version *register_cli_protocol_version(void) +{ + static struct cli_protocol_version autofs_cli_protocol_version[] = { + { SSS_AUTOFS_PROTO_VERSION, NULL, NULL } + }; + + return autofs_cli_protocol_version; +} + +struct sss_cmd_table *get_autofs_cmds(void) +{ + static struct sss_cmd_table autofs_cmds[] = { + { SSS_GET_VERSION, sss_cmd_get_version }, + { SSS_AUTOFS_SETAUTOMNTENT, sss_autofs_cmd_setautomntent }, + { SSS_AUTOFS_GETAUTOMNTENT, sss_autofs_cmd_getautomntent }, + { SSS_AUTOFS_GETAUTOMNTBYNAME, sss_autofs_cmd_getautomntbyname }, + { SSS_AUTOFS_ENDAUTOMNTENT, sss_autofs_cmd_endautomntent }, + { SSS_CLI_NULL, NULL} + }; + + return autofs_cmds; +} + +int autofs_connection_setup(struct cli_ctx *cctx) +{ + int ret; + + ret = sss_connection_setup(cctx); + if (ret != EOK) return ret; + + return EOK; +} |