summaryrefslogtreecommitdiffstats
path: root/src/tools/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/common')
-rw-r--r--src/tools/common/sss_colondb.c317
-rw-r--r--src/tools/common/sss_colondb.h96
-rw-r--r--src/tools/common/sss_process.c124
-rw-r--r--src/tools/common/sss_process.h29
-rw-r--r--src/tools/common/sss_tools.c617
-rw-r--r--src/tools/common/sss_tools.h105
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_ */