/* Authors: Pavel Březina <pbrezina@redhat.com> Copyright (C) 2011 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 <stdio.h> #include <stdlib.h> #include <limits.h> #include <talloc.h> #include <popt.h> #include <sys/types.h> #include <dirent.h> #include <ctype.h> #include <signal.h> #include <utime.h> #include "config.h" #include "ldb.h" #include "util/util.h" #include "tools/tools_util.h" #include "confdb/confdb.h" #define CHECK(expr, done, msg) do { \ if (expr) { \ ERROR(msg "\n"); \ goto done; \ } \ } while(0) struct debuglevel_tool_ctx { struct confdb_ctx *confdb; char **sections; }; static errno_t set_debug_level(struct debuglevel_tool_ctx *tool_ctx, int debug_to_set, const char *config_file); static errno_t connect_to_confdb(TALLOC_CTX *ctx, struct confdb_ctx **cdb_ctx); static errno_t get_confdb_sections(TALLOC_CTX *ctx, struct confdb_ctx *confdb, char ***output_sections); static int parse_debug_level(const char *strlevel); int main(int argc, const char **argv) { int ret; int pc_debug = SSSDBG_DEFAULT; int debug_to_set = SSSDBG_INVALID; const char *debug_as_string = NULL; const char *config_file = NULL; const char *pc_config_file = NULL; struct debuglevel_tool_ctx *ctx = NULL; struct poptOption long_options[] = { POPT_AUTOHELP {"debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &pc_debug, 0, _("The debug level to run with"), NULL }, {"config", 'c', POPT_ARG_STRING, &pc_config_file, 0, _("Specify a non-default config file"), NULL}, POPT_TABLEEND }; poptContext pc = NULL; debug_prg_name = argv[0]; /* parse parameters */ pc = poptGetContext(argv[0], argc, argv, long_options, 0); poptSetOtherOptionHelp(pc, "DEBUG_LEVEL_TO_SET"); while((ret = poptGetNextOpt(pc)) != -1) { switch(ret) { default: fprintf(stderr, "\nInvalid option %s: %s\n\n", poptBadOption(pc, 0), poptStrerror(ret)); poptPrintUsage(pc, stderr, 0); ret = EXIT_FAILURE; goto fini; } } debug_level = debug_convert_old_level(pc_debug); /* get debug level */ debug_as_string = poptGetArg(pc); if (debug_as_string == NULL) { BAD_POPT_PARAMS(pc, _("Specify debug level you want to set\n"), ret, fini); } /* get config file */ if (pc_config_file) { config_file = talloc_strdup(ctx, pc_config_file); } else { config_file = talloc_strdup(ctx, CONFDB_DEFAULT_CONFIG_FILE); } if (config_file == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_strdup() failed\n")); ret = ENOMEM; goto fini; } CHECK_ROOT(ret, debug_prg_name); /* free pc_config_file? */ /* free debug_as_string? */ debug_to_set = parse_debug_level(debug_as_string); CHECK(debug_to_set == SSSDBG_INVALID, fini, "Invalid debug level."); /* 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 = 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."); ret = set_debug_level(ctx, debug_to_set, config_file); CHECK(ret != EOK, fini, "Could not set debug level."); ret = signal_sssd(SIGHUP); CHECK(ret != EOK, fini, "Could not force sssd processes to reload configuration. " "Is sssd running?"); fini: poptFreeContext(pc); talloc_free(ctx); return ret; } errno_t set_debug_level(struct debuglevel_tool_ctx *tool_ctx, int debug_to_set, const char *config_file) { int ret; int err; const char *values[2]; char **section = NULL; TALLOC_CTX *tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_new() failed\n")); return ENOMEM; } /* convert debug_to_set to string */ values[0] = talloc_asprintf(tmp_ctx, "0x%.4x", debug_to_set); if (values[0] == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, ("Could not allocate memory for " "debug_to_set to string conversion\n")); ret = ENOMEM; goto done; } values[1] = NULL; /* write to confdb */ for (section = tool_ctx->sections; *section != NULL; section++) { ret = confdb_add_param(tool_ctx->confdb, 1, *section, CONFDB_SERVICE_DEBUG_LEVEL, values); if (ret != EOK) { goto done; } } /* * Change atime and mtime of sssd.conf, * so the configuration can be restored on next start. */ errno = 0; if (utime(config_file, NULL) == -1 ) { err = errno; DEBUG(SSSDBG_MINOR_FAILURE, ("Unable to change mtime of \"%s\": %s\n", config_file, strerror(err))); } ret = EOK; done: talloc_free(tmp_ctx); return ret; } errno_t 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; } 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 }; static const int known_services_count = 3; 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 != NULL; domain = domain->next) 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 != NULL; domain = domain->next, 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; } 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); }