/*- * Copyright (c) 2000-2004 Andrey Simonenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #ifndef lint static const char rcsid[] ATTR_UNUSED = "@(#)$Id: ipastat.c,v 1.2.2.5 2007/05/11 16:29:59 simon Exp $"; #endif /* !lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_PTHREAD # include #endif #include "ipa_mod.h" #include "dlapi.h" #include "confcommon.h" #include "pathnames.h" #include "memfunc.h" #include "ipastat_conf.h" #include "ipastat_log.h" #include "ipastat_main.h" #include "ipastat_rules.h" #include "ipastat_st.h" #include "ipastat_time.h" static const char *user_name = NULL; /* -u */ static const char *group_name = NULL; /* -g */ static const char *envprogname; static void exit_err(const char *, ...) ATTR_NORETURN ATTR_FORMAT(printf, 1, 2); static void exit_errx(const char *, ...) ATTR_NORETURN ATTR_FORMAT(printf, 1, 2); /* * Output the program name, a message, an error message and exit. */ static void exit_err(const char *format, ...) { va_list ap; va_start(ap, format); vlogmsg(IPA_LOG_ERR, format, ap); va_end(ap); exit(1); } /* * Output the program name, a message and exit. */ static void exit_errx(const char *format, ...) { va_list ap; va_start(ap, format); vlogmsgx(IPA_LOG_ERR, format, ap); va_end(ap); exit(1); } /* * Output version number (-v and -h switches). */ static void show_version(void) { printf(IPASTAT_NAME ", version " PACKAGE_VERSION"\nRuntime settings: " #ifdef SYM_PREFIX "symbol prefix \"" SYM_PREFIX "\", " #else "symbol prefix is not used, " #endif #ifdef WITH_PTHREAD "thread-safe mode, " #else "single-threaded mode, " #endif #ifdef USE_LIBLTDL "using libtool's ltdl library, " #else "using dlopen interface, " #endif #ifdef WITH_MEMFUNC_DEBUG "memfunc debugging is enabled" #else "memfunc debugging is disabled" #endif "\nSupports: rules" #ifdef WITH_LIMITS ", limits" #endif #ifdef WITH_THRESHOLDS ", thresholds" #endif ".\n"); } /* * Set supplementary groups for process, gid is a GID from getpwnam(), * NGROUPS_MAX is defined in SUSv3. */ static int setsuppgids(const char *user, gid_t gid) { int i, ngids = 1; gid_t gids[NGROUPS_MAX + 1]; char **gr_user; const struct group *grp; gids[0] = gid; errno = 0; while ( (grp = getgrent()) != NULL) { for (i = 0; i < ngids; ++i) if (grp->gr_gid == gids[i]) goto next; for (gr_user = grp->gr_mem; *gr_user != NULL; ++gr_user) { if (strcmp(user, *gr_user) == 0) { if (ngids < NGROUPS_MAX + 1) { gids[ngids++] = grp->gr_gid; break; } } } next: ; } if (errno != 0) { logmsg(IPA_LOG_ERR, "setsuppgids: getgrent"); return -1; } if (setgroups(ngids, gids) < 0) { logmsg(IPA_LOG_ERR, "setsuppgids: setgroups"); return -1; } return 0; } /* * Output help message (-h switch). */ static void usage(void) { show_version(); printf("\ Usage: %s [options]\n\ Statistics viewer\n\ Options are:\n\ -f \tUse given instead of using default\n\ \t\t\tconfiguration file "IPASTAT_CONF_FILE"\n\ -t\t\t\tCheck the configuration file (two switches mimic\n\ \t\t\treal configuration regime) and exit\n\ -c \tSet the directory "IPASTAT_NAME" should chroot(2) into\n\ \t\t\timmediately, working directory is not changed\n\ -u \t\tChange UID of the running copy of "IPASTAT_NAME" to \n\ -g \t\tChange GID of the running copy of "IPASTAT_NAME" to \n\ -q \t\tQuery statistics from modules\n\ -h\t\t\tOutput this help message and exit\n\ -v\t\t\tShow version number and exit\n", envprogname); printf(" Options for the -q switch (details in the manual page):\n\ -a rules\t\tOutput all rules\n" #ifdef WITH_LIMITS " -a limits\t\tOutput all limits (should be use with -r option)\n" #endif #ifdef WITH_THRESHOLDS " -a thresholds\t\tOutput all thresholds (should be use with -r option)\n" #endif " -s \t\tUse given statistics system\n\ -x \t\tFilter output with the given POSIX regular expression\n\ -r \t\tSpecify rule name\n" #ifdef WITH_LIMITS " -l \t\tSpecify limit name\n" #endif #ifdef WITH_THRESHOLDS " -t \tSpecify threshold name\n" #endif " -i|I \tSpecify time interval\n" ); } /* * Implement -u and -g options. */ static void change_user(void) { char *endptr; uid_t uid; gid_t gid; const struct passwd *pwd; const struct group *grp; if (group_name != NULL) { /* -g */ errno = 0; if ( (grp = getgrnam(group_name)) == NULL) { if (errno != 0) exit_err("getgrnam(%s)", group_name); if (isdigit(*group_name) == 0) exit_errx("cannot find group %s", group_name); errno = 0; gid = (gid_t)strtoul(group_name, &endptr, 10); if (errno != 0) exit_err("change_user: strtoul"); if (group_name == endptr || *endptr != '\0') exit_errx("cannot find group %s", group_name); } else gid = grp->gr_gid; if (setgroups(1, &gid) < 0) exit_err("change_user: setgroups(1, [%lu])", (u_long)gid); } if (user_name != NULL) { /* -u */ errno = 0; if ( (pwd = getpwnam(user_name)) == NULL) { if (errno != 0) exit_err("getpwnam(%s)", user_name); if (isdigit(*user_name) == 0) exit_errx("cannot find user %s", user_name); errno = 0; uid = (uid_t)strtoul(user_name, &endptr, 10); if (errno != 0) exit_err("change_user: strtoul"); if (user_name == endptr || *endptr != '\0') exit_errx("cannot find user %s", user_name); if (group_name == NULL) { errno = 0; if ( (pwd = getpwuid(uid)) == NULL) { if (errno != 0) exit_err("getpwuid(%lu)", (u_long)uid); else exit_errx("cannot find user with UID %lu and -g option was not given", (u_long)uid); } } } else uid = pwd->pw_uid; if (pwd != NULL && group_name == NULL) { gid = pwd->pw_gid; if (setsuppgids(pwd->pw_name, gid) < 0) exit_err("change_user: cannot set all groups for user %s", pwd->pw_name); } } if (setgid(gid) < 0) exit_err("change_user: setgid(%lu)", (u_long)gid); if (user_name != NULL) if (setuid(uid) < 0) exit_err("change_user: setuid(%lu)", (u_long)uid); /* To be sure, that passwd and group files are closed. */ endpwent(); endgrent(); } #ifdef WITH_LIMITS # define Q_OPTSTRING_LIMIT "l:" #else # define Q_OPTSTRING_LIMIT "" #endif #ifdef WITH_THRESHOLDS # define Q_OPTSTRING_THRESHOLD "t:" #else # define Q_OPTSTRING_THRESHOLD "" #endif #define Q_OPTSTRING ":a:i:I:r:s:" Q_OPTSTRING_LIMIT Q_OPTSTRING_THRESHOLD "x:" static void parse_query(int argc, char *argv[]) { int opt; size_t len; #ifdef WITH_ANY_LIMITS u_int type; const struct opt_rule *opt_rule; #endif while ( (opt = getopt(argc, argv, Q_OPTSTRING)) != -1) switch (opt) { case 'a': if (a_flag != A_FLAG_ABSENT) exit_errx("query: duplicated the -a option"); len = strlen(optarg); if (strncmp(optarg, "rules", len) == 0) a_flag = A_FLAG_RULES; else #ifdef WITH_LIMITS if (strncmp(optarg, "limits", len) == 0) a_flag = A_FLAG_LIMITS; else #endif #ifdef WITH_THRESHOLDS if (strncmp(optarg, "thresholds", len) == 0) a_flag = A_FLAG_THRESHOLDS; else #endif exit_errx("query: unknown option value \"%s\" for -a", optarg); break; case 'i': if (add_opt_tint(optarg, 0) < 0) exit_errx("query: cannot add time interval"); break; case 'I': if (add_opt_tint(optarg, 1) < 0) exit_errx("query: cannot add time interval"); break; #ifdef WITH_LIMITS case 'l': if (cur_opt_rule == NULL) exit_errx("query: the -l option can be used only after the -r option"); if (add_opt_limit(optarg) < 0) exit_errx("query: cannot add limit"); break; #endif case 'r': if (add_opt_rule(optarg) < 0) exit_errx("query: cannot add rule"); break; case 's': if (add_opt_st(optarg) < 0) exit_errx("query: cannot add statistics systems names"); break; #ifdef WITH_THRESHOLDS case 't': if (cur_opt_rule == NULL) exit_errx("query: the -t option can be used only after the -r option"); if (add_opt_threshold(optarg) < 0) exit_errx("query: cannot add threshold"); break; #endif case 'x': x_pat = optarg; x_reg_ptr = &x_reg; break; case ':': exit_errx("query: option requires an argument -- %c", optopt); /* NOTREACHED */ case '?': exit_errx("query: illegal option -- %c", optopt); /* NOTREACHED */ default: exit_errx("query: unexpected option -- %c", optopt); } if (optind < argc) exit_errx("query: non-switch argument \"%s\"", argv[optind]); switch (a_flag) { case A_FLAG_ABSENT: break; case A_FLAG_RULES: if (!STAILQ_EMPTY(&opt_rules_list) || !STAILQ_EMPTY(&opt_tint_list)) exit_errx("query: \"-a rules\" should be used alone"); break; #ifdef WITH_LIMITS case A_FLAG_LIMITS: if (STAILQ_EMPTY(&opt_rules_list)) exit_errx("query: \"-a limits\" requires -r option(s)"); if (has_opt_limits || has_opt_thresholds) exit_errx("query: \"-a limits\" requires only -r option(s)"); break; #endif #ifdef WITH_THRESHOLDS case A_FLAG_THRESHOLDS: if (STAILQ_EMPTY(&opt_rules_list)) exit_errx("query: \"-a thresholds\" requires -r option(s)"); if (has_opt_limits || has_opt_thresholds) exit_errx("query: \"-a thresholds\" requires only -r option(s)"); break; #endif } #if defined(WITH_ANY_LIMITS) && defined(WITH_THRESHOLDS) if (has_opt_limits && has_opt_thresholds) exit_errx("query: do not mix -l and -t options"); #endif #ifdef WITH_ANY_LIMITS if (!STAILQ_EMPTY(&opt_rules_list)) { type = STAILQ_FIRST(&opt_rules_list)->type; STAILQ_FOREACH(opt_rule, &opt_rules_list, link) if (opt_rule->type != type) exit_errx("query: incorrect combination of -l or -t options with -r options"); # ifdef WITH_THRESHOLDS if (type == OPT_RULE_THRESHOLD && !STAILQ_EMPTY(&opt_tint_list)) exit_errx("query: do not use -i options with -t options"); # endif } #endif /* WITH_ANY_LIMITS */ if (x_pat != NULL) if ( (re_errcode = regcomp(&x_reg, x_pat, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); exit_errx("query: regcomp(%s): %s", x_pat, re_errbuf); } } int main(int argc, char *argv[]) { int opt; /* Current option. */ int ret; /* Return code. */ int q_flag = 0; /* Non-zero if -q. */ int t_flag = 0; /* Non-zero if -t. */ /* Save the program name. */ if ( (envprogname = strrchr(argv[0], '/')) != NULL) ++envprogname; else envprogname = argv[0]; log_ident = envprogname; mypid = (long)getpid(); mvlogmsgx = mvlogmsgx_wrapper; memfunc_pre_init(); if (memfunc_init() < 0) exit_errx("main: memfunc_init failed"); if ( (m_anon = mem_type_new_local(MTYPE_NAME(anonymous), "Anonymous memory", 0)) == NULL) exit_errx("main: mem_type_new_local failed"); if (init_time_data() < 0) exit_errx("main: init_time_data failed"); opterr = 0; while ( (opt = getopt(argc, argv, ":c:f:g:hi:qtu:v")) != -1) switch (opt) { case 'c': if (*optarg != '/') exit_errx("path in the -c option should be absolute"); if (chroot(optarg) < 0) exit_err("main: chroot(%s)", optarg); break; case 'f': ipastat_conf_file = optarg; break; case 'g': group_name = optarg; break; case 'h': usage(); return 0; case 'i': log_ident = optarg; break; case 'q': q_flag = 1; goto getopt_done; case 't': if (t_flag) mimic_real_config = 1; else t_flag = 1; break; case 'u': user_name = optarg; break; case 'v': show_version(); return 0; case ':': exit_errx("option requires an argument -- %c", optopt); /* NOTREACHED */ case '?': exit_errx("illegal option -- %c", optopt); /* NOTREACHED */ default: exit_errx("unexpected option -- %c", optopt); } if (optind < argc) exit_errx("non-switch argument \"%s\"", argv[optind]); getopt_done: if (user_name != NULL || group_name != NULL) change_user(); #ifdef HAVE_DL_INIT if (dl_init() != 0) exit_errx("main: dl_init failed"); #endif setlocale(LC_ALL, ""); if (t_flag) { if (configure(TEST_PARSING) < 0) return 1; show_config(); return 0; } if (!q_flag) exit_errx("statistics query is missed, use the -q option"); parse_query(argc, argv); ret = 1; if (configure(CONFIG_PARSING) < 0) { logmsgx(IPA_LOG_ERR, "main: configure failed"); goto done; } if (parse_opt_st_list() < 0) { logmsgx(IPA_LOG_ERR, "main: cannot parse statistics names given in command line"); goto done; } if (parse_opt_rules() < 0) { logmsgx(IPA_LOG_ERR, "main: cannot completely parse command line"); goto done; } if (ipastat_main() < 0) logmsgx(IPA_LOG_ERR, "main: cannot perform query"); else ret = 0; if (deinit_all() < 0) { logmsgx(IPA_LOG_ERR, "main: deinit_all failed"); ret = 1; } free_opt_tint_list(); free_opt_st_list(); free_opt_rules(); if (free_all() < 0) { logmsgx(IPA_LOG_ERR, "main: free_all failed"); ret = 1; } done: #ifdef HAVE_DL_EXIT if (dl_exit() != 0) { logmsgx(IPA_LOG_ERR, "main: dl_exit failed"); ret = 1; } #endif if (x_pat != NULL) regfree(&x_reg); return ret; }