summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-03-12 22:01:46 +0100
committerLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-03-12 22:01:46 +0100
commit6ac236c1ef06ef17541d3919aed2a008aabaa7f4 (patch)
treec34e7cedcd3a4540b4de0e40df5f6ba104a45f7f
parent39b9b62adda3887e7706754d4e34a7eff2f793b5 (diff)
downloadmonitoring-plugins-6ac236c1.tar.gz
Refactor check_users
-rw-r--r--plugins/Makefile.am2
-rw-r--r--plugins/check_users.c250
-rw-r--r--plugins/check_users.d/config.h20
-rw-r--r--plugins/check_users.d/users.c166
-rw-r--r--plugins/check_users.d/users.h18
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 \
55 check_game.d \ 55 check_game.d \
56 check_radius.d \ 56 check_radius.d \
57 check_time.d \ 57 check_time.d \
58 check_users.d \
58 check_nagios.d \ 59 check_nagios.d \
59 check_dbi.d \ 60 check_dbi.d \
60 check_real.d \ 61 check_real.d \
@@ -152,6 +153,7 @@ check_tcp_LDADD = $(SSLOBJS)
152check_time_LDADD = $(NETLIBS) 153check_time_LDADD = $(NETLIBS)
153check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS) 154check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS)
154check_ups_LDADD = $(NETLIBS) 155check_ups_LDADD = $(NETLIBS)
156check_users_SOURCES = check_users.c check_users.d/users.c
155check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS) 157check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS)
156check_by_ssh_LDADD = $(NETLIBS) 158check_by_ssh_LDADD = $(NETLIBS)
157check_ide_smart_LDADD = $(BASEOBJS) 159check_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";
34const char *copyright = "2000-2024"; 34const char *copyright = "2000-2024";
35const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
36 36
37#include "common.h" 37#include "check_users.d/users.h"
38#include "utils.h" 38#include "output.h"
39#include "perfdata.h"
40#include "states.h"
41#include "utils_base.h"
42#include "./common.h"
43#include "./utils.h"
44#include "check_users.d/config.h"
45#include "thresholds.h"
39 46
40#if HAVE_WTSAPI32_H 47#if HAVE_WTSAPI32_H
41# include <windows.h> 48# include <windows.h>
@@ -53,29 +60,16 @@ const char *email = "devel@monitoring-plugins.org";
53# include <systemd/sd-login.h> 60# include <systemd/sd-login.h>
54#endif 61#endif
55 62
56#define possibly_set(a, b) ((a) == 0 ? (b) : 0) 63typedef struct process_argument_wrapper {
64 int errorcode;
65 check_users_config config;
66} check_users_config_wrapper;
67check_users_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
57 68
58static int process_arguments(int, char **); 69void print_help(void);
59static void print_help(void);
60void print_usage(void); 70void print_usage(void);
61 71
62static char *warning_range = NULL;
63static char *critical_range = NULL;
64static thresholds *thlds = NULL;
65
66int main(int argc, char **argv) { 72int main(int argc, char **argv) {
67 int users = -1;
68 int result = STATE_UNKNOWN;
69#if HAVE_WTSAPI32_H
70 WTS_SESSION_INFO *wtsinfo;
71 DWORD wtscount;
72 DWORD index;
73#elif HAVE_UTMPX_H
74 struct utmpx *putmpx;
75#else
76 char input_buffer[MAX_INPUT_BUFFER];
77#endif
78
79 setlocale(LC_ALL, ""); 73 setlocale(LC_ALL, "");
80 bindtextdomain(PACKAGE, LOCALEDIR); 74 bindtextdomain(PACKAGE, LOCALEDIR);
81 textdomain(PACKAGE); 75 textdomain(PACKAGE);
@@ -83,133 +77,100 @@ int main(int argc, char **argv) {
83 /* Parse extra opts if any */ 77 /* Parse extra opts if any */
84 argv = np_extra_opts(&argc, argv, progname); 78 argv = np_extra_opts(&argc, argv, progname);
85 79
86 if (process_arguments(argc, argv) == ERROR) { 80 check_users_config_wrapper tmp_config = process_arguments(argc, argv);
81
82 if (tmp_config.errorcode == ERROR) {
87 usage4(_("Could not parse arguments")); 83 usage4(_("Could not parse arguments"));
88 } 84 }
89 85
90 users = 0; 86 check_users_config config = tmp_config.config;
91
92#ifdef HAVE_LIBSYSTEMD
93 if (sd_booted() > 0) {
94 users = sd_get_sessions(NULL);
95 } else {
96#endif
97#if HAVE_WTSAPI32_H
98 if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) {
99 printf(_("Could not enumerate RD sessions: %d\n"), GetLastError());
100 return STATE_UNKNOWN;
101 }
102
103 for (index = 0; index < wtscount; index++) {
104 LPTSTR username;
105 DWORD size;
106 int len;
107
108 if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size)) {
109 continue;
110 }
111
112 len = lstrlen(username);
113
114 WTSFreeMemory(username);
115
116 if (len == 0) {
117 continue;
118 }
119
120 if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) {
121 users++;
122 }
123 }
124 87
125 WTSFreeMemory(wtsinfo); 88#ifdef _WIN32
126#elif HAVE_UTMPX_H 89# if HAVE_WTSAPI32_H
127 /* get currently logged users from utmpx */ 90 get_num_of_users_wrapper user_wrapper = get_num_of_users_windows();
128 setutxent(); 91# else
129 92# error Did not find WTSAPI32
130 while ((putmpx = getutxent()) != NULL) { 93# endif // HAVE_WTSAPI32_H
131 if (putmpx->ut_type == USER_PROCESS) {
132 users++;
133 }
134 }
135
136 endutxent();
137#else 94#else
138 /* run the command */ 95# ifdef HAVE_LIBSYSTEMD
139 child_process = spopen(WHO_COMMAND); 96 get_num_of_users_wrapper user_wrapper = get_num_of_users_systemd();
140 if (child_process == NULL) { 97# elif HAVE_UTMPX_H
141 printf(_("Could not open pipe: %s\n"), WHO_COMMAND); 98 get_num_of_users_wrapper user_wrapper = get_num_of_users_utmp();
142 return STATE_UNKNOWN; 99# else // !HAVE_LIBSYSTEMD && !HAVE_UTMPX_H
143 } 100 get_num_of_users_wrapper user_wrapper = get_num_of_users_who_command();
144 101# endif // HAVE_LIBSYSTEMD
145 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); 102#endif // _WIN32
146 if (child_stderr == NULL) { 103
147 printf(_("Could not open stderr for %s\n"), WHO_COMMAND); 104 mp_check overall = mp_check_init();
148 } 105 if (config.output_format_is_set) {
149 106 mp_set_format(config.output_format);
150 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
151 /* increment 'users' on all lines except total user count */
152 if (input_buffer[0] != '#') {
153 users++;
154 continue;
155 }
156
157 /* get total logged in users */
158 if (sscanf(input_buffer, _("# users=%d"), &users) == 1) {
159 break;
160 }
161 }
162
163 /* check STDERR */
164 if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
165 result = possibly_set(result, STATE_UNKNOWN);
166 } 107 }
167 (void)fclose(child_stderr); 108 mp_subcheck sc_users = mp_subcheck_init();
168 109
169 /* close the pipe */ 110 if (user_wrapper.errorcode != 0) {
170 if (spclose(child_process)) { 111 sc_users = mp_set_subcheck_state(sc_users, STATE_UNKNOWN);
171 result = possibly_set(result, STATE_UNKNOWN); 112 sc_users.output = "Failed to retrieve number of users";
113 mp_add_subcheck_to_check(&overall, sc_users);
114 mp_exit(overall);
172 } 115 }
173#endif
174#ifdef HAVE_LIBSYSTEMD
175 }
176#endif
177
178 /* check the user count against warning and critical thresholds */ 116 /* check the user count against warning and critical thresholds */
179 result = get_status((double)users, thlds);
180 117
181 if (result == STATE_UNKNOWN) { 118 mp_perfdata users_pd = {
182 printf("%s\n", _("Unable to read output")); 119 .label = "users",
183 } else { 120 .value = mp_create_pd_value(user_wrapper.users),
184 printf(_("USERS %s - %d users currently logged in |%s\n"), state_text(result), users, 121 };
185 sperfdata_int("users", users, "", warning_range, critical_range, true, 0, false, 0)); 122
123 users_pd = mp_pd_set_thresholds(users_pd, config.thresholds);
124 mp_add_perfdata_to_subcheck(&sc_users, users_pd);
125
126 int tmp_status = mp_get_pd_status(users_pd);
127 sc_users = mp_set_subcheck_state(sc_users, tmp_status);
128
129 switch (tmp_status) {
130 case STATE_WARNING:
131 xasprintf(&sc_users.output, "%d users currently logged in. This violates the warning threshold", user_wrapper.users);
132 break;
133 case STATE_CRITICAL:
134 xasprintf(&sc_users.output, "%d users currently logged in. This violates the critical threshold", user_wrapper.users);
135 break;
136 default:
137 xasprintf(&sc_users.output, "%d users currently logged in", user_wrapper.users);
186 } 138 }
187 139
188 return result; 140 mp_add_subcheck_to_check(&overall, sc_users);
141 mp_exit(overall);
189} 142}
190 143
144#define output_format_index CHAR_MAX + 1
145
191/* process command-line arguments */ 146/* process command-line arguments */
192int process_arguments(int argc, char **argv) { 147check_users_config_wrapper process_arguments(int argc, char **argv) {
193 static struct option longopts[] = {{"critical", required_argument, 0, 'c'}, 148 static struct option longopts[] = {{"critical", required_argument, 0, 'c'},
194 {"warning", required_argument, 0, 'w'}, 149 {"warning", required_argument, 0, 'w'},
195 {"version", no_argument, 0, 'V'}, 150 {"version", no_argument, 0, 'V'},
196 {"help", no_argument, 0, 'h'}, 151 {"help", no_argument, 0, 'h'},
152 {"output-format", required_argument, 0, output_format_index},
197 {0, 0, 0, 0}}; 153 {0, 0, 0, 0}};
198 154
199 if (argc < 2) { 155 if (argc < 2) {
200 usage("\n"); 156 usage(progname);
201 } 157 }
202 158
203 int option_char; 159 char *warning_range = NULL;
160 char *critical_range = NULL;
161 check_users_config_wrapper result = {
162 .config = check_users_config_init(),
163 .errorcode = OK,
164 };
165
204 while (true) { 166 while (true) {
205 int option = 0; 167 int counter = getopt_long(argc, argv, "+hVvc:w:", longopts, NULL);
206 option_char = getopt_long(argc, argv, "+hVvc:w:", longopts, &option);
207 168
208 if (option_char == -1 || option_char == EOF || option_char == 1) { 169 if (counter == -1 || counter == EOF || counter == 1) {
209 break; 170 break;
210 } 171 }
211 172
212 switch (option_char) { 173 switch (counter) {
213 case '?': /* print short usage statement if args not parsable */ 174 case '?': /* print short usage statement if args not parsable */
214 usage5(); 175 usage5();
215 case 'h': /* help */ 176 case 'h': /* help */
@@ -224,31 +185,45 @@ int process_arguments(int argc, char **argv) {
224 case 'w': /* warning */ 185 case 'w': /* warning */
225 warning_range = optarg; 186 warning_range = optarg;
226 break; 187 break;
227 } 188 case output_format_index: {
228 } 189 parsed_output_format parser = mp_parse_output_format(optarg);
229 190 if (!parser.parsing_success) {
230 option_char = optind; 191 // TODO List all available formats here, maybe add anothoer usage function
231 192 printf("Invalid output format: %s\n", optarg);
232 if (warning_range == NULL && argc > option_char) { 193 exit(STATE_UNKNOWN);
233 warning_range = argv[option_char++]; 194 }
234 }
235 195
236 if (critical_range == NULL && argc > option_char) { 196 result.config.output_format_is_set = true;
237 critical_range = argv[option_char++]; 197 result.config.output_format = parser.output_format;
198 break;
199 }
200 }
238 } 201 }
239 202
240 /* this will abort in case of invalid ranges */ 203 // TODO add proper verification for ranges here!
241 set_thresholds(&thlds, warning_range, critical_range); 204 if (warning_range) {
242 205 mp_range_parsed tmp = mp_parse_range_string(warning_range);
243 if (!thlds->warning) { 206 if (tmp.error == MP_PARSING_SUCCES) {
244 usage4(_("Warning threshold must be a valid range expression")); 207 result.config.thresholds.warning = tmp.range;
208 result.config.thresholds.warning_is_set = true;
209 } else {
210 printf("Failed to parse warning range: %s", warning_range);
211 exit(STATE_UNKNOWN);
212 }
245 } 213 }
246 214
247 if (!thlds->critical) { 215 if (critical_range) {
248 usage4(_("Critical threshold must be a valid range expression")); 216 mp_range_parsed tmp = mp_parse_range_string(critical_range);
217 if (tmp.error == MP_PARSING_SUCCES) {
218 result.config.thresholds.critical = tmp.range;
219 result.config.thresholds.critical_is_set = true;
220 } else {
221 printf("Failed to parse critical range: %s", critical_range);
222 exit(STATE_UNKNOWN);
223 }
249 } 224 }
250 225
251 return OK; 226 return result;
252} 227}
253 228
254void print_help(void) { 229void print_help(void) {
@@ -271,6 +246,7 @@ void print_help(void) {
271 printf(" %s\n", _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION")); 246 printf(" %s\n", _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION"));
272 printf(" %s\n", "-c, --critical=RANGE_EXPRESSION"); 247 printf(" %s\n", "-c, --critical=RANGE_EXPRESSION");
273 printf(" %s\n", _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION")); 248 printf(" %s\n", _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION"));
249 printf(UT_OUTPUT_FORMAT);
274 250
275 printf(UT_SUPPORT); 251 printf(UT_SUPPORT);
276} 252}
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 @@
1#pragma once
2
3#include "output.h"
4#include "thresholds.h"
5
6typedef struct check_users_config {
7 mp_thresholds thresholds;
8
9 bool output_format_is_set;
10 mp_output_format output_format;
11} check_users_config;
12
13check_users_config check_users_config_init() {
14 check_users_config tmp = {
15 .thresholds = mp_thresholds_init(),
16
17 .output_format_is_set = false,
18 };
19 return tmp;
20}
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 @@
1#include "./users.h"
2
3#ifdef _WIN32
4# ifdef HAVE_WTSAPI32_H
5# include <windows.h>
6# include <wtsapi32.h>
7# undef ERROR
8# define ERROR -1
9
10get_num_of_users_wrapper get_num_of_users_windows() {
11 WTS_SESSION_INFO *wtsinfo;
12 DWORD wtscount;
13
14 get_num_of_users_wrapper result = {};
15
16 if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) {
17 // printf(_("Could not enumerate RD sessions: %d\n"), GetLastError());
18 result.error = WINDOWS_COULD_NOT_ENUMERATE_SESSIONS;
19 return result;
20 }
21
22 for (DWORD index = 0; index < wtscount; index++) {
23 LPTSTR username;
24 DWORD size;
25
26 if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size)) {
27 continue;
28 }
29
30 int len = lstrlen(username);
31
32 WTSFreeMemory(username);
33
34 if (len == 0) {
35 continue;
36 }
37
38 if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) {
39 result.users++;
40 }
41 }
42
43 WTSFreeMemory(wtsinfo);
44 return result;
45}
46# else // HAVE_WTSAPI32_H
47# error On windows but without the WTSAPI32 lib
48# endif // HAVE_WTSAPI32_H
49
50#else // _WIN32
51
52# include "../../config.h"
53
54# ifdef HAVE_LIBSYSTEMD
55# include <systemd/sd-daemon.h>
56# include <systemd/sd-login.h>
57
58get_num_of_users_wrapper get_num_of_users_systemd() {
59 get_num_of_users_wrapper result = {};
60
61 // Test whether we booted with systemd
62 if (sd_booted() > 0) {
63 int users = sd_get_sessions(NULL);
64 if (users >= 0) {
65 // Success
66 result.users = users;
67 return result;
68 }
69
70 // Failure! return the error code
71 result.errorcode = users;
72 return result;
73 }
74
75 // Looks like we are not running systemd,
76 // return with error here
77 result.errorcode = NO_SYSTEMD_ERROR;
78 return result;
79}
80# endif
81
82# ifdef HAVE_UTMPX_H
83# include <utmpx.h>
84
85get_num_of_users_wrapper get_num_of_users_utmp() {
86 int users = 0;
87
88 /* get currently logged users from utmpx */
89 setutxent();
90
91 struct utmpx *putmpx;
92 while ((putmpx = getutxent()) != NULL) {
93 if (putmpx->ut_type == USER_PROCESS) {
94 users++;
95 }
96 }
97
98 endutxent();
99
100 get_num_of_users_wrapper result = {
101 .errorcode = 0,
102 .users = users,
103 };
104
105 return result;
106}
107# endif
108
109# ifndef HAVE_WTSAPI32_H
110# ifndef HAVE_LIBSYSTEMD
111# ifndef HAVE_UTMPX_H
112// Fall back option here for the others (probably still not on windows)
113
114# include "../popen.h"
115# include "../common.h"
116# include "../utils.h"
117
118get_num_of_users_wrapper get_num_of_users_who_command() {
119 /* run the command */
120 child_process = spopen(WHO_COMMAND);
121 if (child_process == NULL) {
122 // printf(_("Could not open pipe: %s\n"), WHO_COMMAND);
123 get_num_of_users_wrapper result = {
124 .errorcode = COULD_NOT_OPEN_PIPE,
125 };
126 return result;
127 }
128
129 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
130 if (child_stderr == NULL) {
131 // printf(_("Could not open stderr for %s\n"), WHO_COMMAND);
132 // TODO this error should probably be reported
133 }
134
135 get_num_of_users_wrapper result = {};
136 char input_buffer[MAX_INPUT_BUFFER];
137 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
138 /* increment 'users' on all lines except total user count */
139 if (input_buffer[0] != '#') {
140 result.users++;
141 continue;
142 }
143
144 /* get total logged in users */
145 if (sscanf(input_buffer, _("# users=%d"), &result.users) == 1) {
146 break;
147 }
148 }
149
150 /* check STDERR */
151 if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
152 // if this fails, something broke and the result can not be relied upon or so is the theorie here
153 result.errorcode = STDERR_COULD_NOT_BE_READ;
154 }
155 (void)fclose(child_stderr);
156
157 /* close the pipe */
158 spclose(child_process);
159
160 return result;
161}
162
163# endif
164# endif
165# endif
166#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 @@
1#pragma once
2
3typedef struct get_num_of_users_wrapper {
4 int errorcode;
5 int users;
6} get_num_of_users_wrapper;
7
8enum {
9 NO_SYSTEMD_ERROR = 64,
10 WINDOWS_COULD_NOT_ENUMERATE_SESSIONS,
11 COULD_NOT_OPEN_PIPE,
12 STDERR_COULD_NOT_BE_READ,
13};
14
15get_num_of_users_wrapper get_num_of_users_systemd();
16get_num_of_users_wrapper get_num_of_users_utmp();
17get_num_of_users_wrapper get_num_of_users_windows();
18get_num_of_users_wrapper get_num_of_users_who_command();