summaryrefslogtreecommitdiffstats
path: root/src/tools/sssctl/sssctl_logs.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/tools/sssctl/sssctl_logs.c608
1 files changed, 608 insertions, 0 deletions
diff --git a/src/tools/sssctl/sssctl_logs.c b/src/tools/sssctl/sssctl_logs.c
new file mode 100644
index 0000000..f8f5a65
--- /dev/null
+++ b/src/tools/sssctl/sssctl_logs.c
@@ -0,0 +1,608 @@
+/*
+ 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 "config.h"
+
+#include <stdlib.h>
+#include <limits.h>
+#include <talloc.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <signal.h>
+#include <utime.h>
+#include <ldb.h>
+#include <popt.h>
+#include <stdio.h>
+#include <glob.h>
+
+#include "util/util.h"
+#include "tools/common/sss_tools.h"
+#include "tools/common/sss_process.h"
+#include "tools/sssctl/sssctl.h"
+#include "tools/tools_util.h"
+#include "confdb/confdb.h"
+#include "sss_iface/sss_iface_sync.h"
+#include "responder/ifp/ifp_iface/ifp_iface_sync.h"
+
+#define LOG_FILE(file) " " LOG_PATH "/" file
+#define LOG_FILES LOG_FILE("*.log")
+#define SSS_ANALYZE SSSD_LIBEXEC_PATH"/sss_analyze"
+
+#define CHECK(expr, done, msg) do { \
+ if (expr) { \
+ ERROR(msg "\n"); \
+ goto done; \
+ } \
+} while(0)
+
+#define POPT_SERV_OPTION(NAME, VAR, DESC) \
+ {services[SERV_ ## NAME].name, '\0', POPT_BIT_SET, &VAR, \
+ services[SERV_ ## NAME].mask, DESC, NULL}
+
+#define STARTS_WITH(s, p) (strncmp((s), (p), strlen(p)) == 0)
+#define REMOVE_PREFIX(s, p) (STARTS_WITH(s, p) ? (s) + strlen(p) : (s))
+#define IS_DOMAIN(c) STARTS_WITH((c), "domain/")
+#define DOMAIN_NAME(c) REMOVE_PREFIX((c), "domain/")
+#define EMPTY_TARGETS(t) ((t)[0] == NULL)
+
+enum debug_level_action {
+ ACTION_SET,
+ ACTION_GET
+};
+
+struct debuglevel_tool_ctx {
+ struct confdb_ctx *confdb;
+ char **sections;
+};
+
+struct sssctl_logs_opts {
+ int delete;
+ int archived;
+};
+
+struct sssctl_service_desc {
+ const char *name;
+ int mask;
+};
+
+enum serv_idx {
+ SERV_SSSD,
+ SERV_NSS,
+ SERV_PAM,
+ SERV_SUDO,
+ SERV_AUTOFS,
+ SERV_SSH,
+ SERV_PAC,
+ SERV_IFP,
+ SERV_COUNT
+};
+
+struct sssctl_service_desc services[] = {
+ { "sssd", 1U << SERV_SSSD },
+ { "nss", 1U << SERV_NSS },
+ { "pam", 1U << SERV_PAM },
+ { "sudo", 1U << SERV_SUDO },
+ { "autofs", 1U << SERV_AUTOFS},
+ { "ssh", 1U << SERV_SSH },
+ { "pac", 1U << SERV_PAC },
+ { "ifp", 1U << SERV_IFP }
+};
+
+static struct sbus_sync_connection *connect_to_sbus(TALLOC_CTX *mem_ctx)
+{
+ struct sbus_sync_connection *conn;
+
+ conn = sbus_sync_connect_private(mem_ctx, SSS_MONITOR_ADDRESS, NULL);
+ if (conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to connect to the sbus monitor\n");
+ }
+
+ return conn;
+}
+
+static const char *get_busname(TALLOC_CTX *mem_ctx, struct confdb_ctx *confdb,
+ const char *component)
+{
+ errno_t ret;
+ const char *busname;
+ struct sss_domain_info *domain;
+
+ if (strcmp(component, "sssd") == 0) {
+ busname = talloc_strdup(mem_ctx, SSS_BUS_MONITOR);
+ } else if (IS_DOMAIN(component)) {
+ ret = confdb_get_domain(confdb, DOMAIN_NAME(component), &domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown domain: %s\n", component);
+ busname = NULL;
+ goto done;
+ }
+
+ busname = sss_iface_domain_bus(mem_ctx, domain);
+ } else {
+ busname = talloc_asprintf(mem_ctx, "sssd.%s", component);
+ }
+
+done:
+ return busname;
+}
+
+/* in_out_value is an input argument when action is ACTION_SET; it is an output
+ * argument when action is ACTION_GET. */
+static errno_t do_debug_level(enum debug_level_action action,
+ struct sbus_sync_connection *conn,
+ struct confdb_ctx *confdb,
+ const char *component,
+ uint32_t *in_out_value)
+{
+ errno_t ret;
+ uint32_t value;
+ const char *busname;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ busname = get_busname(tmp_ctx, confdb, component);
+ if (busname == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create the bus name for %s\n",
+ component);
+ }
+
+ if (action == ACTION_GET) {
+ ret = sbus_get_service_debug_level(conn, busname, SSS_BUS_PATH, &value);
+ if (ret != EOK) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ *in_out_value = value;
+ } else {
+ ret = sbus_set_service_debug_level(conn, busname, SSS_BUS_PATH,
+ *in_out_value);
+ if (ret != EOK) {
+ ret = ENOENT;
+ goto done;
+ }
+ }
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sssctl_do_debug_level(enum debug_level_action action,
+ struct debuglevel_tool_ctx *tool_ctx,
+ const char **targets,
+ uint32_t debug_to_set)
+{
+ bool all_targets = EMPTY_TARGETS(targets);
+ errno_t ret = EOK;
+ errno_t final_ret = EOK;
+ uint32_t current_level = SSSDBG_INVALID;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ const char *stripped_target;
+ const char **curr_target;
+ struct sbus_sync_connection *conn;
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ conn = connect_to_sbus(tmp_ctx);
+ if (conn == NULL) {
+ ERROR("SSSD is not running.\n");
+ ret = EIO;
+ goto fini;
+ }
+
+ curr_target = (all_targets ?
+ discard_const_p(const char *, tool_ctx->sections) : targets);
+ while (*curr_target != NULL) {
+ stripped_target = REMOVE_PREFIX(*curr_target, "config/");
+
+ if (action == ACTION_GET) {
+ ret = do_debug_level(ACTION_GET, conn, tool_ctx->confdb,
+ stripped_target, &current_level);
+ CHECK(ret != EOK && ret != ENOENT, fini,
+ "Could not read the debug level.");
+
+ if (ret == EOK) {
+ PRINT(_("%1$-25s %2$#.4x\n"), stripped_target, current_level);
+ } else {
+ if (!all_targets) {
+ if (IS_DOMAIN(stripped_target)) {
+ PRINT(_("%1$-25s Unknown domain\n"), stripped_target);
+ } else {
+ PRINT(_("%1$-25s Unreachable service\n"), stripped_target);
+ }
+ final_ret = ENOENT;
+ }
+ }
+ } else {
+ ret = do_debug_level(ACTION_SET, conn, tool_ctx->confdb,
+ stripped_target, &debug_to_set);
+ CHECK(ret != EOK && ret != ENOENT, fini,
+ "Could not set the debug level.");
+ if (ret == ENOENT && !all_targets) {
+ final_ret = ret;
+ }
+ }
+ curr_target++;
+ }
+
+ if (ret == EOK) {
+ ret = final_ret;
+ }
+
+fini:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t get_confdb_sections(TALLOC_CTX *ctx, struct confdb_ctx *confdb,
+ char ***output_sections)
+{
+ int ret;
+ int domain_count = 0;
+ int i = 0;
+ struct sss_domain_info *domain = NULL;
+ struct sss_domain_info *domain_list = NULL;
+ char **sections;
+ const char *known_services[] = {
+ CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_NSS_CONF_ENTRY,
+ CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAC_CONF_ENTRY,
+ CONFDB_SSH_CONF_ENTRY,
+ CONFDB_SUDO_CONF_ENTRY,
+ CONFDB_AUTOFS_CONF_ENTRY,
+ CONFDB_IFP_CONF_ENTRY,
+ };
+ static const int known_services_count = sizeof(known_services)
+ / sizeof(*known_services);
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ /* get domains */
+ ret = confdb_get_domains(confdb, &domain_list);
+ if (ret != EOK)
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain list\n");
+
+ for (domain = domain_list;
+ domain;
+ domain = get_next_domain(domain, 0)) {
+ domain_count++;
+ }
+
+ /* allocate output space */
+ sections = talloc_array(ctx, char *,
+ domain_count + known_services_count + 1);
+ if (sections == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for sections\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ for (i = 0; i < known_services_count; i++) {
+ sections[i] = talloc_strdup(tmp_ctx, known_services[i]);
+ if (sections[i] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+ }
+
+ for (domain = domain_list;
+ domain;
+ domain = get_next_domain(domain, 0), i++) {
+ sections[i] = talloc_asprintf(tmp_ctx, CONFDB_DOMAIN_PATH_TMPL,
+ domain->name);
+ if (sections[i] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+ }
+
+ /* add NULL to the end */
+ sections[i] = NULL;
+
+ *output_sections = talloc_steal(ctx, sections);
+
+ return EOK;
+fail:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char **get_targets(TALLOC_CTX *mem_ctx, int services_mask,
+ const char **domainv)
+{
+ int i;
+ int count = 1;
+ const char **targets = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return NULL;
+ }
+
+ targets = talloc_zero_array(tmp_ctx, const char *, count);
+ if (targets == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for the list of targets\n");
+ goto done;
+ }
+
+ if (services_mask != 0) {
+ for (i = 0; i < SERV_COUNT; i++) {
+ if (services_mask == 0 || (services_mask & services[i].mask) != 0) {
+ targets = talloc_realloc(tmp_ctx, targets, const char *, count + 1);
+ if (targets == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for the list of targets\n");
+ goto done;
+ }
+ targets[count - 1] = talloc_strdup(tmp_ctx, services[i].name);
+ targets[count++] = NULL;
+ }
+ }
+ }
+
+ if (domainv != NULL) {
+ for (; *domainv != NULL; domainv++) {
+ targets = talloc_realloc(tmp_ctx, targets, const char *, count + 1);
+ if (targets == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for the list of targets\n");
+ goto done;
+ }
+ if (IS_DOMAIN(*domainv)) {
+ targets[count - 1] = talloc_strdup(tmp_ctx, *domainv);
+ } else {
+ targets[count - 1] = talloc_asprintf(tmp_ctx, "domain/%s", *domainv);
+ }
+ targets[count++] = NULL;
+ }
+ }
+
+ targets = talloc_steal(mem_ctx, targets);
+ for (i = 0; i < count; i++) {
+ targets[i] = talloc_steal(mem_ctx, targets[i]);
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return targets;
+}
+
+int parse_debug_level(const char *strlevel)
+{
+ long value;
+ char *endptr;
+
+ errno = 0;
+ value = strtol(strlevel, &endptr, 0);
+ if ((errno != 0) || (endptr == strlevel) || (*endptr != '\0')) {
+ return SSSDBG_INVALID;
+ }
+
+ return debug_convert_old_level(value);
+}
+
+errno_t sssctl_logs_remove(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_logs_opts opts = {0};
+ errno_t ret;
+ glob_t globbuf;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"delete", 'd', POPT_ARG_NONE, &opts.delete, 0, _("Delete log files instead of truncating"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt(cmdline, options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ if (opts.delete) {
+ PRINT("Deleting log files...\n");
+ ret = sss_remove_subtree(LOG_PATH);
+ if (ret != EOK) {
+ ERROR("Unable to remove log files\n");
+ return ret;
+ }
+
+ sss_signal(SIGHUP);
+ } else {
+ globbuf.gl_offs = 4;
+ ret = glob(LOG_PATH"/*.log", GLOB_ERR|GLOB_DOOFFS, NULL, &globbuf);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand log files list\n");
+ return ret;
+ }
+ globbuf.gl_pathv[0] = discard_const_p(char, "truncate");
+ globbuf.gl_pathv[1] = discard_const_p(char, "--no-create");
+ globbuf.gl_pathv[2] = discard_const_p(char, "--size");
+ globbuf.gl_pathv[3] = discard_const_p(char, "0");
+
+ PRINT("Truncating log files...\n");
+ ret = sssctl_run_command((const char * const*)globbuf.gl_pathv);
+ globfree(&globbuf);
+ if (ret != EOK) {
+ ERROR("Unable to truncate log files\n");
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+errno_t sssctl_logs_fetch(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ const char *file = NULL;
+ errno_t ret;
+ glob_t globbuf;
+
+ /* Parse command line. */
+ ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL, NULL, NULL,
+ "FILE", "Output file", SSS_TOOL_OPT_REQUIRED,
+ &file, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ globbuf.gl_offs = 3;
+ ret = glob(LOG_PATH"/*.log", GLOB_ERR|GLOB_DOOFFS, NULL, &globbuf);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand log files list\n");
+ goto done;
+ }
+ globbuf.gl_pathv[0] = discard_const_p(char, "tar");
+ globbuf.gl_pathv[1] = discard_const_p(char, "-czf");
+ globbuf.gl_pathv[2] = discard_const_p(char, file);
+
+ PRINT("Archiving log files into %s...\n", file);
+ ret = sssctl_run_command((const char * const*)globbuf.gl_pathv);
+ globfree(&globbuf);
+ if (ret != EOK) {
+ ERROR("Unable to archive log files\n");
+ goto done;
+ }
+
+done:
+ free(discard_const(file));
+
+ return ret;
+}
+
+errno_t sssctl_debug_level(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ int ret;
+ int pc_services = 0;
+ uint32_t debug_to_set = SSSDBG_INVALID;
+ const char **pc_domains = NULL;
+ const char **targets = NULL;
+ const char *debug_as_string = NULL;
+
+ struct debuglevel_tool_ctx *ctx = NULL;
+ struct poptOption long_options[] = {
+ {"domain", '\0', POPT_ARG_ARGV, &pc_domains,
+ 0, _("Target a specific domain"), _("domain")},
+ POPT_SERV_OPTION(SSSD, pc_services, _("Target the SSSD service")),
+ POPT_SERV_OPTION(NSS, pc_services, _("Target the NSS service")),
+ POPT_SERV_OPTION(PAM, pc_services, _("Target the PAM service")),
+ POPT_SERV_OPTION(SUDO, pc_services, _("Target the SUDO service")),
+ POPT_SERV_OPTION(AUTOFS, pc_services, _("Target the AUTOFS service")),
+ POPT_SERV_OPTION(SSH, pc_services, _("Target the SSH service")),
+ POPT_SERV_OPTION(PAC, pc_services, _("Target the PAC service")),
+ POPT_SERV_OPTION(IFP, pc_services, _("Target the IFP service")),
+ POPT_TABLEEND
+ };
+
+ /* allocate context */
+ ctx = talloc_zero(NULL, struct debuglevel_tool_ctx);
+ if (ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for tools context\n");
+ ret = ENOMEM;
+ goto fini;
+ }
+
+ ret = sss_tool_popt_ex(cmdline, long_options, SSS_TOOL_OPT_OPTIONAL, NULL,
+ NULL, "DEBUG_LEVEL_TO_SET",
+ _("Specify debug level you want to set"),
+ SSS_TOOL_OPT_OPTIONAL, &debug_as_string, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto fini;
+ }
+
+ CHECK_ROOT(ret, debug_prg_name);
+
+ if (debug_as_string != NULL) {
+ debug_to_set = (uint32_t) parse_debug_level(debug_as_string);
+ CHECK(debug_to_set == SSSDBG_INVALID, fini, "Invalid debug level.");
+ }
+
+ /* Create a list with all the target names (services + domains) */
+ targets = get_targets(ctx, pc_services, pc_domains);
+ CHECK(targets == NULL, fini, "Could not allocate memory.");
+
+ ret = sss_tool_connect_to_confdb(ctx, &ctx->confdb);
+ CHECK(ret != EOK, fini, "Could not connect to configuration database.");
+
+ ret = get_confdb_sections(ctx, ctx->confdb, &ctx->sections);
+ CHECK(ret != EOK, fini, "Could not get all configuration sections.");
+
+ if (debug_as_string == NULL) {
+ ret = sssctl_do_debug_level(ACTION_GET, ctx, targets, 0);
+ } else {
+ ret = sssctl_do_debug_level(ACTION_SET, ctx, targets, debug_to_set);
+ }
+
+ /* Only report missing components that the user requested,
+ except for the monitor (sssd not running) */
+ if (ret != ENOENT && ret != EIO && EMPTY_TARGETS(targets)) {
+ ret = EOK;
+ }
+
+fini:
+ talloc_free(ctx);
+ free(discard_const(debug_as_string));
+
+ return ret;
+}
+
+errno_t sssctl_analyze(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+#ifndef BUILD_CHAIN_ID
+ PRINT("ERROR: Tevent chain ID support missing, log analyzer is unsupported.\n");
+ return EOK;
+#endif
+ errno_t ret;
+
+ ret = sssctl_wrap_command(SSS_ANALYZE, NULL, cmdline, tool_ctx, pvt);
+
+ return ret;
+}