summaryrefslogtreecommitdiffstats
path: root/src/responder/autofs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
commit74aa0bc6779af38018a03fd2cf4419fe85917904 (patch)
tree9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/responder/autofs
parentInitial commit. (diff)
downloadsssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz
sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/responder/autofs')
-rw-r--r--src/responder/autofs/autofs_private.h74
-rw-r--r--src/responder/autofs/autofssrv.c242
-rw-r--r--src/responder/autofs/autofssrv_cmd.c961
3 files changed, 1277 insertions, 0 deletions
diff --git a/src/responder/autofs/autofs_private.h b/src/responder/autofs/autofs_private.h
new file mode 100644
index 0000000..c15e028
--- /dev/null
+++ b/src/responder/autofs/autofs_private.h
@@ -0,0 +1,74 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ 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/>.
+*/
+
+#ifndef _AUTOFSSRV_PRIVATE_H_
+#define _AUTOFSSRV_PRIVATE_H_
+
+#include <dhash.h>
+
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+
+#define SSS_AUTOFS_PROTO_VERSION 0x001
+
+struct autofs_ctx {
+ struct resp_ctx *rctx;
+
+ int neg_timeout;
+
+ hash_table_t *maps;
+};
+
+struct autofs_cmd_ctx {
+ struct autofs_ctx *autofs_ctx;
+ struct cli_ctx *cli_ctx;
+
+ const char *mapname;
+ const char *keyname;
+ uint32_t max_entries;
+ uint32_t cursor;
+};
+
+struct autofs_enum_ctx {
+ /* Results. First result is the map objects, next results are map entries. */
+ struct cache_req_result *result;
+
+ /* True if the map was found. */
+ bool found;
+
+ /* False if the result is being created. */
+ bool ready;
+
+ /* Enumeration context key. */
+ const char *key;
+
+ /* Hash table that contains this enumeration context. */
+ hash_table_t *table;
+
+ /* Requests that awaits the data. */
+ struct setent_req_list *notify_list;
+};
+
+struct sss_cmd_table *get_autofs_cmds(void);
+int autofs_connection_setup(struct cli_ctx *cctx);
+
+void autofs_orphan_maps(struct autofs_ctx *actx);
+
+#endif /* _AUTOFSSRV_PRIVATE_H_ */
diff --git a/src/responder/autofs/autofssrv.c b/src/responder/autofs/autofssrv.c
new file mode 100644
index 0000000..1dbbe9f
--- /dev/null
+++ b/src/responder/autofs/autofssrv.c
@@ -0,0 +1,242 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ Autofs responder: the responder server
+
+ 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 <popt.h>
+
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder.h"
+#include "providers/data_provider.h"
+#include "responder/autofs/autofs_private.h"
+#include "sss_iface/sss_iface_async.h"
+#include "util/sss_ptr_hash.h"
+
+static errno_t
+autofs_get_config(struct autofs_ctx *actx,
+ struct confdb_ctx *cdb)
+{
+ errno_t ret;
+
+ ret = confdb_get_int(cdb, CONFDB_AUTOFS_CONF_ENTRY,
+ CONFDB_AUTOFS_MAP_NEG_TIMEOUT, 15,
+ &actx->neg_timeout);
+ return ret;
+}
+
+static errno_t
+autofs_clean_hash_table(TALLOC_CTX *mem_ctx,
+ struct sbus_request *sbus_req,
+ struct autofs_ctx *actx)
+{
+ autofs_orphan_maps(actx);
+
+ return EOK;
+}
+
+static void
+autofs_maps_delete_cb(hash_entry_t *item,
+ hash_destroy_enum deltype,
+ void *pvt)
+{
+ struct autofs_ctx *autofs_ctx;
+ struct autofs_enum_ctx *enum_ctx;
+
+ autofs_ctx = talloc_get_type(pvt, struct autofs_ctx);
+ enum_ctx = talloc_get_type(item->value.ptr, struct autofs_enum_ctx);
+
+ talloc_unlink(autofs_ctx->maps, enum_ctx);
+}
+
+static errno_t
+autofs_register_service_iface(struct autofs_ctx *autofs_ctx,
+ struct resp_ctx *rctx)
+{
+ errno_t ret;
+
+ SBUS_INTERFACE(iface_svc,
+ sssd_service,
+ SBUS_METHODS(
+ SBUS_SYNC(METHOD, sssd_service, rotateLogs, responder_logrotate, rctx),
+ SBUS_SYNC(METHOD, sssd_service, clearEnumCache, autofs_clean_hash_table, autofs_ctx)
+ ),
+ SBUS_SIGNALS(SBUS_NO_SIGNALS),
+ SBUS_PROPERTIES(
+ SBUS_SYNC(GETTER, sssd_service, debug_level, generic_get_debug_level, NULL),
+ SBUS_SYNC(SETTER, sssd_service, debug_level, generic_set_debug_level, NULL)
+ )
+ );
+
+ ret = sbus_connection_add_path(rctx->mon_conn, SSS_BUS_PATH, &iface_svc);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register service interface"
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+static int
+autofs_process_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb)
+{
+ struct resp_ctx *rctx;
+ struct sss_cmd_table *autofs_cmds;
+ struct autofs_ctx *autofs_ctx;
+ int ret;
+
+ autofs_cmds = get_autofs_cmds();
+ ret = sss_process_init(mem_ctx, ev, cdb,
+ autofs_cmds,
+ SSS_AUTOFS_SOCKET_NAME, -1, NULL, -1,
+ CONFDB_AUTOFS_CONF_ENTRY,
+ SSS_BUS_AUTOFS, SSS_AUTOFS_SBUS_SERVICE_NAME,
+ autofs_connection_setup,
+ &rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sss_process_init() failed\n");
+ return ret;
+ }
+
+ autofs_ctx = talloc_zero(rctx, struct autofs_ctx);
+ if (!autofs_ctx) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing autofs_ctx\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ret = autofs_get_config(autofs_ctx, cdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Cannot read autofs configuration\n");
+ goto fail;
+ }
+
+ autofs_ctx->rctx = rctx;
+ autofs_ctx->rctx->pvt_ctx = autofs_ctx;
+
+ /* Create the lookup table for setautomntent results */
+ autofs_ctx->maps = sss_ptr_hash_create(autofs_ctx,
+ autofs_maps_delete_cb,
+ autofs_ctx);
+ if (autofs_ctx->maps == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to initialize automount maps hash table\n");
+ ret = EIO;
+ goto fail;
+ }
+
+ ret = schedule_get_domains_task(rctx, rctx->ev, rctx, NULL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "schedule_get_domains_tasks failed.\n");
+ goto fail;
+ }
+
+ /* The responder is initialized. Now tell it to the monitor. */
+ ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_AUTOFS,
+ SSS_AUTOFS_SBUS_SERVICE_NAME,
+ SSS_AUTOFS_SBUS_SERVICE_VERSION,
+ MT_SVC_SERVICE,
+ &rctx->last_request_time, &rctx->mon_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up message bus\n");
+ goto fail;
+ }
+
+ ret = autofs_register_service_iface(autofs_ctx, rctx);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "autofs Initialization complete\n");
+ return EOK;
+
+fail:
+ talloc_free(rctx);
+ return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ char *opt_logger = NULL;
+ struct main_context *main_ctx;
+ int ret;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_MAIN_OPTS
+ SSSD_LOGGER_OPTS
+ SSSD_SERVER_OPTS(uid, gid)
+ SSSD_RESPONDER_OPTS
+ POPT_TABLEEND
+ };
+
+ /* Set debug level to invalid value so we can decide if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ umask(DFL_RSP_UMASK);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ /* set up things like debug, signals, daemonization, etc. */
+ debug_log_file = "sssd_autofs";
+ DEBUG_INIT(debug_level, opt_logger);
+
+ ret = server_setup("autofs", true, 0, uid, gid,
+ CONFDB_AUTOFS_CONF_ENTRY, &main_ctx, true);
+ if (ret != EOK) {
+ return 2;
+ }
+
+ ret = die_if_parent_died();
+ if (ret != EOK) {
+ /* This is not fatal, don't return */
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set up to exit "
+ "when parent process does\n");
+ }
+
+ ret = autofs_process_init(main_ctx,
+ main_ctx->event_ctx,
+ main_ctx->confdb_ctx);
+ if (ret != EOK) {
+ return 3;
+ }
+
+ /* loop on main */
+ server_loop(main_ctx);
+
+ return 0;
+}
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;
+}