diff options
Diffstat (limited to 'src/tools/common')
-rw-r--r-- | src/tools/common/sss_colondb.c | 317 | ||||
-rw-r--r-- | src/tools/common/sss_colondb.h | 96 | ||||
-rw-r--r-- | src/tools/common/sss_process.c | 124 | ||||
-rw-r--r-- | src/tools/common/sss_process.h | 29 | ||||
-rw-r--r-- | src/tools/common/sss_tools.c | 617 | ||||
-rw-r--r-- | src/tools/common/sss_tools.h | 105 |
6 files changed, 1288 insertions, 0 deletions
diff --git a/src/tools/common/sss_colondb.c b/src/tools/common/sss_colondb.c new file mode 100644 index 0000000..41e6c3a --- /dev/null +++ b/src/tools/common/sss_colondb.c @@ -0,0 +1,317 @@ +/* + 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 <stdlib.h> + +#include "util/util.h" +#include "util/strtonum.h" +#include "tools/common/sss_colondb.h" + +#define IS_STD_FILE(db) ((db)->file == stdin || (db)->file == stdout) + +static char *read_field_as_string(char *line, + const char **_value) +{ + char *rest; + char *value; + + if (line == NULL || *line == '\n' || *line == '\0') { + /* There is nothing else to read. */ + rest = NULL; + value = NULL; + goto done; + } + + if (*line == ':') { + /* Special case for empty value. */ + *line = '\0'; + rest = line + 1; + value = NULL; + goto done; + } + + /* Value starts at current position. */ + value = line; + + /* Find next field delimiter. */ + rest = strchr(line, ':'); + if (rest == NULL) { + /* There is no more field. Remove \n from the end. */ + rest = strchr(line, '\n'); + if (rest != NULL) { + *rest = '\0'; + rest = NULL; + } + goto done; + } + + /* Remove it and step one character further. */ + *rest = '\0'; + rest++; + +done: + *_value = value; + + return rest; +} + +static char *read_field_as_uint32(char *line, + uint32_t *_value) +{ + const char *str; + char *rest; + errno_t ret; + char *endptr; + + rest = read_field_as_string(line, &str); + if (str == NULL) { + *_value = 0; + return rest; + } + + *_value = strtouint32(str, &endptr, 10); + if ((errno != 0) || *endptr || (str == endptr)) { + ret = errno ? errno : EINVAL; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse number [%d]: %s\n", + ret, sss_strerror(ret)); + + *_value = 0; + } + + return rest; +} + +struct sss_colondb { + FILE *file; + enum sss_colondb_mode mode; +}; + +errno_t sss_colondb_readline(TALLOC_CTX *mem_ctx, + struct sss_colondb *db, + struct sss_colondb_read_field *table) +{ + int readchars; + size_t linelen = 0; + char *line = NULL; + char *tcline; + char *rest; + errno_t ret; + int i; + + if (db->mode != SSS_COLONDB_READ) { + return ERR_INTERNAL; + } + + readchars = getline(&line, &linelen, db->file); + if (readchars == -1) { + /* Nothing was read. */ + + free(line); + line = NULL; + + if (errno != 0) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read line [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + return EOF; + } + + /* Copy line to mem_ctx. */ + tcline = talloc_strdup(mem_ctx, line); + + free(line); + line = NULL; + + if (tcline == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n"); + return ENOMEM; + } + + rest = tcline; + for (i = 0; table[i].type != SSS_COLONDB_SENTINEL; i++) { + switch (table[i].type) { + case SSS_COLONDB_UINT32: + rest = read_field_as_uint32(rest, table[i].data.uint32); + break; + case SSS_COLONDB_STRING: + rest = read_field_as_string(rest, table[i].data.str); + break; + case SSS_COLONDB_SENTINEL: + DEBUG(SSSDBG_CRIT_FAILURE, "Trying to process sentinel?!\n"); + ret = ERR_INTERNAL; + goto done; + } + + if (rest == NULL && table[i + 1].type != SSS_COLONDB_SENTINEL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Line contains less values than expected!\n"); + ret = EINVAL; + goto done; + } else if (rest != NULL && table[i + 1].type == SSS_COLONDB_SENTINEL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Line contains more values than expected!\n"); + ret = EINVAL; + goto done; + } + } + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(tcline); + } + + return ret; +} + +errno_t sss_colondb_writeline(struct sss_colondb *db, + struct sss_colondb_write_field *table) +{ + TALLOC_CTX *tmp_ctx; + char *line = NULL; + errno_t ret; + int i; + + if (db->mode != SSS_COLONDB_WRITE) { + return ERR_INTERNAL; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n"); + return ENOMEM; + } + + line = talloc_strdup(tmp_ctx, ""); + if (line == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n"); + ret = ENOMEM; + goto done; + } + + for (i = 0; table[i].type != SSS_COLONDB_SENTINEL; i++) { + switch (table[i].type) { + case SSS_COLONDB_UINT32: + if (table[i].data.uint32 == 0) { + line = talloc_asprintf_append(line, ":"); + } else { + line = talloc_asprintf_append(line, ":%u", table[i].data.uint32); + } + break; + case SSS_COLONDB_STRING: + if (table[i].data.str == NULL) { + line = talloc_asprintf_append(line, ":"); + } else { + line = talloc_asprintf_append(line, ":%s", table[i].data.str); + } + break; + case SSS_COLONDB_SENTINEL: + DEBUG(SSSDBG_CRIT_FAILURE, "Trying to process sentinel?!\n"); + ret = ERR_INTERNAL; + goto done; + } + + if (line == NULL) { + ret = ENOMEM; + goto done; + } + } + + /* Remove starting : */ + line++; + + fprintf(db->file, "%s\n", line); + fflush(db->file); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +static int sss_colondb_close(void *pvt) +{ + struct sss_colondb *db = talloc_get_type(pvt, struct sss_colondb); + + if (db->file == NULL || IS_STD_FILE(db)) { + return 0; + } + + fclose(db->file); + db->file = NULL; + + return 0; +} + +static FILE *open_db(const char *filename, enum sss_colondb_mode mode) +{ + FILE *fp = NULL; + errno_t ret; + + errno = 0; + + switch (mode) { + case SSS_COLONDB_READ: + fp = filename == NULL ? stdin : fopen(filename, "r"); + break; + case SSS_COLONDB_WRITE: + fp = filename == NULL ? stdout : fopen(filename, "w"); + break; + } + + if (fp == NULL && filename != NULL) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to open file %s [%d]: %s\n", + filename, ret, sss_strerror(ret)); + } + + return fp; +} + +struct sss_colondb *sss_colondb_open(TALLOC_CTX *mem_ctx, + enum sss_colondb_mode mode, + const char *filename) +{ + struct sss_colondb *db; + + db = talloc_zero(mem_ctx, struct sss_colondb); + if (db == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n"); + return NULL; + } + + db->file = open_db(filename, mode); + db->mode = mode; + + if (db->file == NULL) { + talloc_free(db); + return NULL; + } + + talloc_set_destructor((TALLOC_CTX *)db, sss_colondb_close); + + return db; +} diff --git a/src/tools/common/sss_colondb.h b/src/tools/common/sss_colondb.h new file mode 100644 index 0000000..cb90400 --- /dev/null +++ b/src/tools/common/sss_colondb.h @@ -0,0 +1,96 @@ +/* + 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/>. +*/ + +#ifndef _SSS_COLONDB_H_ +#define _SSS_COLONDB_H_ + +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> +#include <talloc.h> + +struct sss_colondb; + +enum sss_colondb_mode { + SSS_COLONDB_READ, + SSS_COLONDB_WRITE +}; + +enum sss_colondb_type { + SSS_COLONDB_UINT32, + SSS_COLONDB_STRING, + SSS_COLONDB_SENTINEL +}; + +union sss_colondb_write_data { + uint32_t uint32; + const char *str; +}; + +union sss_colondb_read_data { + uint32_t *uint32; + const char **str; +}; + +struct sss_colondb_write_field { + enum sss_colondb_type type; + union sss_colondb_write_data data; +}; + +struct sss_colondb_read_field { + enum sss_colondb_type type; + union sss_colondb_read_data data; +}; + +/** + * Open colon DB and return connection. + * @param[in|out] mem_ctx Memory context. Internal sss_colondb_close() is set + * on destructor of this memory context. + * @param[in] mode Open mode of db: SSS_COLONDB_READ or SSS_COLONDB_WRITE. + * @param[in] filename Name of file. + * @return Pointer to structure holding DB connection, or NULL if fail. + */ +struct sss_colondb *sss_colondb_open(TALLOC_CTX *mem_ctx, + enum sss_colondb_mode mode, + const char *filename); + +/** + * Read line from colon DB. + * @param[in|out] mem_ctx Memory context. + * @param[in] db Pointer to structure holding DB connection. + * @param[in|out] table Array of expected structure of line. It is expected + * that last item has SSS_COLONDB_SENTINEL type. + * @return EOK if success, else error code. + */ +errno_t sss_colondb_readline(TALLOC_CTX *mem_ctx, + struct sss_colondb *db, + struct sss_colondb_read_field *table); + +/** + * Write line to colon DB. + * @param[in] db Pointer to structure holding DB connection. + * @param[in] table Array with data. It is expected that last item has + * SSS_COLONDB_SENTINEL type. + * @return EOK if success, else error code. + */ +errno_t sss_colondb_writeline(struct sss_colondb *db, + struct sss_colondb_write_field *table); + +#endif /* _SSS_COLONDB_H_ */ diff --git a/src/tools/common/sss_process.c b/src/tools/common/sss_process.c new file mode 100644 index 0000000..fc710a5 --- /dev/null +++ b/src/tools/common/sss_process.c @@ -0,0 +1,124 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 <stdlib.h> +#include <stdio.h> +#include <signal.h> + +#include "util/util.h" +#include "tools/common/sss_process.h" + +static pid_t parse_pid(const char *strpid) +{ + long value; + char *endptr; + + errno = 0; + value = strtol(strpid, &endptr, 10); + if ((errno != 0) || (endptr == strpid) + || ((*endptr != '\0') && (*endptr != '\n'))) { + return 0; + } + + return value; +} + +static errno_t sss_pid(pid_t *out_pid) +{ + int ret; + size_t fsize; + FILE *pid_file; + char pid_str[MAX_PID_LENGTH] = {'\0'}; + + *out_pid = 0; + + errno = 0; + pid_file = fopen(SSSD_PIDFILE, "r"); + if (pid_file == NULL) { + ret = errno; + DEBUG(SSSDBG_MINOR_FAILURE, "Unable to open pid file \"%s\": %s\n", + SSSD_PIDFILE, strerror(ret)); + goto done; + } + + fsize = fread(pid_str, sizeof(char), MAX_PID_LENGTH * sizeof(char), + pid_file); + if (!feof(pid_file)) { + /* eof not reached */ + ret = ferror(pid_file); + if (ret != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read from file \"%s\": %s\n", + SSSD_PIDFILE, strerror(ret)); + } else { + DEBUG(SSSDBG_CRIT_FAILURE, "File \"%s\" contains invalid pid.\n", + SSSD_PIDFILE); + } + goto done; + } + if (fsize == 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "File \"%s\" contains no pid.\n", + SSSD_PIDFILE); + ret = EINVAL; + goto done; + } + + pid_str[MAX_PID_LENGTH-1] = '\0'; + *out_pid = parse_pid(pid_str); + if (*out_pid == 0) { + DEBUG(SSSDBG_CRIT_FAILURE, + "File \"%s\" contains invalid pid.\n", SSSD_PIDFILE); + ret = EINVAL; + goto done; + } + + ret = EOK; + +done: + if (pid_file != NULL) { + fclose(pid_file); + } + return ret; +} + +bool sss_daemon_running(void) +{ + return sss_signal(0) == EOK; +} + +errno_t sss_signal(int signum) +{ + int ret; + pid_t pid; + + ret = sss_pid(&pid); + if (ret != EOK) { + return ret; + } + + if (kill(pid, signum) != 0) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "Could not send signal %d to process %d: %s\n", + signum, pid, strerror(errno)); + return ret; + } + + return EOK; +} diff --git a/src/tools/common/sss_process.h b/src/tools/common/sss_process.h new file mode 100644 index 0000000..6bbb094 --- /dev/null +++ b/src/tools/common/sss_process.h @@ -0,0 +1,29 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2016 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 _SSS_PROCESS_H_ +#define _SSS_PROCESS_H_ + +#include "util/util.h" + +bool sss_daemon_running(void); +errno_t sss_signal(int signum); + +#endif /* _SSS_PROCESS_H_ */ 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; +} diff --git a/src/tools/common/sss_tools.h b/src/tools/common/sss_tools.h new file mode 100644 index 0000000..af49675 --- /dev/null +++ b/src/tools/common/sss_tools.h @@ -0,0 +1,105 @@ +/* + 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/>. +*/ + +#ifndef _SSS_TOOLS_H_ +#define _SSS_TOOLS_H_ + +#include <talloc.h> +#include <popt.h> + +#include "confdb/confdb.h" + +struct sss_tool_ctx { + struct confdb_ctx *confdb; + + bool print_help; + char *default_domain; + struct sss_domain_info *domains; +}; + +struct sss_cmdline { + const char *exec; /* argv[0] */ + const char *command; /* command name */ + int argc; /* rest of arguments */ + const char **argv; +}; + +typedef errno_t +(*sss_route_fn)(struct sss_cmdline *cmdline, + struct sss_tool_ctx *tool_ctx, + void *pvt); + +#define SSS_TOOL_COMMAND_FLAGS(cmd, msg, err, fn, flags) \ + {cmd, _(msg), err, fn, flags} +#define SSS_TOOL_COMMAND(cmd, msg, err, fn) \ + {cmd, _(msg), err, fn, 0} +#define SSS_TOOL_COMMAND_NOMSG(cmd, err, fn) {cmd, NULL, err, fn, 0} +#define SSS_TOOL_DELIMITER(message) {"", _(message), 0, NULL, 0} +#define SSS_TOOL_LAST {NULL, NULL, 0, NULL, 0} + +#define SSS_TOOL_FLAG_SKIP_CMD_INIT 0x01 +#define SSS_TOOL_FLAG_SKIP_ROOT_CHECK 0x02 + +struct sss_route_cmd { + const char *command; + const char *description; + errno_t handles_init_err; + sss_route_fn fn; + int flags; +}; + +typedef errno_t (*sss_popt_fn)(poptContext pc, char option, void *pvt); + +enum sss_tool_opt { + SSS_TOOL_OPT_REQUIRED, + SSS_TOOL_OPT_OPTIONAL +}; + +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); + +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); + +int sss_tool_main(int argc, const char **argv, + struct sss_route_cmd *commands, + void *pvt); + +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); + + +errno_t sss_tool_connect_to_confdb(TALLOC_CTX *ctx, struct confdb_ctx **cdb_ctx); + +#endif /* SRC_TOOLS_COMMON_SSS_TOOLS_H_ */ |