summaryrefslogtreecommitdiffstats
path: root/src/tools/common/sss_tools.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/tools/common/sss_tools.c617
1 files changed, 617 insertions, 0 deletions
diff --git a/src/tools/common/sss_tools.c b/src/tools/common/sss_tools.c
new file mode 100644
index 0000000..e67de3a
--- /dev/null
+++ b/src/tools/common/sss_tools.c
@@ -0,0 +1,617 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 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/>.
+*/
+
+#include <talloc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <popt.h>
+
+#include "config.h"
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "confdb/confdb_setup.h"
+#include "db/sysdb.h"
+#include "tools/common/sss_tools.h"
+
+static void sss_tool_print_common_opts(int min_len)
+{
+ ERROR("Help options:\n");
+ fprintf(stderr, " %-*s\t %s\n", min_len, "-?, --help",
+ _("Show this for a command"));
+ fprintf(stderr, " %-*s\t %s\n", min_len, "--usage",
+ _("Show brief usage message for a command"));
+ ERROR("\n");
+
+ ERROR("Debug options:\n");
+ fprintf(stderr, " %-*s\t %s\n", min_len, "--debug",
+ _("Enable debug log level of sssctl tool"));
+}
+
+static struct poptOption *sss_tool_common_opts_table(void)
+{
+ static struct poptOption common_opts[] = {
+ {"debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, NULL,
+ 0, NULL, NULL },
+ POPT_TABLEEND
+ };
+
+ common_opts[0].descrip = _("The debug level to run with");
+
+ return common_opts;
+}
+
+static void sss_tool_common_opts(struct sss_tool_ctx *tool_ctx,
+ int *argc, const char **argv)
+{
+ poptContext pc;
+ int debug = SSSDBG_TOOLS_DEFAULT;
+ int orig_argc = *argc;
+ int help = 0;
+
+ struct poptOption options[] = {
+ {"debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_STRIP, &debug,
+ 0, _("The debug level to run with"), NULL },
+ {"help", '?', POPT_ARG_VAL | POPT_ARGFLAG_DOC_HIDDEN, &help,
+ 1, NULL, NULL },
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(argv[0], orig_argc, argv, options, 0);
+ while (poptGetNextOpt(pc) != -1) {
+ /* do nothing */
+ }
+
+ /* Strip common options from arguments. We will discard_const here,
+ * since it is not worth the trouble to convert it back and forth. */
+ *argc = poptStrippedArgv(pc, orig_argc, discard_const_p(char *, argv));
+ tool_ctx->print_help = help;
+
+ DEBUG_CLI_INIT(debug);
+
+ poptFreeContext(pc);
+}
+
+static errno_t sss_tool_confdb_init(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx **_confdb)
+{
+ struct confdb_ctx *confdb;
+ char *path;
+ errno_t ret;
+
+ path = talloc_asprintf(mem_ctx, "%s/%s", DB_PATH, CONFDB_FILE);
+ if (path == NULL) {
+ return ENOMEM;
+ }
+
+ ret = confdb_setup(mem_ctx, path,
+ SSSD_CONFIG_FILE, CONFDB_DEFAULT_CONFIG_DIR,
+ NULL,
+ &confdb);
+ talloc_zfree(path);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to setup ConfDB [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (_confdb != NULL) {
+ *_confdb = confdb;
+ }
+
+ return EOK;
+}
+
+static errno_t sss_tool_domains_init(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *confdb,
+ struct sss_domain_info **_domains)
+{
+ struct sss_domain_info *domains;
+ struct sss_domain_info *dom;
+ errno_t ret;
+
+ ret = confdb_expand_app_domains(confdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to expand application domains [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ ret = confdb_get_domains(confdb, &domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup domains [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ ret = sysdb_init(mem_ctx, domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not initialize connection to the sysdb\n");
+ return ret;
+ }
+
+ for (dom = domains; dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_DESCEND)) {
+ if (!IS_SUBDOMAIN(dom)) {
+ /* Get flat name and domain ID (SID) from the cache
+ * if available */
+ ret = sysdb_master_domain_update(dom);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Failed to update domain %s.\n",
+ dom->name);
+ }
+
+ /* Update list of subdomains for this domain */
+ ret = sysdb_update_subdomains(dom, confdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to update subdomains for domain %s.\n",
+ dom->name);
+ }
+ }
+ }
+
+ for (dom = domains; dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_DESCEND)) {
+ ret = sss_names_init(mem_ctx, confdb, dom->name, &dom->names);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_names_init() failed\n");
+ return ret;
+ }
+ }
+
+ *_domains = domains;
+
+ return ret;
+}
+
+static errno_t sss_tool_init(TALLOC_CTX *mem_ctx,
+ int *argc, const char **argv,
+ struct sss_tool_ctx **_tool_ctx)
+{
+ struct sss_tool_ctx *tool_ctx;
+
+ tool_ctx = talloc_zero(mem_ctx, struct sss_tool_ctx);
+ if (tool_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
+ return ENOMEM;
+ }
+
+ sss_tool_common_opts(tool_ctx, argc, argv);
+ *_tool_ctx = tool_ctx;
+
+ return EOK;
+}
+
+static bool sss_tool_is_delimiter(struct sss_route_cmd *command)
+{
+ if (command->command != NULL && command->command[0] == '\0') {
+ return true;
+ }
+
+ return false;
+}
+
+static bool sss_tools_handles_init_error(struct sss_route_cmd *command,
+ errno_t init_err)
+{
+ if (init_err == EOK) {
+ return true;
+ }
+
+ return command->handles_init_err == init_err;
+}
+
+static size_t sss_tool_max_length(struct sss_route_cmd *commands)
+{
+ size_t max = 0;
+ size_t len;
+ int i;
+
+ for (i = 0; commands[i].command != NULL; i++) {
+ if (sss_tool_is_delimiter(&commands[i])) {
+ continue;
+ }
+
+ len = strlen(commands[i].command);
+ if (max < len) {
+ max = len;
+ }
+ }
+
+ return max;
+}
+
+static void sss_tool_usage(const char *tool_name, struct sss_route_cmd *commands)
+{
+ int min_len;
+ int i;
+
+ ERROR("Usage:\n%s COMMAND COMMAND-ARGS\n\n", tool_name);
+ ERROR("Available commands:\n");
+
+ min_len = sss_tool_max_length(commands);
+
+ for (i = 0; commands[i].command != NULL; i++) {
+ if (sss_tool_is_delimiter(&commands[i])) {
+ fprintf(stderr, "\n%s\n", commands[i].description);
+ continue;
+ }
+
+ if (commands[i].description == NULL) {
+ fprintf(stderr, "* %40s\n", commands[i].command);
+ } else {
+ fprintf(stderr, "* %-*s\t %s\n",
+ min_len, commands[i].command, commands[i].description);
+ }
+ }
+
+ ERROR("\n");
+ sss_tool_print_common_opts(min_len);
+}
+
+static int tool_cmd_init(struct sss_tool_ctx *tool_ctx,
+ struct sss_route_cmd *command)
+{
+ int ret;
+ uid_t uid;
+
+ if (!(command->flags & SSS_TOOL_FLAG_SKIP_ROOT_CHECK)) {
+ uid = getuid();
+ if (uid != 0) {
+ ERROR("'%s' must be run as root\n", command->command);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (command->flags & SSS_TOOL_FLAG_SKIP_CMD_INIT) {
+ return EOK;
+ }
+
+ /* Connect to confdb. */
+ ret = sss_tool_confdb_init(tool_ctx, &tool_ctx->confdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to open confdb [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ /* Setup domains. */
+ ret = sss_tool_domains_init(tool_ctx, tool_ctx->confdb, &tool_ctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup domains [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = confdb_get_string(tool_ctx->confdb, tool_ctx,
+ CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_DEFAULT_DOMAIN,
+ NULL, &tool_ctx->default_domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot get the default domain [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+static errno_t sss_tool_route(int argc, const char **argv,
+ struct sss_tool_ctx *tool_ctx,
+ struct sss_route_cmd *commands,
+ void *pvt)
+{
+ struct sss_cmdline cmdline;
+ const char *cmd;
+ int i;
+ int ret;
+
+ if (commands == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: commands can't be NULL!\n");
+ return EINVAL;
+ }
+
+ if (argc < 2) {
+ sss_tool_usage(argv[0], commands);
+ return EINVAL;
+ }
+
+ cmd = argv[1];
+ for (i = 0; commands[i].command != NULL; i++) {
+ if (sss_tool_is_delimiter(&commands[i])) {
+ continue;
+ }
+
+ if (strcmp(commands[i].command, cmd) == 0) {
+ cmdline.exec = argv[0];
+ cmdline.command = argv[1];
+ cmdline.argc = argc - 2;
+ cmdline.argv = argv + 2;
+
+ if (!tool_ctx->print_help) {
+ ret = tool_cmd_init(tool_ctx, &commands[i]);
+
+ if (!sss_tools_handles_init_error(&commands[i], ret)) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Command %s does not handle initialization error [%d] %s\n",
+ cmdline.command, ret, sss_strerror(ret));
+ return ret;
+ }
+ }
+
+ return commands[i].fn(&cmdline, tool_ctx, pvt);
+ }
+ }
+
+ sss_tool_usage(argv[0], commands);
+ return EINVAL;
+}
+
+static struct poptOption *nonnull_popt_table(struct poptOption *options)
+{
+ static struct poptOption empty[] = {
+ POPT_TABLEEND
+ };
+
+ if (options == NULL) {
+ return empty;
+ }
+
+ return options;
+}
+
+errno_t sss_tool_popt_ex(struct sss_cmdline *cmdline,
+ struct poptOption *options,
+ enum sss_tool_opt require_option,
+ sss_popt_fn popt_fn,
+ void *popt_fn_pvt,
+ const char *fopt_name,
+ const char *fopt_help,
+ enum sss_tool_opt fopt_require,
+ const char **_fopt,
+ bool *_opt_set)
+{
+ struct poptOption opts_table[] = {
+ {NULL, '\0', POPT_ARG_INCLUDE_TABLE, nonnull_popt_table(options), \
+ 0, _("Command options:"), NULL },
+ {NULL, '\0', POPT_ARG_INCLUDE_TABLE, sss_tool_common_opts_table(), \
+ 0, NULL, NULL },
+ POPT_AUTOHELP
+ POPT_TABLEEND
+ };
+ const char *fopt;
+ char *help;
+ poptContext pc;
+ bool opt_set;
+ int ret;
+
+ /* Set output parameter _fopt to NULL value if present. */
+ if (_fopt != NULL) {
+ *_fopt = NULL;
+ }
+
+ /* Create help option string. We always need to append command name since
+ * we use POPT_CONTEXT_KEEP_FIRST. */
+ if (fopt_name == NULL) {
+ help = talloc_asprintf(NULL, "%s %s %s", cmdline->exec,
+ cmdline->command, _("[OPTIONS...]"));
+ } else {
+ help = talloc_asprintf(NULL, "%s %s %s %s", cmdline->exec,
+ cmdline->command, fopt_name, _("[OPTIONS...]"));
+ }
+ if (help == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ return ENOMEM;
+ }
+
+ /* Create popt context. This function is supposed to be called on
+ * command argv which does not contain executable (argv[0]), therefore
+ * we need to use KEEP_FIRST that ensures argv[0] is also processed. */
+ pc = poptGetContext(cmdline->exec, cmdline->argc, cmdline->argv,
+ opts_table, POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, help);
+
+ /* Parse options. Invoke custom function if provided. If no parsing
+ * function is provided, print error on unknown option. */
+ while ((ret = poptGetNextOpt(pc)) != -1) {
+ if (popt_fn != NULL) {
+ ret = popt_fn(pc, ret, popt_fn_pvt);
+ if (ret != EOK) {
+ goto done;
+ }
+ } else {
+ ERROR("Invalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(ret));
+ poptPrintHelp(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ /* Parse free option which is required if requested and fopt_require
+ * is SSS_TOOL_OPT_REQUIRED */
+ opt_set = true;
+ fopt = poptGetArg(pc);
+ if (_fopt != NULL) {
+ if (fopt == NULL) {
+ if (fopt_require == SSS_TOOL_OPT_REQUIRED) {
+ ERROR("Missing option: %s\n\n", fopt_help);
+ poptPrintHelp(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+ opt_set = false;
+ }
+
+ /* No more arguments expected. If something follows it is an error. */
+ if (poptGetArg(pc)) {
+ ERROR("Only one free argument is expected!\n\n");
+ poptPrintHelp(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (fopt != NULL) {
+ *_fopt = strdup(fopt);
+ if (*_fopt == NULL) {
+ ERROR("Out of memory!");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ } else if (_fopt == NULL && fopt != NULL) {
+ /* Unexpected free argument. */
+ ERROR("Unexpected parameter: %s\n\n", fopt);
+ poptPrintHelp(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if ((_fopt != NULL && fopt_require == SSS_TOOL_OPT_REQUIRED && cmdline->argc < 2)
+ || cmdline->argc < 1) {
+ opt_set = false;
+
+ /* If at least one option is required and not provided, print error. */
+ if (require_option == SSS_TOOL_OPT_REQUIRED) {
+ ERROR("At least one option is required!\n\n");
+ poptPrintHelp(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ if (_opt_set != NULL) {
+ *_opt_set = opt_set;
+ }
+
+ ret = EOK;
+
+done:
+ poptFreeContext(pc);
+ talloc_free(help);
+ if (ret != EOK && _fopt != NULL) {
+ free(discard_const(*_fopt));
+ *_fopt = NULL;
+ }
+
+ return ret;
+}
+
+errno_t sss_tool_popt(struct sss_cmdline *cmdline,
+ struct poptOption *options,
+ enum sss_tool_opt require_option,
+ sss_popt_fn popt_fn,
+ void *popt_fn_pvt)
+{
+ return sss_tool_popt_ex(cmdline, options, require_option,
+ popt_fn, popt_fn_pvt, NULL, NULL,
+ SSS_TOOL_OPT_REQUIRED, NULL, NULL);
+}
+
+int sss_tool_main(int argc, const char **argv,
+ struct sss_route_cmd *commands,
+ void *pvt)
+{
+ struct sss_tool_ctx *tool_ctx;
+ errno_t ret;
+
+ ret = sss_tool_init(NULL, &argc, argv, &tool_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tool context\n");
+ return EXIT_FAILURE;
+ }
+
+ ret = sss_tool_route(argc, argv, tool_ctx, commands, pvt);
+ SYSDB_VERSION_ERROR(ret);
+ talloc_free(tool_ctx);
+ if (ret != EOK) {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+errno_t sss_tool_parse_name(TALLOC_CTX *mem_ctx,
+ struct sss_tool_ctx *tool_ctx,
+ const char *input,
+ const char **_username,
+ struct sss_domain_info **_domain)
+{
+ char *username = NULL;
+ char *domname = NULL;
+ struct sss_domain_info *domain;
+ int ret;
+
+ ret = sss_parse_name_for_domains(mem_ctx, tool_ctx->domains,
+ tool_ctx->default_domain, input,
+ &domname, &username);
+ if (ret == EAGAIN) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to find domain. The domain name may "
+ "be a subdomain that was not yet found.\n");
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ domain = find_domain_by_name(tool_ctx->domains, domname, true);
+
+ *_username = username;
+ *_domain = domain;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_zfree(username);
+ talloc_zfree(domname);
+ }
+
+ return ret;
+}
+
+errno_t sss_tool_connect_to_confdb(TALLOC_CTX *ctx, struct confdb_ctx **cdb_ctx)
+{
+ int ret;
+ char *confdb_path = NULL;
+
+ confdb_path = talloc_asprintf(ctx, "%s/%s", DB_PATH, CONFDB_FILE);
+ if (confdb_path == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for confdb path\n");
+ return ENOMEM;
+ }
+
+ ret = confdb_init(ctx, cdb_ctx, confdb_path);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not initialize connection to the confdb\n");
+ }
+
+ talloc_free(confdb_path);
+ return ret;
+}