From fb4f46f93da4ac50654fdcc2f26b2f37c73a9230 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 22 Feb 2025 22:39:10 +0100 Subject: Migrate check_users to new ouput infrastructure --- plugins/Makefile.am | 1 + plugins/check_users.c | 249 ++++++++++++++++++++--------------------- plugins/check_users.d/config.h | 19 ++++ plugins/check_users.d/users.c | 166 +++++++++++++++++++++++++++ plugins/check_users.d/users.h | 18 +++ 5 files changed, 323 insertions(+), 130 deletions(-) create mode 100644 plugins/check_users.d/config.h create mode 100644 plugins/check_users.d/users.c create mode 100644 plugins/check_users.d/users.h (limited to 'plugins') diff --git a/plugins/Makefile.am b/plugins/Makefile.am index d43c1971..e0069653 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -125,6 +125,7 @@ check_time_LDADD = $(NETLIBS) check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS) check_ups_LDADD = $(NETLIBS) check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS) +check_users_SOURCES = check_users.c check_users.d/users.c check_by_ssh_LDADD = $(NETLIBS) check_ide_smart_LDADD = $(BASEOBJS) negate_LDADD = $(BASEOBJS) diff --git a/plugins/check_users.c b/plugins/check_users.c index f1e1c39d..6ec87d21 100644 --- a/plugins/check_users.c +++ b/plugins/check_users.c @@ -3,7 +3,7 @@ * Monitoring check_users plugin * * License: GPL - * Copyright (c) 2000-2024 Monitoring Plugins Development Team + * Copyright (c) 2000-2025 Monitoring Plugins Development Team * * Description: * @@ -30,12 +30,19 @@ * *****************************************************************************/ +#include "check_users.d/config.h" +#include "thresholds.h" const char *progname = "check_users"; -const char *copyright = "2000-2024"; +const char *copyright = "2000-2025"; const char *email = "devel@monitoring-plugins.org"; -#include "common.h" -#include "utils.h" +#include "check_users.d/users.h" +#include "output.h" +#include "perfdata.h" +#include "states.h" +#include "utils_base.h" +#include "./common.h" +#include "./utils.h" #if HAVE_WTSAPI32_H # include @@ -53,29 +60,16 @@ const char *email = "devel@monitoring-plugins.org"; # include #endif -#define possibly_set(a, b) ((a) == 0 ? (b) : 0) +typedef struct process_argument_wrapper { + int errorcode; + check_users_config config; +} process_argument_wrapper; -static int process_arguments(int, char **); -static void print_help(void); +process_argument_wrapper process_arguments(int /*argc*/, char ** /*argv*/); +void print_help(void); void print_usage(void); -static char *warning_range = NULL; -static char *critical_range = NULL; -static thresholds *thlds = NULL; - int main(int argc, char **argv) { - int users = -1; - int result = STATE_UNKNOWN; -#if HAVE_WTSAPI32_H - WTS_SESSION_INFO *wtsinfo; - DWORD wtscount; - DWORD index; -#elif HAVE_UTMPX_H - struct utmpx *putmpx; -#else - char input_buffer[MAX_INPUT_BUFFER]; -#endif - setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); @@ -83,121 +77,106 @@ int main(int argc, char **argv) { /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - if (process_arguments(argc, argv) == ERROR) - usage4(_("Could not parse arguments")); - - users = 0; - -#ifdef HAVE_LIBSYSTEMD - if (sd_booted() > 0) - users = sd_get_sessions(NULL); - else { -#endif -#if HAVE_WTSAPI32_H - if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) { - printf(_("Could not enumerate RD sessions: %d\n"), GetLastError()); - return STATE_UNKNOWN; - } - - for (index = 0; index < wtscount; index++) { - LPTSTR username; - DWORD size; - int len; - - if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size)) - continue; - - len = lstrlen(username); - - WTSFreeMemory(username); - - if (len == 0) - continue; - - if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) - users++; - } - - WTSFreeMemory(wtsinfo); -#elif HAVE_UTMPX_H - /* get currently logged users from utmpx */ - setutxent(); - - while ((putmpx = getutxent()) != NULL) - if (putmpx->ut_type == USER_PROCESS) - users++; - - endutxent(); -#else - /* run the command */ - child_process = spopen(WHO_COMMAND); - if (child_process == NULL) { - printf(_("Could not open pipe: %s\n"), WHO_COMMAND); - return STATE_UNKNOWN; - } - - child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); - if (child_stderr == NULL) - printf(_("Could not open stderr for %s\n"), WHO_COMMAND); + process_argument_wrapper tmp = process_arguments(argc, argv); - while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { - /* increment 'users' on all lines except total user count */ - if (input_buffer[0] != '#') { - users++; - continue; - } - - /* get total logged in users */ - if (sscanf(input_buffer, _("# users=%d"), &users) == 1) - break; + if (tmp.errorcode == ERROR) { + usage4(_("Could not parse arguments")); } - /* check STDERR */ - if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) - result = possibly_set(result, STATE_UNKNOWN); - (void)fclose(child_stderr); + check_users_config config = tmp.config; - /* close the pipe */ - if (spclose(child_process)) - result = possibly_set(result, STATE_UNKNOWN); -#endif -#ifdef HAVE_LIBSYSTEMD +#ifdef _WIN32 +# if HAVE_WTSAPI32_H + get_num_of_users_wrapper user_wrapper = get_num_of_users_windows(); +# else +# error Did not find WTSAPI32 +# endif // HAVE_WTSAPI32_H +#else +# ifdef HAVE_LIBSYSTEMD + get_num_of_users_wrapper user_wrapper = get_num_of_users_systemd(); +# elif HAVE_UTMPX_H + get_num_of_users_wrapper user_wrapper = get_num_of_users_utmp(); +# else // !HAVE_LIBSYSTEMD && !HAVE_UTMPX_H + get_num_of_users_wrapper user_wrapper = get_num_of_users_who_command(); +# endif // HAVE_LIBSYSTEMD +#endif // _WIN32 + + mp_check overall = mp_check_init(); + mp_subcheck sc_users = mp_subcheck_init(); + + if (user_wrapper.errorcode != 0) { + sc_users = mp_set_subcheck_state(sc_users, STATE_UNKNOWN); + sc_users.output = "Failed to retrieve number of users"; + mp_add_subcheck_to_check(&overall, sc_users); + mp_exit(overall); } -#endif - /* check the user count against warning and critical thresholds */ - result = get_status((double)users, thlds); - if (result == STATE_UNKNOWN) - printf("%s\n", _("Unable to read output")); - else { - printf(_("USERS %s - %d users currently logged in |%s\n"), state_text(result), users, - sperfdata_int("users", users, "", warning_range, critical_range, true, 0, false, 0)); + mp_perfdata users_pd = { + .label = "users", + .value = mp_create_pd_value(user_wrapper.users), + }; + + users_pd = mp_pd_set_thresholds(users_pd, config.thresholds); + mp_add_perfdata_to_subcheck(&sc_users, users_pd); + + int tmp_status = mp_get_pd_status(users_pd); + sc_users = mp_set_subcheck_state(sc_users, tmp_status); + + switch (tmp_status) { + case STATE_WARNING: + xasprintf(&sc_users.output, "%d users currently logged in. This violates the warning threshold", user_wrapper.users); + break; + case STATE_CRITICAL: + xasprintf(&sc_users.output, "%d users currently logged in. This violates the critical threshold", user_wrapper.users); + break; + default: + xasprintf(&sc_users.output, "%d users currently logged in", user_wrapper.users); } - return result; + mp_add_subcheck_to_check(&overall, sc_users); + mp_exit(overall); } +#define output_format_index CHAR_MAX + 1 + /* process command-line arguments */ -int process_arguments(int argc, char **argv) { +process_argument_wrapper process_arguments(int argc, char **argv) { static struct option longopts[] = {{"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; - if (argc < 2) + if (argc < 2) { usage("\n"); + } + + char *warning_range = NULL; + char *critical_range = NULL; + check_users_config config = check_users_config_init(); - int option_char; while (true) { - int option = 0; - option_char = getopt_long(argc, argv, "+hVvc:w:", longopts, &option); + int counter = getopt_long(argc, argv, "+hVvc:w:", longopts, NULL); - if (option_char == -1 || option_char == EOF || option_char == 1) + if (counter == -1 || counter == EOF || counter == 1) { break; + } - switch (option_char) { + switch (counter) { + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + config.output_format_is_set = true; + config.output_format = parser.output_format; + break; + } case '?': /* print short usage statement if args not parsable */ usage5(); case 'h': /* help */ @@ -215,26 +194,35 @@ int process_arguments(int argc, char **argv) { } } - option_char = optind; - - if (warning_range == NULL && argc > option_char) - warning_range = argv[option_char++]; - - if (critical_range == NULL && argc > option_char) - critical_range = argv[option_char++]; - - /* this will abort in case of invalid ranges */ - set_thresholds(&thlds, warning_range, critical_range); - - if (!thlds->warning) { - usage4(_("Warning threshold must be a valid range expression")); + // TODO add proper verification for ranges here! + if (warning_range) { + mp_range_parsed tmp = mp_parse_range_string(warning_range); + if (tmp.error == MP_PARSING_SUCCES) { + config.thresholds.warning = tmp.range; + config.thresholds.warning_is_set = true; + } else { + printf("Failed to parse warning range: %s", warning_range); + exit(STATE_UNKNOWN); + } } - if (!thlds->critical) { - usage4(_("Critical threshold must be a valid range expression")); + if (critical_range) { + mp_range_parsed tmp = mp_parse_range_string(critical_range); + if (tmp.error == MP_PARSING_SUCCES) { + config.thresholds.critical = tmp.range; + config.thresholds.critical_is_set = true; + } else { + printf("Failed to parse critical range: %s", critical_range); + exit(STATE_UNKNOWN); + } } - return OK; + process_argument_wrapper result = { + .errorcode = OK, + .config = config, + }; + + return result; } void print_help(void) { @@ -257,6 +245,7 @@ void print_help(void) { printf(" %s\n", _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION")); printf(" %s\n", "-c, --critical=RANGE_EXPRESSION"); printf(" %s\n", _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION")); + printf(UT_OUTPUT_FORMAT); printf(UT_SUPPORT); } diff --git a/plugins/check_users.d/config.h b/plugins/check_users.d/config.h new file mode 100644 index 00000000..35b7b7f8 --- /dev/null +++ b/plugins/check_users.d/config.h @@ -0,0 +1,19 @@ +#pragma once + +#include "output.h" +#include "thresholds.h" +typedef struct check_users_config { + mp_thresholds thresholds; + + bool output_format_is_set; + mp_output_format output_format; +} check_users_config; + +check_users_config check_users_config_init() { + check_users_config tmp = { + .thresholds = mp_thresholds_init(), + + .output_format_is_set = false, + }; + return tmp; +} diff --git a/plugins/check_users.d/users.c b/plugins/check_users.d/users.c new file mode 100644 index 00000000..7969ae79 --- /dev/null +++ b/plugins/check_users.d/users.c @@ -0,0 +1,166 @@ +#include "./users.h" + +#ifdef _WIN32 +# ifdef HAVE_WTSAPI32_H +# include +# include +# undef ERROR +# define ERROR -1 + +get_num_of_users_wrapper get_num_of_users_windows() { + WTS_SESSION_INFO *wtsinfo; + DWORD wtscount; + + get_num_of_users_wrapper result = {}; + + if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) { + // printf(_("Could not enumerate RD sessions: %d\n"), GetLastError()); + result.error = WINDOWS_COULD_NOT_ENUMERATE_SESSIONS; + return result; + } + + for (DWORD index = 0; index < wtscount; index++) { + LPTSTR username; + DWORD size; + + if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size)) { + continue; + } + + int len = lstrlen(username); + + WTSFreeMemory(username); + + if (len == 0) { + continue; + } + + if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) { + result.users++; + } + } + + WTSFreeMemory(wtsinfo); + return result; +} +# else // HAVE_WTSAPI32_H +# error On windows but without the WTSAPI32 lib +# endif // HAVE_WTSAPI32_H + +#else // _WIN32 + +# include "../../config.h" + +# ifdef HAVE_LIBSYSTEMD +# include +# include + +get_num_of_users_wrapper get_num_of_users_systemd() { + get_num_of_users_wrapper result = {}; + + // Test whether we booted with systemd + if (sd_booted() > 0) { + int users = sd_get_sessions(NULL); + if (users >= 0) { + // Success + result.users = users; + return result; + } + + // Failure! return the error code + result.errorcode = users; + return result; + } + + // Looks like we are not running systemd, + // return with error here + result.errorcode = NO_SYSTEMD_ERROR; + return result; +} +# endif + +# ifdef HAVE_UTMPX_H +# include + +get_num_of_users_wrapper get_num_of_users_utmp() { + int users = 0; + + /* get currently logged users from utmpx */ + setutxent(); + + struct utmpx *putmpx; + while ((putmpx = getutxent()) != NULL) { + if (putmpx->ut_type == USER_PROCESS) { + users++; + } + } + + endutxent(); + + get_num_of_users_wrapper result = { + .errorcode = 0, + .users = users, + }; + + return result; +} +# endif + +# ifndef HAVE_WTSAPI32_H +# ifndef HAVE_LIBSYSTEMD +# ifndef HAVE_UTMPX_H +// Fall back option here for the others (probably still not on windows) + +# include "../popen.h" +# include "../common.h" +# include "../utils.h" + +get_num_of_users_wrapper get_num_of_users_who_command() { + /* run the command */ + child_process = spopen(WHO_COMMAND); + if (child_process == NULL) { + // printf(_("Could not open pipe: %s\n"), WHO_COMMAND); + get_num_of_users_wrapper result = { + .errorcode = COULD_NOT_OPEN_PIPE, + }; + return result; + } + + child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); + if (child_stderr == NULL) { + // printf(_("Could not open stderr for %s\n"), WHO_COMMAND); + // TODO this error should probably be reported + } + + get_num_of_users_wrapper result = {}; + char input_buffer[MAX_INPUT_BUFFER]; + while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { + /* increment 'users' on all lines except total user count */ + if (input_buffer[0] != '#') { + result.users++; + continue; + } + + /* get total logged in users */ + if (sscanf(input_buffer, _("# users=%d"), &result.users) == 1) { + break; + } + } + + /* check STDERR */ + if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { + // if this fails, something broke and the result can not be relied upon or so is the theorie here + result.errorcode = STDERR_COULD_NOT_BE_READ; + } + (void)fclose(child_stderr); + + /* close the pipe */ + spclose(child_process); + + return result; +} + +# endif +# endif +# endif +#endif diff --git a/plugins/check_users.d/users.h b/plugins/check_users.d/users.h new file mode 100644 index 00000000..aacba775 --- /dev/null +++ b/plugins/check_users.d/users.h @@ -0,0 +1,18 @@ +#pragma once + +typedef struct get_num_of_users_wrapper { + int errorcode; + int users; +} get_num_of_users_wrapper; + +enum { + NO_SYSTEMD_ERROR = 64, + WINDOWS_COULD_NOT_ENUMERATE_SESSIONS, + COULD_NOT_OPEN_PIPE, + STDERR_COULD_NOT_BE_READ, +}; + +get_num_of_users_wrapper get_num_of_users_systemd(); +get_num_of_users_wrapper get_num_of_users_utmp(); +get_num_of_users_wrapper get_num_of_users_windows(); +get_num_of_users_wrapper get_num_of_users_who_command(); -- cgit v1.2.3-74-g34f1