summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLorenz Kästle <12514511+RincewindsHat@users.noreply.github.com>2025-09-16 14:31:19 +0200
committerGitHub <noreply@github.com>2025-09-16 14:31:19 +0200
commit1f20998d0c2c80fc2b407debfdd60d6039c87b8c (patch)
tree117f119af9353bdf77a03e5f466ce36b585237a6
parentc1f0f113c95787e0cb553f2d0ed0a702164eaa97 (diff)
parentdab009654cfb76d690d7fb64ca9a190d855a308f (diff)
downloadmonitoring-plugins-1f20998d.tar.gz
Merge pull request #2148 from RincewindsHat/refactor/check_apt
Refactor/check apt: implement new output functionality
-rw-r--r--plugins/check_apt.c251
-rw-r--r--plugins/check_apt.d/config.h9
-rw-r--r--plugins/t/check_apt.t14
3 files changed, 188 insertions, 86 deletions
diff --git a/plugins/check_apt.c b/plugins/check_apt.c
index ab66a8d2..9ed5b6cf 100644
--- a/plugins/check_apt.c
+++ b/plugins/check_apt.c
@@ -29,31 +29,33 @@
29 * 29 *
30 *****************************************************************************/ 30 *****************************************************************************/
31 31
32#include "states.h" 32#include "perfdata.h"
33const char *progname = "check_apt"; 33const char *progname = "check_apt";
34const char *copyright = "2006-2024"; 34const char *copyright = "2006-2024";
35const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
36 36
37#include "states.h"
38#include "output.h"
37#include "common.h" 39#include "common.h"
38#include "runcmd.h" 40#include "runcmd.h"
39#include "utils.h" 41#include "utils.h"
40#include "regex.h" 42#include "regex.h"
41#include "check_apt.d/config.h" 43#include "check_apt.d/config.h"
42 44
43/* Character for hidden input file option (for testing). */
44#define INPUT_FILE_OPT CHAR_MAX + 1
45/* the default opts can be overridden via the cmdline */ 45/* the default opts can be overridden via the cmdline */
46#define UPGRADE_DEFAULT_OPTS "-o 'Debug::NoLocking=true' -s -qq" 46const char *UPGRADE_DEFAULT_OPTS = "-o 'Debug::NoLocking=true' -s -qq";
47#define UPDATE_DEFAULT_OPTS "-q" 47const char *UPDATE_DEFAULT_OPTS = "-q";
48
48/* until i commit the configure.in patch which gets this, i'll define 49/* until i commit the configure.in patch which gets this, i'll define
49 * it here as well */ 50 * it here as well */
50#ifndef PATH_TO_APTGET 51#ifndef PATH_TO_APTGET
51# define PATH_TO_APTGET "/usr/bin/apt-get" 52# define PATH_TO_APTGET "/usr/bin/apt-get"
52#endif /* PATH_TO_APTGET */ 53#endif /* PATH_TO_APTGET */
54
53/* String found at the beginning of the apt output lines we're interested in */ 55/* String found at the beginning of the apt output lines we're interested in */
54#define PKGINST_PREFIX "Inst " 56const char *PKGINST_PREFIX = "Inst ";
55/* the RE that catches security updates */ 57/* the RE that catches security updates */
56#define SECURITY_RE "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)" 58const char *SECURITY_RE = "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)";
57 59
58/* some standard functions */ 60/* some standard functions */
59typedef struct { 61typedef struct {
@@ -66,20 +68,28 @@ void print_usage(void);
66 68
67/* construct the appropriate apt-get cmdline */ 69/* construct the appropriate apt-get cmdline */
68static char *construct_cmdline(upgrade_type /*u*/, const char * /*opts*/); 70static char *construct_cmdline(upgrade_type /*u*/, const char * /*opts*/);
71
69/* run an apt-get update */ 72/* run an apt-get update */
70static int run_update(char * /*update_opts*/); 73typedef struct {
74 mp_subcheck sc;
75 bool stderr_warning;
76 bool exec_warning;
77} run_update_result;
78static run_update_result run_update(char *update_opts);
71 79
72typedef struct { 80typedef struct {
73 int errorcode; 81 int errorcode;
74 int package_count; 82 size_t package_count;
75 int security_package_count; 83 size_t security_package_count;
76 char **packages_list; 84 char **packages_list;
77 char **secpackages_list; 85 char **secpackages_list;
86 bool exec_warning;
78} run_upgrade_result; 87} run_upgrade_result;
79 88
80/* run an apt-get upgrade */ 89/* run an apt-get upgrade */
81run_upgrade_result run_upgrade(upgrade_type upgrade, const char *do_include, const char *do_exclude, const char *do_critical, 90run_upgrade_result run_upgrade(upgrade_type upgrade, const char *do_include, const char *do_exclude,
82 const char *upgrade_opts, const char *input_filename); 91 const char *do_critical, const char *upgrade_opts,
92 const char *input_filename);
83 93
84/* add another clause to a regexp */ 94/* add another clause to a regexp */
85static char *add_to_regexp(char * /*expr*/, const char * /*next*/); 95static char *add_to_regexp(char * /*expr*/, const char * /*next*/);
@@ -107,6 +117,10 @@ int main(int argc, char **argv) {
107 117
108 const check_apt_config config = tmp_config.config; 118 const check_apt_config config = tmp_config.config;
109 119
120 if (config.output_format_is_set) {
121 mp_set_format(config.output_format);
122 }
123
110 /* Set signal handling and alarm timeout */ 124 /* Set signal handling and alarm timeout */
111 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 125 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
112 usage_va(_("Cannot catch SIGALRM")); 126 usage_va(_("Cannot catch SIGALRM"));
@@ -115,55 +129,91 @@ int main(int argc, char **argv) {
115 /* handle timeouts gracefully... */ 129 /* handle timeouts gracefully... */
116 alarm(timeout_interval); 130 alarm(timeout_interval);
117 131
118 mp_state_enum result = STATE_UNKNOWN; 132 mp_check overall = mp_check_init();
119 /* if they want to run apt-get update first... */ 133 /* if they want to run apt-get update first... */
120 if (config.do_update) { 134 if (config.do_update) {
121 result = run_update(config.update_opts); 135 run_update_result update_result = run_update(config.update_opts);
136
137 mp_add_subcheck_to_check(&overall, update_result.sc);
122 } 138 }
123 139
124 /* apt-get upgrade */ 140 /* apt-get upgrade */
125 run_upgrade_result upgrad_res = 141 run_upgrade_result upgrad_res =
126 run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical, config.upgrade_opts, config.input_filename); 142 run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical,
143 config.upgrade_opts, config.input_filename);
144
145 mp_subcheck sc_run_upgrade = mp_subcheck_init();
146 if (upgrad_res.errorcode == OK) {
147 sc_run_upgrade = mp_set_subcheck_state(sc_run_upgrade, STATE_OK);
148 }
149 xasprintf(&sc_run_upgrade.output, "Executed apt upgrade (dry run)");
127 150
128 result = max_state(result, upgrad_res.errorcode); 151 mp_add_subcheck_to_check(&overall, sc_run_upgrade);
129 int packages_available = upgrad_res.package_count; 152
130 int sec_count = upgrad_res.security_package_count; 153 size_t packages_available = upgrad_res.package_count;
154 size_t number_of_security_updates = upgrad_res.security_package_count;
131 char **packages_list = upgrad_res.packages_list; 155 char **packages_list = upgrad_res.packages_list;
132 char **secpackages_list = upgrad_res.secpackages_list; 156 char **secpackages_list = upgrad_res.secpackages_list;
133 157
134 if (sec_count > 0) { 158 mp_perfdata pd_security_updates = perfdata_init();
135 result = max_state(result, STATE_CRITICAL); 159 pd_security_updates.value = mp_create_pd_value(number_of_security_updates);
136 } else if (packages_available >= config.packages_warning && !config.only_critical) { 160 pd_security_updates.label = "critical_updates";
137 result = max_state(result, STATE_WARNING); 161
138 } else if (result > STATE_UNKNOWN) { 162 mp_subcheck sc_security_updates = mp_subcheck_init();
139 result = STATE_UNKNOWN; 163 xasprintf(&sc_security_updates.output, "Security updates available: %zu",
164 number_of_security_updates);
165 mp_add_perfdata_to_subcheck(&sc_security_updates, pd_security_updates);
166
167 if (number_of_security_updates > 0) {
168 sc_security_updates = mp_set_subcheck_state(sc_security_updates, STATE_CRITICAL);
169 } else {
170 sc_security_updates = mp_set_subcheck_state(sc_security_updates, STATE_OK);
140 } 171 }
141 172
142 printf(_("APT %s: %d packages available for %s (%d critical updates). %s%s%s%s|available_upgrades=%d;;;0 critical_updates=%d;;;0\n"), 173 mp_perfdata pd_other_updates = perfdata_init();
143 state_text(result), packages_available, (config.upgrade == DIST_UPGRADE) ? "dist-upgrade" : "upgrade", sec_count, 174 pd_other_updates.value = mp_create_pd_value(packages_available);
144 (stderr_warning) ? " warnings detected" : "", (stderr_warning && exec_warning) ? "," : "", 175 pd_other_updates.label = "available_upgrades";
145 (exec_warning) ? " errors detected" : "", (stderr_warning || exec_warning) ? "." : "", packages_available, sec_count); 176
177 mp_subcheck sc_other_updates = mp_subcheck_init();
178
179 xasprintf(&sc_other_updates.output, "Updates available: %zu", packages_available);
180 sc_other_updates = mp_set_subcheck_default_state(sc_other_updates, STATE_OK);
181 mp_add_perfdata_to_subcheck(&sc_other_updates, pd_other_updates);
182
183 if (packages_available >= config.packages_warning && !config.only_critical) {
184 sc_other_updates = mp_set_subcheck_state(sc_other_updates, STATE_WARNING);
185 }
146 186
147 if (config.list) { 187 if (config.list) {
148 qsort(secpackages_list, sec_count, sizeof(char *), cmpstringp); 188 qsort(secpackages_list, number_of_security_updates, sizeof(char *), cmpstringp);
149 qsort(packages_list, packages_available - sec_count, sizeof(char *), cmpstringp); 189 qsort(packages_list, packages_available - number_of_security_updates, sizeof(char *),
190 cmpstringp);
150 191
151 for (int i = 0; i < sec_count; i++) { 192 for (size_t i = 0; i < number_of_security_updates; i++) {
152 printf("%s (security)\n", secpackages_list[i]); 193 xasprintf(&sc_security_updates.output, "%s\n%s (security)", sc_security_updates.output,
194 secpackages_list[i]);
153 } 195 }
154 196
155 if (!config.only_critical) { 197 if (!config.only_critical) {
156 for (int i = 0; i < packages_available - sec_count; i++) { 198 for (size_t i = 0; i < packages_available - number_of_security_updates; i++) {
157 printf("%s\n", packages_list[i]); 199 xasprintf(&sc_other_updates.output, "%s\n%s", sc_other_updates.output,
200 packages_list[i]);
158 } 201 }
159 } 202 }
160 } 203 }
204 mp_add_subcheck_to_check(&overall, sc_security_updates);
205 mp_add_subcheck_to_check(&overall, sc_other_updates);
161 206
162 return result; 207 mp_exit(overall);
163} 208}
164 209
165/* process command-line arguments */ 210/* process command-line arguments */
166check_apt_config_wrapper process_arguments(int argc, char **argv) { 211check_apt_config_wrapper process_arguments(int argc, char **argv) {
212 enum {
213 /* Character for hidden input file option (for testing). */
214 INPUT_FILE_OPT = CHAR_MAX + 1,
215 output_format_index,
216 };
167 static struct option longopts[] = {{"version", no_argument, 0, 'V'}, 217 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
168 {"help", no_argument, 0, 'h'}, 218 {"help", no_argument, 0, 'h'},
169 {"verbose", no_argument, 0, 'v'}, 219 {"verbose", no_argument, 0, 'v'},
@@ -179,6 +229,7 @@ check_apt_config_wrapper process_arguments(int argc, char **argv) {
179 {"only-critical", no_argument, 0, 'o'}, 229 {"only-critical", no_argument, 0, 'o'},
180 {"input-file", required_argument, 0, INPUT_FILE_OPT}, 230 {"input-file", required_argument, 0, INPUT_FILE_OPT},
181 {"packages-warning", required_argument, 0, 'w'}, 231 {"packages-warning", required_argument, 0, 'w'},
232 {"output-format", required_argument, 0, output_format_index},
182 {0, 0, 0, 0}}; 233 {0, 0, 0, 0}};
183 234
184 check_apt_config_wrapper result = { 235 check_apt_config_wrapper result = {
@@ -257,6 +308,18 @@ check_apt_config_wrapper process_arguments(int argc, char **argv) {
257 case 'w': 308 case 'w':
258 result.config.packages_warning = atoi(optarg); 309 result.config.packages_warning = atoi(optarg);
259 break; 310 break;
311 case output_format_index: {
312 parsed_output_format parser = mp_parse_output_format(optarg);
313 if (!parser.parsing_success) {
314 // TODO List all available formats here, maybe add anothoer usage function
315 printf("Invalid output format: %s\n", optarg);
316 exit(STATE_UNKNOWN);
317 }
318
319 result.config.output_format_is_set = true;
320 result.config.output_format = parser.output_format;
321 break;
322 }
260 default: 323 default:
261 /* print short usage statement if args not parsable */ 324 /* print short usage statement if args not parsable */
262 usage5(); 325 usage5();
@@ -267,37 +330,38 @@ check_apt_config_wrapper process_arguments(int argc, char **argv) {
267} 330}
268 331
269/* run an apt-get upgrade */ 332/* run an apt-get upgrade */
270run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_include, const char *do_exclude, const char *do_critical, 333run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_include,
334 const char *do_exclude, const char *do_critical,
271 const char *upgrade_opts, const char *input_filename) { 335 const char *upgrade_opts, const char *input_filename) {
272 regex_t ereg; 336 regex_t exclude_regex;
273 /* initialize ereg as it is possible it is printed while uninitialized */ 337 /* initialize ereg as it is possible it is printed while uninitialized */
274 memset(&ereg, '\0', sizeof(ereg.buffer)); 338 memset(&exclude_regex, '\0', sizeof(exclude_regex.buffer));
275 339
276 run_upgrade_result result = { 340 run_upgrade_result result = {
277 .errorcode = STATE_UNKNOWN, 341 .errorcode = OK,
278 }; 342 };
279 343
280 if (upgrade == NO_UPGRADE) { 344 if (upgrade == NO_UPGRADE) {
281 result.errorcode = STATE_OK; 345 result.errorcode = OK;
282 return result; 346 return result;
283 } 347 }
284 348
285 int regres = 0; 349 int regres = 0;
286 regex_t ireg; 350 regex_t include_regex;
287 char rerrbuf[64]; 351 char rerrbuf[64];
288 /* compile the regexps */ 352 /* compile the regexps */
289 if (do_include != NULL) { 353 if (do_include != NULL) {
290 regres = regcomp(&ireg, do_include, REG_EXTENDED); 354 regres = regcomp(&include_regex, do_include, REG_EXTENDED);
291 if (regres != 0) { 355 if (regres != 0) {
292 regerror(regres, &ireg, rerrbuf, 64); 356 regerror(regres, &include_regex, rerrbuf, 64);
293 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 357 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
294 } 358 }
295 } 359 }
296 360
297 if (do_exclude != NULL) { 361 if (do_exclude != NULL) {
298 regres = regcomp(&ereg, do_exclude, REG_EXTENDED); 362 regres = regcomp(&exclude_regex, do_exclude, REG_EXTENDED);
299 if (regres != 0) { 363 if (regres != 0) {
300 regerror(regres, &ereg, rerrbuf, 64); 364 regerror(regres, &exclude_regex, rerrbuf, 64);
301 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 365 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
302 } 366 }
303 } 367 }
@@ -306,7 +370,7 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ
306 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE; 370 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE;
307 regres = regcomp(&sreg, crit_ptr, REG_EXTENDED); 371 regres = regcomp(&sreg, crit_ptr, REG_EXTENDED);
308 if (regres != 0) { 372 if (regres != 0) {
309 regerror(regres, &ereg, rerrbuf, 64); 373 regerror(regres, &exclude_regex, rerrbuf, 64);
310 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 374 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
311 } 375 }
312 376
@@ -322,13 +386,12 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ
322 result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0); 386 result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0);
323 } 387 }
324 388
325 /* apt-get upgrade only changes exit status if there is an 389 // apt-get upgrade only changes exit status if there is an
326 * internal error when run in dry-run mode. therefore we will 390 // internal error when run in dry-run mode.
327 * treat such an error as UNKNOWN */ 391 if (result.errorcode != 0) {
328 if (result.errorcode != STATE_OK) { 392 result.exec_warning = true;
329 exec_warning = 1; 393 result.errorcode = ERROR;
330 result.errorcode = STATE_UNKNOWN; 394 // fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline);
331 fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline);
332 } 395 }
333 396
334 char **pkglist = malloc(sizeof(char *) * chld_out.lines); 397 char **pkglist = malloc(sizeof(char *) * chld_out.lines);
@@ -349,27 +412,31 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ
349 * we may need to switch to the --print-uris output format, 412 * we may need to switch to the --print-uris output format,
350 * in which case the logic here will slightly change. 413 * in which case the logic here will slightly change.
351 */ 414 */
352 int package_counter = 0; 415 size_t package_counter = 0;
353 int security_package_counter = 0; 416 size_t security_package_counter = 0;
354 for (size_t i = 0; i < chld_out.lines; i++) { 417 for (size_t i = 0; i < chld_out.lines; i++) {
355 if (verbose) { 418 if (verbose) {
356 printf("%s\n", chld_out.line[i]); 419 printf("%s\n", chld_out.line[i]);
357 } 420 }
421
358 /* if it is a package we care about */ 422 /* if it is a package we care about */
359 if (strncmp(PKGINST_PREFIX, chld_out.line[i], strlen(PKGINST_PREFIX)) == 0 && 423 if (strncmp(PKGINST_PREFIX, chld_out.line[i], strlen(PKGINST_PREFIX)) == 0 &&
360 (do_include == NULL || regexec(&ireg, chld_out.line[i], 0, NULL, 0) == 0)) { 424 (do_include == NULL || regexec(&include_regex, chld_out.line[i], 0, NULL, 0) == 0)) {
361 /* if we're not excluding, or it's not in the 425 /* if we're not excluding, or it's not in the
362 * list of stuff to exclude */ 426 * list of stuff to exclude */
363 if (do_exclude == NULL || regexec(&ereg, chld_out.line[i], 0, NULL, 0) != 0) { 427 if (do_exclude == NULL || regexec(&exclude_regex, chld_out.line[i], 0, NULL, 0) != 0) {
364 package_counter++; 428 package_counter++;
365 if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) { 429 if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) {
366 security_package_counter++; 430 security_package_counter++;
431
367 if (verbose) { 432 if (verbose) {
368 printf("*"); 433 printf("*");
369 } 434 }
435
370 (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]); 436 (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]);
371 } else { 437 } else {
372 (pkglist)[package_counter - security_package_counter - 1] = pkg_name(chld_out.line[i]); 438 (pkglist)[package_counter - security_package_counter - 1] =
439 pkg_name(chld_out.line[i]);
373 } 440 }
374 if (verbose) { 441 if (verbose) {
375 printf("*%s\n", chld_out.line[i]); 442 printf("*%s\n", chld_out.line[i]);
@@ -377,6 +444,7 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ
377 } 444 }
378 } 445 }
379 } 446 }
447
380 result.package_count = package_counter; 448 result.package_count = package_counter;
381 result.security_package_count = security_package_counter; 449 result.security_package_count = security_package_counter;
382 result.packages_list = pkglist; 450 result.packages_list = pkglist;
@@ -385,41 +453,55 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ
385 /* If we get anything on stderr, at least set warning */ 453 /* If we get anything on stderr, at least set warning */
386 if (input_filename == NULL && chld_err.buflen) { 454 if (input_filename == NULL && chld_err.buflen) {
387 stderr_warning = true; 455 stderr_warning = true;
388 result.errorcode = max_state(result.errorcode, STATE_WARNING); 456 result.errorcode = ERROR;
457
389 if (verbose) { 458 if (verbose) {
390 for (size_t i = 0; i < chld_err.lines; i++) { 459 for (size_t i = 0; i < chld_err.lines; i++) {
391 fprintf(stderr, "%s\n", chld_err.line[i]); 460 fprintf(stderr, "%s\n", chld_err.line[i]);
392 } 461 }
393 } 462 }
394 } 463 }
464
395 if (do_include != NULL) { 465 if (do_include != NULL) {
396 regfree(&ireg); 466 regfree(&include_regex);
397 } 467 }
468
398 regfree(&sreg); 469 regfree(&sreg);
470
399 if (do_exclude != NULL) { 471 if (do_exclude != NULL) {
400 regfree(&ereg); 472 regfree(&exclude_regex);
401 } 473 }
474
402 free(cmdline); 475 free(cmdline);
476
403 return result; 477 return result;
404} 478}
405 479
406/* run an apt-get update (needs root) */ 480/* run an apt-get update (needs root) */
407int run_update(char *update_opts) { 481run_update_result run_update(char *update_opts) {
408 int result = STATE_UNKNOWN;
409 char *cmdline; 482 char *cmdline;
410 /* run the update */ 483 /* run the update */
411 cmdline = construct_cmdline(NO_UPGRADE, update_opts); 484 cmdline = construct_cmdline(NO_UPGRADE, update_opts);
412 485
486 run_update_result result = {
487 .exec_warning = false,
488 .stderr_warning = false,
489 .sc = mp_subcheck_init(),
490 };
491
492 result.sc = mp_set_subcheck_default_state(result.sc, STATE_OK);
493 xasprintf(&result.sc.output, "executing '%s' first", cmdline);
494
413 output chld_out; 495 output chld_out;
414 output chld_err; 496 output chld_err;
415 result = np_runcmd(cmdline, &chld_out, &chld_err, 0); 497 int cmd_error = np_runcmd(cmdline, &chld_out, &chld_err, 0);
416 /* apt-get update changes exit status if it can't fetch packages. 498 /* apt-get update changes exit status if it can't fetch packages.
417 * since we were explicitly asked to do so, this is treated as 499 * since we were explicitly asked to do so, this is treated as
418 * a critical error. */ 500 * a critical error. */
419 if (result != 0) { 501 if (cmd_error != 0) {
420 exec_warning = true; 502 exec_warning = true;
421 result = STATE_CRITICAL; 503 result.sc = mp_set_subcheck_state(result.sc, STATE_CRITICAL);
422 fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline); 504 xasprintf(&result.sc.output, _("'%s' exited with non-zero status.\n"), cmdline);
423 } 505 }
424 506
425 if (verbose) { 507 if (verbose) {
@@ -430,15 +512,18 @@ int run_update(char *update_opts) {
430 512
431 /* If we get anything on stderr, at least set warning */ 513 /* If we get anything on stderr, at least set warning */
432 if (chld_err.buflen) { 514 if (chld_err.buflen) {
433 stderr_warning = 1; 515 stderr_warning = true;
434 result = max_state(result, STATE_WARNING); 516 result.sc = mp_set_subcheck_state(
517 result.sc, max_state(mp_compute_subcheck_state(result.sc), STATE_WARNING));
435 if (verbose) { 518 if (verbose) {
436 for (size_t i = 0; i < chld_err.lines; i++) { 519 for (size_t i = 0; i < chld_err.lines; i++) {
437 fprintf(stderr, "%s\n", chld_err.line[i]); 520 fprintf(stderr, "%s\n", chld_err.line[i]);
438 } 521 }
439 } 522 }
440 } 523 }
524
441 free(cmdline); 525 free(cmdline);
526
442 return result; 527 return result;
443} 528}
444 529
@@ -520,7 +605,7 @@ char *construct_cmdline(upgrade_type upgrade, const char *opts) {
520 break; 605 break;
521 } 606 }
522 607
523 int len = 0; 608 size_t len = 0;
524 len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */ 609 len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */
525 len += strlen(opts_ptr) + 1; /* "opts " */ 610 len += strlen(opts_ptr) + 1; /* "opts " */
526 len += strlen(aptcmd) + 1; /* "upgrade\0" */ 611 len += strlen(aptcmd) + 1; /* "upgrade\0" */
@@ -558,7 +643,8 @@ void print_help(void) {
558 printf(" %s\n", _("List packages available for upgrade. Packages are printed sorted by")); 643 printf(" %s\n", _("List packages available for upgrade. Packages are printed sorted by"));
559 printf(" %s\n", _("name with security packages listed first.")); 644 printf(" %s\n", _("name with security packages listed first."));
560 printf(" %s\n", "-i, --include=REGEXP"); 645 printf(" %s\n", "-i, --include=REGEXP");
561 printf(" %s\n", _("Include only packages matching REGEXP. Can be specified multiple times")); 646 printf(" %s\n",
647 _("Include only packages matching REGEXP. Can be specified multiple times"));
562 printf(" %s\n", _("the values will be combined together. Any packages matching this list")); 648 printf(" %s\n", _("the values will be combined together. Any packages matching this list"));
563 printf(" %s\n", _("cause the plugin to return WARNING status. Others will be ignored.")); 649 printf(" %s\n", _("cause the plugin to return WARNING status. Others will be ignored."));
564 printf(" %s\n", _("Default is to include all packages.")); 650 printf(" %s\n", _("Default is to include all packages."));
@@ -567,7 +653,8 @@ void print_help(void) {
567 printf(" %s\n", _("otherwise be included. Can be specified multiple times; the values")); 653 printf(" %s\n", _("otherwise be included. Can be specified multiple times; the values"));
568 printf(" %s\n", _("will be combined together. Default is to exclude no packages.")); 654 printf(" %s\n", _("will be combined together. Default is to exclude no packages."));
569 printf(" %s\n", "-c, --critical=REGEXP"); 655 printf(" %s\n", "-c, --critical=REGEXP");
570 printf(" %s\n", _("If the full package information of any of the upgradable packages match")); 656 printf(" %s\n",
657 _("If the full package information of any of the upgradable packages match"));
571 printf(" %s\n", _("this REGEXP, the plugin will return CRITICAL status. Can be specified")); 658 printf(" %s\n", _("this REGEXP, the plugin will return CRITICAL status. Can be specified"));
572 printf(" %s\n", _("multiple times like above. Default is a regexp matching security")); 659 printf(" %s\n", _("multiple times like above. Default is a regexp matching security"));
573 printf(" %s\n", _("upgrades for Debian and Ubuntu:")); 660 printf(" %s\n", _("upgrades for Debian and Ubuntu:"));
@@ -576,15 +663,21 @@ void print_help(void) {
576 printf(" %s\n", _("information is compared against the critical list.")); 663 printf(" %s\n", _("information is compared against the critical list."));
577 printf(" %s\n", "-o, --only-critical"); 664 printf(" %s\n", "-o, --only-critical");
578 printf(" %s\n", _("Only warn about upgrades matching the critical list. The total number")); 665 printf(" %s\n", _("Only warn about upgrades matching the critical list. The total number"));
579 printf(" %s\n", _("of upgrades will be printed, but any non-critical upgrades will not cause")); 666 printf(" %s\n",
667 _("of upgrades will be printed, but any non-critical upgrades will not cause"));
580 printf(" %s\n", _("the plugin to return WARNING status.")); 668 printf(" %s\n", _("the plugin to return WARNING status."));
581 printf(" %s\n", "-w, --packages-warning"); 669 printf(" %s\n", "-w, --packages-warning");
582 printf(" %s\n", _("Minimum number of packages available for upgrade to return WARNING status.")); 670 printf(" %s\n",
671 _("Minimum number of packages available for upgrade to return WARNING status."));
583 printf(" %s\n\n", _("Default is 1 package.")); 672 printf(" %s\n\n", _("Default is 1 package."));
584 673
585 printf("%s\n\n", _("The following options require root privileges and should be used with care:")); 674 printf(UT_OUTPUT_FORMAT);
675
676 printf("%s\n\n",
677 _("The following options require root privileges and should be used with care:"));
586 printf(" %s\n", "-u, --update=OPTS"); 678 printf(" %s\n", "-u, --update=OPTS");
587 printf(" %s\n", _("First perform an 'apt-get update'. An optional OPTS parameter overrides")); 679 printf(" %s\n",
680 _("First perform an 'apt-get update'. An optional OPTS parameter overrides"));
588 printf(" %s\n", _("the default options. Note: you may also need to adjust the global")); 681 printf(" %s\n", _("the default options. Note: you may also need to adjust the global"));
589 printf(" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get")); 682 printf(" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get"));
590 printf(" %s\n", _("upgrade is expected to take longer than the default timeout.")); 683 printf(" %s\n", _("upgrade is expected to take longer than the default timeout."));
@@ -593,8 +686,10 @@ void print_help(void) {
593 printf(" %s\n", _("apt-get will be run with these command line options instead of the")); 686 printf(" %s\n", _("apt-get will be run with these command line options instead of the"));
594 printf(" %s", _("default ")); 687 printf(" %s", _("default "));
595 printf("(%s).\n", UPGRADE_DEFAULT_OPTS); 688 printf("(%s).\n", UPGRADE_DEFAULT_OPTS);
596 printf(" %s\n", _("Note that you may be required to have root privileges if you do not use")); 689 printf(" %s\n",
597 printf(" %s\n", _("the default options, which will only run a simulation and NOT perform the upgrade")); 690 _("Note that you may be required to have root privileges if you do not use"));
691 printf(" %s\n",
692 _("the default options, which will only run a simulation and NOT perform the upgrade"));
598 printf(" %s\n", "-d, --dist-upgrade=OPTS"); 693 printf(" %s\n", "-d, --dist-upgrade=OPTS");
599 printf(" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS")); 694 printf(" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS"));
600 printf(" %s\n", _("can be provided to override the default options.")); 695 printf(" %s\n", _("can be provided to override the default options."));
diff --git a/plugins/check_apt.d/config.h b/plugins/check_apt.d/config.h
index 981f4f42..e4d622f1 100644
--- a/plugins/check_apt.d/config.h
+++ b/plugins/check_apt.d/config.h
@@ -2,6 +2,7 @@
2 2
3#include "../../config.h" 3#include "../../config.h"
4#include <stddef.h> 4#include <stddef.h>
5#include "../lib/output.h"
5 6
6/* some constants */ 7/* some constants */
7typedef enum { 8typedef enum {
@@ -16,7 +17,7 @@ typedef struct {
16 bool only_critical; /* whether to warn about non-critical updates */ 17 bool only_critical; /* whether to warn about non-critical updates */
17 bool list; /* list packages available for upgrade */ 18 bool list; /* list packages available for upgrade */
18 /* number of packages available for upgrade to return WARNING status */ 19 /* number of packages available for upgrade to return WARNING status */
19 int packages_warning; 20 size_t packages_warning;
20 21
21 char *upgrade_opts; /* options to override defaults for upgrade */ 22 char *upgrade_opts; /* options to override defaults for upgrade */
22 char *update_opts; /* options to override defaults for update */ 23 char *update_opts; /* options to override defaults for update */
@@ -24,6 +25,9 @@ typedef struct {
24 char *do_exclude; /* regexp to only exclude certain packages */ 25 char *do_exclude; /* regexp to only exclude certain packages */
25 char *do_critical; /* regexp specifying critical packages */ 26 char *do_critical; /* regexp specifying critical packages */
26 char *input_filename; /* input filename for testing */ 27 char *input_filename; /* input filename for testing */
28
29 bool output_format_is_set;
30 mp_output_format output_format;
27} check_apt_config; 31} check_apt_config;
28 32
29check_apt_config check_apt_config_init() { 33check_apt_config check_apt_config_init() {
@@ -36,6 +40,7 @@ check_apt_config check_apt_config_init() {
36 .do_include = NULL, 40 .do_include = NULL,
37 .do_exclude = NULL, 41 .do_exclude = NULL,
38 .do_critical = NULL, 42 .do_critical = NULL,
39 .input_filename = NULL}; 43 .input_filename = NULL,
44 .output_format_is_set = false};
40 return tmp; 45 return tmp;
41} 46}
diff --git a/plugins/t/check_apt.t b/plugins/t/check_apt.t
index 430eb53e..736bc2f2 100644
--- a/plugins/t/check_apt.t
+++ b/plugins/t/check_apt.t
@@ -5,6 +5,7 @@
5# 5#
6 6
7use strict; 7use strict;
8use warnings;
8use Test::More; 9use Test::More;
9use NPTest; 10use NPTest;
10 11
@@ -12,18 +13,18 @@ sub make_result_regexp {
12 my ($warning, $critical) = @_; 13 my ($warning, $critical) = @_;
13 my $status; 14 my $status;
14 if ($warning == 0 && $critical == 0) { 15 if ($warning == 0 && $critical == 0) {
15 $status = "OK"; 16 $status = "OK";
16 } elsif ($critical == 0) { 17 } elsif ($critical == 0) {
17 $status = "WARNING"; 18 $status = "WARNING";
18 } else { 19 } else {
19 $status = "CRITICAL"; 20 $status = "CRITICAL";
20 } 21 }
21 return sprintf('/^APT %s: %d packages available for upgrade \(%d critical updates\)\. |available_upgrades=%d;;;0 critical_updates=%d;;;0$/', 22 return sprintf('/.*[%s].*Updates available: %d.*Security updates available: %d.*\'available_upgrades\'=%d;;; \'critical_updates\'=%d;;; /s',
22 $status, $warning, $critical, $warning, $critical); 23 $status, $warning, $critical, $warning, $critical);
23} 24}
24 25
25if (-x "./check_apt") { 26if (-x "./check_apt") {
26 plan tests => 36; 27 plan tests => 35;
27} else { 28} else {
28 plan skip_all => "No check_apt compiled"; 29 plan skip_all => "No check_apt compiled";
29} 30}
@@ -42,7 +43,8 @@ like( $result->output, make_result_regexp(13, 0), "Output correct" );
42 43
43$result = NPTest->testCmd( sprintf($testfile_command, "-o", "debian2") ); 44$result = NPTest->testCmd( sprintf($testfile_command, "-o", "debian2") );
44is( $result->return_code, 0, "Debian apt output, no critical" ); 45is( $result->return_code, 0, "Debian apt output, no critical" );
45like( $result->output, make_result_regexp(13, 0), "Output correct" ); 46# this test does not work, since -o was given
47# like( $result->output, make_result_regexp(13, 0), "Output correct" );
46 48
47$result = NPTest->testCmd( sprintf($testfile_command, "", "debian3") ); 49$result = NPTest->testCmd( sprintf($testfile_command, "", "debian3") );
48is( $result->return_code, 2, "Debian apt output, some critical" ); 50is( $result->return_code, 2, "Debian apt output, some critical" );