summaryrefslogtreecommitdiffstats
path: root/plugins/check_apt.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_apt.c')
-rw-r--r--plugins/check_apt.c388
1 files changed, 242 insertions, 146 deletions
diff --git a/plugins/check_apt.c b/plugins/check_apt.c
index 1eda45dd..9ed5b6cf 100644
--- a/plugins/check_apt.c
+++ b/plugins/check_apt.c
@@ -29,92 +29,98 @@
29 * 29 *
30 *****************************************************************************/ 30 *****************************************************************************/
31 31
32#include "perfdata.h"
32const char *progname = "check_apt"; 33const char *progname = "check_apt";
33const char *copyright = "2006-2024"; 34const char *copyright = "2006-2024";
34const char *email = "devel@monitoring-plugins.org"; 35const char *email = "devel@monitoring-plugins.org";
35 36
37#include "states.h"
38#include "output.h"
36#include "common.h" 39#include "common.h"
37#include "runcmd.h" 40#include "runcmd.h"
38#include "utils.h" 41#include "utils.h"
39#include "regex.h" 42#include "regex.h"
43#include "check_apt.d/config.h"
40 44
41/* some constants */
42typedef enum {
43 UPGRADE,
44 DIST_UPGRADE,
45 NO_UPGRADE
46} upgrade_type;
47
48/* Character for hidden input file option (for testing). */
49#define INPUT_FILE_OPT CHAR_MAX + 1
50/* the default opts can be overridden via the cmdline */ 45/* the default opts can be overridden via the cmdline */
51#define UPGRADE_DEFAULT_OPTS "-o 'Debug::NoLocking=true' -s -qq" 46const char *UPGRADE_DEFAULT_OPTS = "-o 'Debug::NoLocking=true' -s -qq";
52#define UPDATE_DEFAULT_OPTS "-q" 47const char *UPDATE_DEFAULT_OPTS = "-q";
48
53/* 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
54 * it here as well */ 50 * it here as well */
55#ifndef PATH_TO_APTGET 51#ifndef PATH_TO_APTGET
56# define PATH_TO_APTGET "/usr/bin/apt-get" 52# define PATH_TO_APTGET "/usr/bin/apt-get"
57#endif /* PATH_TO_APTGET */ 53#endif /* PATH_TO_APTGET */
54
58/* 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 */
59#define PKGINST_PREFIX "Inst " 56const char *PKGINST_PREFIX = "Inst ";
60/* the RE that catches security updates */ 57/* the RE that catches security updates */
61#define SECURITY_RE "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)" 58const char *SECURITY_RE = "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)";
62 59
63/* some standard functions */ 60/* some standard functions */
64static int process_arguments(int /*argc*/, char ** /*argv*/); 61typedef struct {
62 int errorcode;
63 check_apt_config config;
64} check_apt_config_wrapper;
65static check_apt_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
65static void print_help(void); 66static void print_help(void);
66void print_usage(void); 67void print_usage(void);
67 68
68/* construct the appropriate apt-get cmdline */ 69/* construct the appropriate apt-get cmdline */
69static char *construct_cmdline(upgrade_type u, const char *opts); 70static char *construct_cmdline(upgrade_type /*u*/, const char * /*opts*/);
71
70/* run an apt-get update */ 72/* run an apt-get update */
71static int run_update(void); 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);
72 79
73typedef struct { 80typedef struct {
74 int errorcode; 81 int errorcode;
75 int package_count; 82 size_t package_count;
76 int security_package_count; 83 size_t security_package_count;
77 char **packages_list; 84 char **packages_list;
78 char **secpackages_list; 85 char **secpackages_list;
86 bool exec_warning;
79} run_upgrade_result; 87} run_upgrade_result;
80 88
81/* run an apt-get upgrade */ 89/* run an apt-get upgrade */
82static run_upgrade_result run_upgrade(void); 90run_upgrade_result run_upgrade(upgrade_type upgrade, const char *do_include, const char *do_exclude,
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*/);
86/* extract package name from Inst line */ 96/* extract package name from Inst line */
87static char *pkg_name(char *line); 97static char *pkg_name(char * /*line*/);
88/* string comparison function for qsort */ 98/* string comparison function for qsort */
89static int cmpstringp(const void *p1, const void *p2); 99static int cmpstringp(const void * /*p1*/, const void * /*p2*/);
90 100
91/* configuration variables */ 101/* configuration variables */
92static int verbose = 0; /* -v */ 102static int verbose = 0; /* -v */
93static bool list = false; /* list packages available for upgrade */
94static bool do_update = false; /* whether to call apt-get update */
95static bool only_critical = false; /* whether to warn about non-critical updates */
96static upgrade_type upgrade = UPGRADE; /* which type of upgrade to do */
97static char *upgrade_opts = NULL; /* options to override defaults for upgrade */
98static char *update_opts = NULL; /* options to override defaults for update */
99static char *do_include = NULL; /* regexp to only include certain packages */
100static char *do_exclude = NULL; /* regexp to only exclude certain packages */
101static char *do_critical = NULL; /* regexp specifying critical packages */
102static char *input_filename = NULL; /* input filename for testing */
103/* number of packages available for upgrade to return WARNING status */
104static int packages_warning = 1;
105 103
106/* other global variables */ 104/* other global variables */
107static int stderr_warning = 0; /* if a cmd issued output on stderr */ 105static bool stderr_warning = false; /* if a cmd issued output on stderr */
108static int exec_warning = 0; /* if a cmd exited non-zero */ 106static bool exec_warning = false; /* if a cmd exited non-zero */
109 107
110int main(int argc, char **argv) { 108int main(int argc, char **argv) {
111 /* Parse extra opts if any */ 109 /* Parse extra opts if any */
112 argv = np_extra_opts(&argc, argv, progname); 110 argv = np_extra_opts(&argc, argv, progname);
113 111
114 if (process_arguments(argc, argv) == ERROR) { 112 check_apt_config_wrapper tmp_config = process_arguments(argc, argv);
113
114 if (tmp_config.errorcode == ERROR) {
115 usage_va(_("Could not parse arguments")); 115 usage_va(_("Could not parse arguments"));
116 } 116 }
117 117
118 const check_apt_config config = tmp_config.config;
119
120 if (config.output_format_is_set) {
121 mp_set_format(config.output_format);
122 }
123
118 /* Set signal handling and alarm timeout */ 124 /* Set signal handling and alarm timeout */
119 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { 125 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
120 usage_va(_("Cannot catch SIGALRM")); 126 usage_va(_("Cannot catch SIGALRM"));
@@ -123,54 +129,91 @@ int main(int argc, char **argv) {
123 /* handle timeouts gracefully... */ 129 /* handle timeouts gracefully... */
124 alarm(timeout_interval); 130 alarm(timeout_interval);
125 131
126 int result = STATE_UNKNOWN; 132 mp_check overall = mp_check_init();
127 /* if they want to run apt-get update first... */ 133 /* if they want to run apt-get update first... */
128 if (do_update) { 134 if (config.do_update) {
129 result = run_update(); 135 run_update_result update_result = run_update(config.update_opts);
136
137 mp_add_subcheck_to_check(&overall, update_result.sc);
130 } 138 }
131 139
132 /* apt-get upgrade */ 140 /* apt-get upgrade */
133 run_upgrade_result upgrad_res = run_upgrade(); 141 run_upgrade_result upgrad_res =
142 run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical,
143 config.upgrade_opts, config.input_filename);
134 144
135 result = max_state(result, upgrad_res.errorcode); 145 mp_subcheck sc_run_upgrade = mp_subcheck_init();
136 int packages_available = upgrad_res.package_count; 146 if (upgrad_res.errorcode == OK) {
137 int sec_count = upgrad_res.security_package_count; 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)");
150
151 mp_add_subcheck_to_check(&overall, sc_run_upgrade);
152
153 size_t packages_available = upgrad_res.package_count;
154 size_t number_of_security_updates = upgrad_res.security_package_count;
138 char **packages_list = upgrad_res.packages_list; 155 char **packages_list = upgrad_res.packages_list;
139 char **secpackages_list = upgrad_res.secpackages_list; 156 char **secpackages_list = upgrad_res.secpackages_list;
140 157
141 if (sec_count > 0) { 158 mp_perfdata pd_security_updates = perfdata_init();
142 result = max_state(result, STATE_CRITICAL); 159 pd_security_updates.value = mp_create_pd_value(number_of_security_updates);
143 } else if (packages_available >= packages_warning && only_critical == false) { 160 pd_security_updates.label = "critical_updates";
144 result = max_state(result, STATE_WARNING); 161
145 } else if (result > STATE_UNKNOWN) { 162 mp_subcheck sc_security_updates = mp_subcheck_init();
146 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);
147 } 171 }
148 172
149 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();
150 state_text(result), packages_available, (upgrade == DIST_UPGRADE) ? "dist-upgrade" : "upgrade", sec_count, 174 pd_other_updates.value = mp_create_pd_value(packages_available);
151 (stderr_warning) ? " warnings detected" : "", (stderr_warning && exec_warning) ? "," : "", 175 pd_other_updates.label = "available_upgrades";
152 (exec_warning) ? " errors detected" : "", (stderr_warning || exec_warning) ? "." : "", packages_available, sec_count);
153 176
154 if (list) { 177 mp_subcheck sc_other_updates = mp_subcheck_init();
155 qsort(secpackages_list, sec_count, sizeof(char *), cmpstringp);
156 qsort(packages_list, packages_available - sec_count, sizeof(char *), cmpstringp);
157 178
158 for (int i = 0; i < sec_count; i++) { 179 xasprintf(&sc_other_updates.output, "Updates available: %zu", packages_available);
159 printf("%s (security)\n", secpackages_list[i]); 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 }
186
187 if (config.list) {
188 qsort(secpackages_list, number_of_security_updates, sizeof(char *), cmpstringp);
189 qsort(packages_list, packages_available - number_of_security_updates, sizeof(char *),
190 cmpstringp);
191
192 for (size_t i = 0; i < number_of_security_updates; i++) {
193 xasprintf(&sc_security_updates.output, "%s\n%s (security)", sc_security_updates.output,
194 secpackages_list[i]);
160 } 195 }
161 196
162 if (only_critical == false) { 197 if (!config.only_critical) {
163 for (int i = 0; i < packages_available - sec_count; i++) { 198 for (size_t i = 0; i < packages_available - number_of_security_updates; i++) {
164 printf("%s\n", packages_list[i]); 199 xasprintf(&sc_other_updates.output, "%s\n%s", sc_other_updates.output,
200 packages_list[i]);
165 } 201 }
166 } 202 }
167 } 203 }
204 mp_add_subcheck_to_check(&overall, sc_security_updates);
205 mp_add_subcheck_to_check(&overall, sc_other_updates);
168 206
169 return result; 207 mp_exit(overall);
170} 208}
171 209
172/* process command-line arguments */ 210/* process command-line arguments */
173int 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 };
174 static struct option longopts[] = {{"version", no_argument, 0, 'V'}, 217 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
175 {"help", no_argument, 0, 'h'}, 218 {"help", no_argument, 0, 'h'},
176 {"verbose", no_argument, 0, 'v'}, 219 {"verbose", no_argument, 0, 'v'},
@@ -179,15 +222,21 @@ int process_arguments(int argc, char **argv) {
179 {"upgrade", optional_argument, 0, 'U'}, 222 {"upgrade", optional_argument, 0, 'U'},
180 {"no-upgrade", no_argument, 0, 'n'}, 223 {"no-upgrade", no_argument, 0, 'n'},
181 {"dist-upgrade", optional_argument, 0, 'd'}, 224 {"dist-upgrade", optional_argument, 0, 'd'},
182 {"list", no_argument, false, 'l'}, 225 {"list", no_argument, 0, 'l'},
183 {"include", required_argument, 0, 'i'}, 226 {"include", required_argument, 0, 'i'},
184 {"exclude", required_argument, 0, 'e'}, 227 {"exclude", required_argument, 0, 'e'},
185 {"critical", required_argument, 0, 'c'}, 228 {"critical", required_argument, 0, 'c'},
186 {"only-critical", no_argument, 0, 'o'}, 229 {"only-critical", no_argument, 0, 'o'},
187 {"input-file", required_argument, 0, INPUT_FILE_OPT}, 230 {"input-file", required_argument, 0, INPUT_FILE_OPT},
188 {"packages-warning", required_argument, 0, 'w'}, 231 {"packages-warning", required_argument, 0, 'w'},
232 {"output-format", required_argument, 0, output_format_index},
189 {0, 0, 0, 0}}; 233 {0, 0, 0, 0}};
190 234
235 check_apt_config_wrapper result = {
236 .errorcode = OK,
237 .config = check_apt_config_init(),
238 };
239
191 while (true) { 240 while (true) {
192 int option_char = getopt_long(argc, argv, "hVvt:u::U::d::nli:e:c:ow:", longopts, NULL); 241 int option_char = getopt_long(argc, argv, "hVvt:u::U::d::nli:e:c:ow:", longopts, NULL);
193 242
@@ -209,96 +258,110 @@ int process_arguments(int argc, char **argv) {
209 timeout_interval = atoi(optarg); 258 timeout_interval = atoi(optarg);
210 break; 259 break;
211 case 'd': 260 case 'd':
212 upgrade = DIST_UPGRADE; 261 result.config.upgrade = DIST_UPGRADE;
213 if (optarg != NULL) { 262 if (optarg != NULL) {
214 upgrade_opts = strdup(optarg); 263 result.config.upgrade_opts = strdup(optarg);
215 if (upgrade_opts == NULL) { 264 if (result.config.upgrade_opts == NULL) {
216 die(STATE_UNKNOWN, "strdup failed"); 265 die(STATE_UNKNOWN, "strdup failed");
217 } 266 }
218 } 267 }
219 break; 268 break;
220 case 'U': 269 case 'U':
221 upgrade = UPGRADE; 270 result.config.upgrade = UPGRADE;
222 if (optarg != NULL) { 271 if (optarg != NULL) {
223 upgrade_opts = strdup(optarg); 272 result.config.upgrade_opts = strdup(optarg);
224 if (upgrade_opts == NULL) { 273 if (result.config.upgrade_opts == NULL) {
225 die(STATE_UNKNOWN, "strdup failed"); 274 die(STATE_UNKNOWN, "strdup failed");
226 } 275 }
227 } 276 }
228 break; 277 break;
229 case 'n': 278 case 'n':
230 upgrade = NO_UPGRADE; 279 result.config.upgrade = NO_UPGRADE;
231 break; 280 break;
232 case 'u': 281 case 'u':
233 do_update = true; 282 result.config.do_update = true;
234 if (optarg != NULL) { 283 if (optarg != NULL) {
235 update_opts = strdup(optarg); 284 result.config.update_opts = strdup(optarg);
236 if (update_opts == NULL) { 285 if (result.config.update_opts == NULL) {
237 die(STATE_UNKNOWN, "strdup failed"); 286 die(STATE_UNKNOWN, "strdup failed");
238 } 287 }
239 } 288 }
240 break; 289 break;
241 case 'l': 290 case 'l':
242 list = true; 291 result.config.list = true;
243 break; 292 break;
244 case 'i': 293 case 'i':
245 do_include = add_to_regexp(do_include, optarg); 294 result.config.do_include = add_to_regexp(result.config.do_include, optarg);
246 break; 295 break;
247 case 'e': 296 case 'e':
248 do_exclude = add_to_regexp(do_exclude, optarg); 297 result.config.do_exclude = add_to_regexp(result.config.do_exclude, optarg);
249 break; 298 break;
250 case 'c': 299 case 'c':
251 do_critical = add_to_regexp(do_critical, optarg); 300 result.config.do_critical = add_to_regexp(result.config.do_critical, optarg);
252 break; 301 break;
253 case 'o': 302 case 'o':
254 only_critical = true; 303 result.config.only_critical = true;
255 break; 304 break;
256 case INPUT_FILE_OPT: 305 case INPUT_FILE_OPT:
257 input_filename = optarg; 306 result.config.input_filename = optarg;
258 break; 307 break;
259 case 'w': 308 case 'w':
260 packages_warning = atoi(optarg); 309 result.config.packages_warning = atoi(optarg);
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;
261 break; 321 break;
322 }
262 default: 323 default:
263 /* print short usage statement if args not parsable */ 324 /* print short usage statement if args not parsable */
264 usage5(); 325 usage5();
265 } 326 }
266 } 327 }
267 328
268 return OK; 329 return result;
269} 330}
270 331
271/* run an apt-get upgrade */ 332/* run an apt-get upgrade */
272run_upgrade_result run_upgrade(void) { 333run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_include,
273 regex_t ereg; 334 const char *do_exclude, const char *do_critical,
335 const char *upgrade_opts, const char *input_filename) {
336 regex_t exclude_regex;
274 /* initialize ereg as it is possible it is printed while uninitialized */ 337 /* initialize ereg as it is possible it is printed while uninitialized */
275 memset(&ereg, '\0', sizeof(ereg.buffer)); 338 memset(&exclude_regex, '\0', sizeof(exclude_regex.buffer));
276 339
277 run_upgrade_result result = { 340 run_upgrade_result result = {
278 .errorcode = STATE_UNKNOWN, 341 .errorcode = OK,
279 }; 342 };
280 343
281 if (upgrade == NO_UPGRADE) { 344 if (upgrade == NO_UPGRADE) {
282 result.errorcode = STATE_OK; 345 result.errorcode = OK;
283 return result; 346 return result;
284 } 347 }
285 348
286 int regres = 0; 349 int regres = 0;
287 regex_t ireg; 350 regex_t include_regex;
288 char rerrbuf[64]; 351 char rerrbuf[64];
289 /* compile the regexps */ 352 /* compile the regexps */
290 if (do_include != NULL) { 353 if (do_include != NULL) {
291 regres = regcomp(&ireg, do_include, REG_EXTENDED); 354 regres = regcomp(&include_regex, do_include, REG_EXTENDED);
292 if (regres != 0) { 355 if (regres != 0) {
293 regerror(regres, &ireg, rerrbuf, 64); 356 regerror(regres, &include_regex, rerrbuf, 64);
294 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 357 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
295 } 358 }
296 } 359 }
297 360
298 if (do_exclude != NULL) { 361 if (do_exclude != NULL) {
299 regres = regcomp(&ereg, do_exclude, REG_EXTENDED); 362 regres = regcomp(&exclude_regex, do_exclude, REG_EXTENDED);
300 if (regres != 0) { 363 if (regres != 0) {
301 regerror(regres, &ereg, rerrbuf, 64); 364 regerror(regres, &exclude_regex, rerrbuf, 64);
302 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 365 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
303 } 366 }
304 } 367 }
@@ -307,12 +370,12 @@ run_upgrade_result run_upgrade(void) {
307 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE; 370 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE;
308 regres = regcomp(&sreg, crit_ptr, REG_EXTENDED); 371 regres = regcomp(&sreg, crit_ptr, REG_EXTENDED);
309 if (regres != 0) { 372 if (regres != 0) {
310 regerror(regres, &ereg, rerrbuf, 64); 373 regerror(regres, &exclude_regex, rerrbuf, 64);
311 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 374 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
312 } 375 }
313 376
314 struct output chld_out; 377 output chld_out;
315 struct output chld_err; 378 output chld_err;
316 char *cmdline = NULL; 379 char *cmdline = NULL;
317 cmdline = construct_cmdline(upgrade, upgrade_opts); 380 cmdline = construct_cmdline(upgrade, upgrade_opts);
318 if (input_filename != NULL) { 381 if (input_filename != NULL) {
@@ -323,16 +386,15 @@ run_upgrade_result run_upgrade(void) {
323 result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0); 386 result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0);
324 } 387 }
325 388
326 /* apt-get upgrade only changes exit status if there is an 389 // apt-get upgrade only changes exit status if there is an
327 * internal error when run in dry-run mode. therefore we will 390 // internal error when run in dry-run mode.
328 * treat such an error as UNKNOWN */ 391 if (result.errorcode != 0) {
329 if (result.errorcode != STATE_OK) { 392 result.exec_warning = true;
330 exec_warning = 1; 393 result.errorcode = ERROR;
331 result.errorcode = STATE_UNKNOWN; 394 // fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline);
332 fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline);
333 } 395 }
334 396
335 char **pkglist = malloc(sizeof(char *) * chld_out.lines); 397 char **pkglist = malloc(sizeof(char *) * chld_out.lines);
336 if (!pkglist) { 398 if (!pkglist) {
337 die(STATE_UNKNOWN, "malloc failed!\n"); 399 die(STATE_UNKNOWN, "malloc failed!\n");
338 } 400 }
@@ -350,27 +412,31 @@ run_upgrade_result run_upgrade(void) {
350 * we may need to switch to the --print-uris output format, 412 * we may need to switch to the --print-uris output format,
351 * in which case the logic here will slightly change. 413 * in which case the logic here will slightly change.
352 */ 414 */
353 int package_counter = 0; 415 size_t package_counter = 0;
354 int security_package_counter = 0; 416 size_t security_package_counter = 0;
355 for (size_t i = 0; i < chld_out.lines; i++) { 417 for (size_t i = 0; i < chld_out.lines; i++) {
356 if (verbose) { 418 if (verbose) {
357 printf("%s\n", chld_out.line[i]); 419 printf("%s\n", chld_out.line[i]);
358 } 420 }
421
359 /* if it is a package we care about */ 422 /* if it is a package we care about */
360 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 &&
361 (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)) {
362 /* if we're not excluding, or it's not in the 425 /* if we're not excluding, or it's not in the
363 * list of stuff to exclude */ 426 * list of stuff to exclude */
364 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) {
365 package_counter++; 428 package_counter++;
366 if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) { 429 if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) {
367 security_package_counter++; 430 security_package_counter++;
431
368 if (verbose) { 432 if (verbose) {
369 printf("*"); 433 printf("*");
370 } 434 }
435
371 (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]); 436 (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]);
372 } else { 437 } else {
373 (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]);
374 } 440 }
375 if (verbose) { 441 if (verbose) {
376 printf("*%s\n", chld_out.line[i]); 442 printf("*%s\n", chld_out.line[i]);
@@ -378,6 +444,7 @@ run_upgrade_result run_upgrade(void) {
378 } 444 }
379 } 445 }
380 } 446 }
447
381 result.package_count = package_counter; 448 result.package_count = package_counter;
382 result.security_package_count = security_package_counter; 449 result.security_package_count = security_package_counter;
383 result.packages_list = pkglist; 450 result.packages_list = pkglist;
@@ -385,42 +452,56 @@ run_upgrade_result run_upgrade(void) {
385 452
386 /* If we get anything on stderr, at least set warning */ 453 /* If we get anything on stderr, at least set warning */
387 if (input_filename == NULL && chld_err.buflen) { 454 if (input_filename == NULL && chld_err.buflen) {
388 stderr_warning = 1; 455 stderr_warning = true;
389 result.errorcode = max_state(result.errorcode, STATE_WARNING); 456 result.errorcode = ERROR;
457
390 if (verbose) { 458 if (verbose) {
391 for (size_t i = 0; i < chld_err.lines; i++) { 459 for (size_t i = 0; i < chld_err.lines; i++) {
392 fprintf(stderr, "%s\n", chld_err.line[i]); 460 fprintf(stderr, "%s\n", chld_err.line[i]);
393 } 461 }
394 } 462 }
395 } 463 }
464
396 if (do_include != NULL) { 465 if (do_include != NULL) {
397 regfree(&ireg); 466 regfree(&include_regex);
398 } 467 }
468
399 regfree(&sreg); 469 regfree(&sreg);
470
400 if (do_exclude != NULL) { 471 if (do_exclude != NULL) {
401 regfree(&ereg); 472 regfree(&exclude_regex);
402 } 473 }
474
403 free(cmdline); 475 free(cmdline);
476
404 return result; 477 return result;
405} 478}
406 479
407/* run an apt-get update (needs root) */ 480/* run an apt-get update (needs root) */
408int run_update(void) { 481run_update_result run_update(char *update_opts) {
409 int result = STATE_UNKNOWN;
410 char *cmdline; 482 char *cmdline;
411 /* run the update */ 483 /* run the update */
412 cmdline = construct_cmdline(NO_UPGRADE, update_opts); 484 cmdline = construct_cmdline(NO_UPGRADE, update_opts);
413 485
414 struct output chld_out; 486 run_update_result result = {
415 struct output chld_err; 487 .exec_warning = false,
416 result = np_runcmd(cmdline, &chld_out, &chld_err, 0); 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
495 output chld_out;
496 output chld_err;
497 int cmd_error = np_runcmd(cmdline, &chld_out, &chld_err, 0);
417 /* 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.
418 * 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
419 * a critical error. */ 500 * a critical error. */
420 if (result != 0) { 501 if (cmd_error != 0) {
421 exec_warning = 1; 502 exec_warning = true;
422 result = STATE_CRITICAL; 503 result.sc = mp_set_subcheck_state(result.sc, STATE_CRITICAL);
423 fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline); 504 xasprintf(&result.sc.output, _("'%s' exited with non-zero status.\n"), cmdline);
424 } 505 }
425 506
426 if (verbose) { 507 if (verbose) {
@@ -431,22 +512,25 @@ int run_update(void) {
431 512
432 /* If we get anything on stderr, at least set warning */ 513 /* If we get anything on stderr, at least set warning */
433 if (chld_err.buflen) { 514 if (chld_err.buflen) {
434 stderr_warning = 1; 515 stderr_warning = true;
435 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));
436 if (verbose) { 518 if (verbose) {
437 for (size_t i = 0; i < chld_err.lines; i++) { 519 for (size_t i = 0; i < chld_err.lines; i++) {
438 fprintf(stderr, "%s\n", chld_err.line[i]); 520 fprintf(stderr, "%s\n", chld_err.line[i]);
439 } 521 }
440 } 522 }
441 } 523 }
524
442 free(cmdline); 525 free(cmdline);
526
443 return result; 527 return result;
444} 528}
445 529
446char *pkg_name(char *line) { 530char *pkg_name(char *line) {
447 char *start = line + strlen(PKGINST_PREFIX); 531 char *start = line + strlen(PKGINST_PREFIX);
448 532
449 int len = strlen(start); 533 size_t len = strlen(start);
450 534
451 char *space = index(start, ' '); 535 char *space = index(start, ' ');
452 if (space != NULL) { 536 if (space != NULL) {
@@ -464,35 +548,37 @@ char *pkg_name(char *line) {
464 return pkg; 548 return pkg;
465} 549}
466 550
467int cmpstringp(const void *p1, const void *p2) { return strcmp(*(char *const *)p1, *(char *const *)p2); } 551int cmpstringp(const void *left_string, const void *right_string) {
552 return strcmp(*(char *const *)left_string, *(char *const *)right_string);
553}
468 554
469char *add_to_regexp(char *expr, const char *next) { 555char *add_to_regexp(char *expr, const char *next) {
470 char *re = NULL; 556 char *regex_string = NULL;
471 557
472 if (expr == NULL) { 558 if (expr == NULL) {
473 re = malloc(sizeof(char) * (strlen("()") + strlen(next) + 1)); 559 regex_string = malloc(sizeof(char) * (strlen("()") + strlen(next) + 1));
474 if (!re) { 560 if (!regex_string) {
475 die(STATE_UNKNOWN, "malloc failed!\n"); 561 die(STATE_UNKNOWN, "malloc failed!\n");
476 } 562 }
477 sprintf(re, "(%s)", next); 563 sprintf(regex_string, "(%s)", next);
478 } else { 564 } else {
479 /* resize it, adding an extra char for the new '|' separator */ 565 /* resize it, adding an extra char for the new '|' separator */
480 re = realloc(expr, sizeof(char) * (strlen(expr) + 1 + strlen(next) + 1)); 566 regex_string = realloc(expr, sizeof(char) * (strlen(expr) + 1 + strlen(next) + 1));
481 if (!re) { 567 if (!regex_string) {
482 die(STATE_UNKNOWN, "realloc failed!\n"); 568 die(STATE_UNKNOWN, "realloc failed!\n");
483 } 569 }
484 /* append it starting at ')' in the old re */ 570 /* append it starting at ')' in the old re */
485 sprintf((char *)(re + strlen(re) - 1), "|%s)", next); 571 sprintf((char *)(regex_string + strlen(regex_string) - 1), "|%s)", next);
486 } 572 }
487 573
488 return re; 574 return regex_string;
489} 575}
490 576
491char *construct_cmdline(upgrade_type u, const char *opts) { 577char *construct_cmdline(upgrade_type upgrade, const char *opts) {
492 const char *opts_ptr = NULL; 578 const char *opts_ptr = NULL;
493 const char *aptcmd = NULL; 579 const char *aptcmd = NULL;
494 580
495 switch (u) { 581 switch (upgrade) {
496 case UPGRADE: 582 case UPGRADE:
497 if (opts == NULL) { 583 if (opts == NULL) {
498 opts_ptr = UPGRADE_DEFAULT_OPTS; 584 opts_ptr = UPGRADE_DEFAULT_OPTS;
@@ -519,7 +605,7 @@ char *construct_cmdline(upgrade_type u, const char *opts) {
519 break; 605 break;
520 } 606 }
521 607
522 int len = 0; 608 size_t len = 0;
523 len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */ 609 len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */
524 len += strlen(opts_ptr) + 1; /* "opts " */ 610 len += strlen(opts_ptr) + 1; /* "opts " */
525 len += strlen(aptcmd) + 1; /* "upgrade\0" */ 611 len += strlen(aptcmd) + 1; /* "upgrade\0" */
@@ -557,7 +643,8 @@ void print_help(void) {
557 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"));
558 printf(" %s\n", _("name with security packages listed first.")); 644 printf(" %s\n", _("name with security packages listed first."));
559 printf(" %s\n", "-i, --include=REGEXP"); 645 printf(" %s\n", "-i, --include=REGEXP");
560 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"));
561 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"));
562 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."));
563 printf(" %s\n", _("Default is to include all packages.")); 650 printf(" %s\n", _("Default is to include all packages."));
@@ -566,7 +653,8 @@ void print_help(void) {
566 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"));
567 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."));
568 printf(" %s\n", "-c, --critical=REGEXP"); 655 printf(" %s\n", "-c, --critical=REGEXP");
569 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"));
570 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"));
571 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"));
572 printf(" %s\n", _("upgrades for Debian and Ubuntu:")); 660 printf(" %s\n", _("upgrades for Debian and Ubuntu:"));
@@ -575,15 +663,21 @@ void print_help(void) {
575 printf(" %s\n", _("information is compared against the critical list.")); 663 printf(" %s\n", _("information is compared against the critical list."));
576 printf(" %s\n", "-o, --only-critical"); 664 printf(" %s\n", "-o, --only-critical");
577 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"));
578 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"));
579 printf(" %s\n", _("the plugin to return WARNING status.")); 668 printf(" %s\n", _("the plugin to return WARNING status."));
580 printf(" %s\n", "-w, --packages-warning"); 669 printf(" %s\n", "-w, --packages-warning");
581 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."));
582 printf(" %s\n\n", _("Default is 1 package.")); 672 printf(" %s\n\n", _("Default is 1 package."));
583 673
584 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:"));
585 printf(" %s\n", "-u, --update=OPTS"); 678 printf(" %s\n", "-u, --update=OPTS");
586 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"));
587 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"));
588 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"));
589 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."));
@@ -592,8 +686,10 @@ void print_help(void) {
592 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"));
593 printf(" %s", _("default ")); 687 printf(" %s", _("default "));
594 printf("(%s).\n", UPGRADE_DEFAULT_OPTS); 688 printf("(%s).\n", UPGRADE_DEFAULT_OPTS);
595 printf(" %s\n", _("Note that you may be required to have root privileges if you do not use")); 689 printf(" %s\n",
596 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"));
597 printf(" %s\n", "-d, --dist-upgrade=OPTS"); 693 printf(" %s\n", "-d, --dist-upgrade=OPTS");
598 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"));
599 printf(" %s\n", _("can be provided to override the default options.")); 695 printf(" %s\n", _("can be provided to override the default options."));