[monitoring-plugins] Refactor check_users
Lorenz Kästle
git at monitoring-plugins.org
Mon Aug 11 23:30:12 CEST 2025
Module: monitoring-plugins
Branch: master
Commit: 6ac236c1ef06ef17541d3919aed2a008aabaa7f4
Author: Lorenz Kästle <12514511+RincewindsHat at users.noreply.github.com>
Date: Wed Mar 12 22:01:46 2025 +0100
URL: https://www.monitoring-plugins.org/repositories/monitoring-plugins/commit/?id=6ac236c1
Refactor check_users
---
plugins/Makefile.am | 2 +
plugins/check_users.c | 250 +++++++++++++++++++----------------------
plugins/check_users.d/config.h | 20 ++++
plugins/check_users.d/users.c | 166 +++++++++++++++++++++++++++
plugins/check_users.d/users.h | 18 +++
5 files changed, 319 insertions(+), 137 deletions(-)
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 5cd20319..0a2a91aa 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -55,6 +55,7 @@ EXTRA_DIST = t \
check_game.d \
check_radius.d \
check_time.d \
+ check_users.d \
check_nagios.d \
check_dbi.d \
check_real.d \
@@ -152,6 +153,7 @@ check_tcp_LDADD = $(SSLOBJS)
check_time_LDADD = $(NETLIBS)
check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS)
check_ups_LDADD = $(NETLIBS)
+check_users_SOURCES = check_users.c check_users.d/users.c
check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS)
check_by_ssh_LDADD = $(NETLIBS)
check_ide_smart_LDADD = $(BASEOBJS)
diff --git a/plugins/check_users.c b/plugins/check_users.c
index e91ed4a0..61427f97 100644
--- a/plugins/check_users.c
+++ b/plugins/check_users.c
@@ -34,8 +34,15 @@ const char *progname = "check_users";
const char *copyright = "2000-2024";
const char *email = "devel at 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"
+#include "check_users.d/config.h"
+#include "thresholds.h"
#if HAVE_WTSAPI32_H
# include <windows.h>
@@ -53,29 +60,16 @@ const char *email = "devel at monitoring-plugins.org";
# include <systemd/sd-login.h>
#endif
-#define possibly_set(a, b) ((a) == 0 ? (b) : 0)
+typedef struct process_argument_wrapper {
+ int errorcode;
+ check_users_config config;
+} check_users_config_wrapper;
+check_users_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
-static int process_arguments(int, char **);
-static void print_help(void);
+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,133 +77,100 @@ int main(int argc, char **argv) {
/* Parse extra opts if any */
argv = np_extra_opts(&argc, argv, progname);
- if (process_arguments(argc, argv) == ERROR) {
+ check_users_config_wrapper tmp_config = process_arguments(argc, argv);
+
+ if (tmp_config.errorcode == 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++;
- }
- }
+ check_users_config config = tmp_config.config;
- 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();
+#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
- /* 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);
- }
-
- 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;
- }
- }
-
- /* check STDERR */
- if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
- result = possibly_set(result, STATE_UNKNOWN);
+# 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();
+ if (config.output_format_is_set) {
+ mp_set_format(config.output_format);
}
- (void)fclose(child_stderr);
+ mp_subcheck sc_users = mp_subcheck_init();
- /* close the pipe */
- if (spclose(child_process)) {
- result = possibly_set(result, STATE_UNKNOWN);
+ 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
-#ifdef HAVE_LIBSYSTEMD
- }
-#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) {
+check_users_config_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) {
- usage("\n");
+ usage(progname);
}
- int option_char;
+ char *warning_range = NULL;
+ char *critical_range = NULL;
+ check_users_config_wrapper result = {
+ .config = check_users_config_init(),
+ .errorcode = OK,
+ };
+
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 '?': /* print short usage statement if args not parsable */
usage5();
case 'h': /* help */
@@ -224,31 +185,45 @@ int process_arguments(int argc, char **argv) {
case 'w': /* warning */
warning_range = optarg;
break;
- }
- }
-
- option_char = optind;
-
- if (warning_range == NULL && argc > option_char) {
- warning_range = argv[option_char++];
- }
+ 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);
+ }
- if (critical_range == NULL && argc > option_char) {
- critical_range = argv[option_char++];
+ result.config.output_format_is_set = true;
+ result.config.output_format = parser.output_format;
+ break;
+ }
+ }
}
- /* 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) {
+ result.config.thresholds.warning = tmp.range;
+ result.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) {
+ result.config.thresholds.critical = tmp.range;
+ result.config.thresholds.critical_is_set = true;
+ } else {
+ printf("Failed to parse critical range: %s", critical_range);
+ exit(STATE_UNKNOWN);
+ }
}
- return OK;
+ return result;
}
void print_help(void) {
@@ -271,6 +246,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..26d3ee70
--- /dev/null
+++ b/plugins/check_users.d/config.h
@@ -0,0 +1,20 @@
+#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 <windows.h>
+# include <wtsapi32.h>
+# 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 <systemd/sd-daemon.h>
+# include <systemd/sd-login.h>
+
+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 <utmpx.h>
+
+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();
More information about the Commits
mailing list