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.c824
1 files changed, 483 insertions, 341 deletions
diff --git a/plugins/check_apt.c b/plugins/check_apt.c
index 5c0f6e28..9ed5b6cf 100644
--- a/plugins/check_apt.c
+++ b/plugins/check_apt.c
@@ -1,188 +1,250 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* Monitoring check_apt plugin 3 * Monitoring check_apt plugin
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2006-2008 Monitoring Plugins Development Team 6 * Copyright (c) 2006-2024 Monitoring Plugins Development Team
7* 7 *
8* Original author: Sean Finney 8 * Original author: Sean Finney
9* 9 *
10* Description: 10 * Description:
11* 11 *
12* This file contains the check_apt plugin 12 * This file contains the check_apt plugin
13* 13 *
14* Check for available updates in apt package management systems 14 * Check for available updates in apt package management systems
15* 15 *
16* 16 *
17* This program is free software: you can redistribute it and/or modify 17 * This program is free software: you can redistribute it and/or modify
18* it under the terms of the GNU General Public License as published by 18 * it under the terms of the GNU General Public License as published by
19* the Free Software Foundation, either version 3 of the License, or 19 * the Free Software Foundation, either version 3 of the License, or
20* (at your option) any later version. 20 * (at your option) any later version.
21* 21 *
22* This program is distributed in the hope that it will be useful, 22 * This program is distributed in the hope that it will be useful,
23* but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25* GNU General Public License for more details. 25 * GNU General Public License for more details.
26* 26 *
27* You should have received a copy of the GNU General Public License 27 * You should have received a copy of the GNU General Public License
28* along with this program. If not, see <http://www.gnu.org/licenses/>. 28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29* 29 *
30*****************************************************************************/ 30 *****************************************************************************/
31 31
32#include "perfdata.h"
32const char *progname = "check_apt"; 33const char *progname = "check_apt";
33const char *copyright = "2006-2008"; 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 { UPGRADE, DIST_UPGRADE, NO_UPGRADE } upgrade_type;
43
44/* Character for hidden input file option (for testing). */
45#define INPUT_FILE_OPT CHAR_MAX+1
46/* the default opts can be overridden via the cmdline */ 45/* the default opts can be overridden via the cmdline */
47#define UPGRADE_DEFAULT_OPTS "-o 'Debug::NoLocking=true' -s -qq" 46const char *UPGRADE_DEFAULT_OPTS = "-o 'Debug::NoLocking=true' -s -qq";
48#define UPDATE_DEFAULT_OPTS "-q" 47const char *UPDATE_DEFAULT_OPTS = "-q";
48
49/* 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
50 * it here as well */ 50 * it here as well */
51#ifndef PATH_TO_APTGET 51#ifndef PATH_TO_APTGET
52# define PATH_TO_APTGET "/usr/bin/apt-get" 52# define PATH_TO_APTGET "/usr/bin/apt-get"
53#endif /* PATH_TO_APTGET */ 53#endif /* PATH_TO_APTGET */
54
54/* 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 */
55#define PKGINST_PREFIX "Inst " 56const char *PKGINST_PREFIX = "Inst ";
56/* the RE that catches security updates */ 57/* the RE that catches security updates */
57#define SECURITY_RE "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)" 58const char *SECURITY_RE = "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)";
58 59
59/* some standard functions */ 60/* some standard functions */
60int process_arguments(int, char **); 61typedef struct {
61void print_help(void); 62 int errorcode;
63 check_apt_config config;
64} check_apt_config_wrapper;
65static check_apt_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
66static void print_help(void);
62void print_usage(void); 67void print_usage(void);
63 68
64/* construct the appropriate apt-get cmdline */ 69/* construct the appropriate apt-get cmdline */
65char* construct_cmdline(upgrade_type u, const char *opts); 70static char *construct_cmdline(upgrade_type /*u*/, const char * /*opts*/);
71
66/* run an apt-get update */ 72/* run an apt-get update */
67int 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);
79
80typedef struct {
81 int errorcode;
82 size_t package_count;
83 size_t security_package_count;
84 char **packages_list;
85 char **secpackages_list;
86 bool exec_warning;
87} run_upgrade_result;
88
68/* run an apt-get upgrade */ 89/* run an apt-get upgrade */
69int run_upgrade(int *pkgcount, int *secpkgcount, char ***pkglist, char ***secpkglist); 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);
93
70/* add another clause to a regexp */ 94/* add another clause to a regexp */
71char* add_to_regexp(char *expr, const char *next); 95static char *add_to_regexp(char * /*expr*/, const char * /*next*/);
72/* extract package name from Inst line */ 96/* extract package name from Inst line */
73char* pkg_name(char *line); 97static char *pkg_name(char * /*line*/);
74/* string comparison function for qsort */ 98/* string comparison function for qsort */
75int cmpstringp(const void *p1, const void *p2); 99static int cmpstringp(const void * /*p1*/, const void * /*p2*/);
76 100
77/* configuration variables */ 101/* configuration variables */
78static int verbose = 0; /* -v */ 102static int verbose = 0; /* -v */
79static bool list = false; /* list packages available for upgrade */
80static bool do_update = false; /* whether to call apt-get update */
81static bool only_critical = false; /* whether to warn about non-critical updates */
82static upgrade_type upgrade = UPGRADE; /* which type of upgrade to do */
83static char *upgrade_opts = NULL; /* options to override defaults for upgrade */
84static char *update_opts = NULL; /* options to override defaults for update */
85static char *do_include = NULL; /* regexp to only include certain packages */
86static char *do_exclude = NULL; /* regexp to only exclude certain packages */
87static char *do_critical = NULL; /* regexp specifying critical packages */
88static char *input_filename = NULL; /* input filename for testing */
89/* number of packages available for upgrade to return WARNING status */
90static int packages_warning = 1;
91 103
92/* other global variables */ 104/* other global variables */
93static int stderr_warning = 0; /* if a cmd issued output on stderr */ 105static bool stderr_warning = false; /* if a cmd issued output on stderr */
94static int exec_warning = 0; /* if a cmd exited non-zero */ 106static bool exec_warning = false; /* if a cmd exited non-zero */
95
96int main (int argc, char **argv) {
97 int result=STATE_UNKNOWN, packages_available=0, sec_count=0;
98 char **packages_list=NULL, **secpackages_list=NULL;
99 107
108int main(int argc, char **argv) {
100 /* Parse extra opts if any */ 109 /* Parse extra opts if any */
101 argv=np_extra_opts(&argc, argv, progname); 110 argv = np_extra_opts(&argc, argv, progname);
102 111
103 if (process_arguments(argc, argv) == ERROR) 112 check_apt_config_wrapper tmp_config = process_arguments(argc, argv);
113
114 if (tmp_config.errorcode == ERROR) {
104 usage_va(_("Could not parse arguments")); 115 usage_va(_("Could not parse arguments"));
116 }
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 }
105 123
106 /* Set signal handling and alarm timeout */ 124 /* Set signal handling and alarm timeout */
107 if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { 125 if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) {
108 usage_va(_("Cannot catch SIGALRM")); 126 usage_va(_("Cannot catch SIGALRM"));
109 } 127 }
110 128
111 /* handle timeouts gracefully... */ 129 /* handle timeouts gracefully... */
112 alarm (timeout_interval); 130 alarm(timeout_interval);
113 131
132 mp_check overall = mp_check_init();
114 /* if they want to run apt-get update first... */ 133 /* if they want to run apt-get update first... */
115 if(do_update) result = run_update(); 134 if (config.do_update) {
135 run_update_result update_result = run_update(config.update_opts);
136
137 mp_add_subcheck_to_check(&overall, update_result.sc);
138 }
116 139
117 /* apt-get upgrade */ 140 /* apt-get upgrade */
118 result = max_state(result, run_upgrade(&packages_available, &sec_count, &packages_list, &secpackages_list)); 141 run_upgrade_result upgrad_res =
119 142 run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical,
120 if(sec_count > 0){ 143 config.upgrade_opts, config.input_filename);
121 result = max_state(result, STATE_CRITICAL); 144
122 } else if(packages_available >= packages_warning && only_critical == false){ 145 mp_subcheck sc_run_upgrade = mp_subcheck_init();
123 result = max_state(result, STATE_WARNING); 146 if (upgrad_res.errorcode == OK) {
124 } else if(result > STATE_UNKNOWN){ 147 sc_run_upgrade = mp_set_subcheck_state(sc_run_upgrade, STATE_OK);
125 result = STATE_UNKNOWN; 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;
155 char **packages_list = upgrad_res.packages_list;
156 char **secpackages_list = upgrad_res.secpackages_list;
157
158 mp_perfdata pd_security_updates = perfdata_init();
159 pd_security_updates.value = mp_create_pd_value(number_of_security_updates);
160 pd_security_updates.label = "critical_updates";
161
162 mp_subcheck sc_security_updates = mp_subcheck_init();
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);
171 }
172
173 mp_perfdata pd_other_updates = perfdata_init();
174 pd_other_updates.value = mp_create_pd_value(packages_available);
175 pd_other_updates.label = "available_upgrades";
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);
126 } 185 }
127 186
128 printf(_("APT %s: %d packages available for %s (%d critical updates). %s%s%s%s|available_upgrades=%d;;;0 critical_updates=%d;;;0\n"), 187 if (config.list) {
129 state_text(result), 188 qsort(secpackages_list, number_of_security_updates, sizeof(char *), cmpstringp);
130 packages_available, 189 qsort(packages_list, packages_available - number_of_security_updates, sizeof(char *),
131 (upgrade==DIST_UPGRADE)?"dist-upgrade":"upgrade", 190 cmpstringp);
132 sec_count, 191
133 (stderr_warning)?" warnings detected":"", 192 for (size_t i = 0; i < number_of_security_updates; i++) {
134 (stderr_warning && exec_warning)?",":"", 193 xasprintf(&sc_security_updates.output, "%s\n%s (security)", sc_security_updates.output,
135 (exec_warning)?" errors detected":"", 194 secpackages_list[i]);
136 (stderr_warning||exec_warning)?".":"", 195 }
137 packages_available, 196
138 sec_count 197 if (!config.only_critical) {
139 ); 198 for (size_t i = 0; i < packages_available - number_of_security_updates; i++) {
140 199 xasprintf(&sc_other_updates.output, "%s\n%s", sc_other_updates.output,
141 if(list) { 200 packages_list[i]);
142 qsort(secpackages_list, sec_count, sizeof(char*), cmpstringp); 201 }
143 qsort(packages_list, packages_available-sec_count, sizeof(char*), cmpstringp);
144
145 for(int i = 0; i < sec_count; i++)
146 printf("%s (security)\n", secpackages_list[i]);
147
148 if (only_critical == false) {
149 for(int i = 0; i < packages_available - sec_count; i++)
150 printf("%s\n", packages_list[i]);
151 } 202 }
152 } 203 }
204 mp_add_subcheck_to_check(&overall, sc_security_updates);
205 mp_add_subcheck_to_check(&overall, sc_other_updates);
153 206
154 return result; 207 mp_exit(overall);
155} 208}
156 209
157/* process command-line arguments */ 210/* process command-line arguments */
158int process_arguments (int argc, char **argv) { 211check_apt_config_wrapper process_arguments(int argc, char **argv) {
159 int c; 212 enum {
160 213 /* Character for hidden input file option (for testing). */
161 static struct option longopts[] = { 214 INPUT_FILE_OPT = CHAR_MAX + 1,
162 {"version", no_argument, 0, 'V'}, 215 output_format_index,
163 {"help", no_argument, 0, 'h'}, 216 };
164 {"verbose", no_argument, 0, 'v'}, 217 static struct option longopts[] = {{"version", no_argument, 0, 'V'},
165 {"timeout", required_argument, 0, 't'}, 218 {"help", no_argument, 0, 'h'},
166 {"update", optional_argument, 0, 'u'}, 219 {"verbose", no_argument, 0, 'v'},
167 {"upgrade", optional_argument, 0, 'U'}, 220 {"timeout", required_argument, 0, 't'},
168 {"no-upgrade", no_argument, 0, 'n'}, 221 {"update", optional_argument, 0, 'u'},
169 {"dist-upgrade", optional_argument, 0, 'd'}, 222 {"upgrade", optional_argument, 0, 'U'},
170 {"list", no_argument, false, 'l'}, 223 {"no-upgrade", no_argument, 0, 'n'},
171 {"include", required_argument, 0, 'i'}, 224 {"dist-upgrade", optional_argument, 0, 'd'},
172 {"exclude", required_argument, 0, 'e'}, 225 {"list", no_argument, 0, 'l'},
173 {"critical", required_argument, 0, 'c'}, 226 {"include", required_argument, 0, 'i'},
174 {"only-critical", no_argument, 0, 'o'}, 227 {"exclude", required_argument, 0, 'e'},
175 {"input-file", required_argument, 0, INPUT_FILE_OPT}, 228 {"critical", required_argument, 0, 'c'},
176 {"packages-warning", required_argument, 0, 'w'}, 229 {"only-critical", no_argument, 0, 'o'},
177 {0, 0, 0, 0} 230 {"input-file", required_argument, 0, INPUT_FILE_OPT},
231 {"packages-warning", required_argument, 0, 'w'},
232 {"output-format", required_argument, 0, output_format_index},
233 {0, 0, 0, 0}};
234
235 check_apt_config_wrapper result = {
236 .errorcode = OK,
237 .config = check_apt_config_init(),
178 }; 238 };
179 239
180 while(1) { 240 while (true) {
181 c = 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);
182 242
183 if(c == -1 || c == EOF || c == 1) break; 243 if (option_char == -1 || option_char == EOF || option_char == 1) {
244 break;
245 }
184 246
185 switch(c) { 247 switch (option_char) {
186 case 'h': 248 case 'h':
187 print_help(); 249 print_help();
188 exit(STATE_UNKNOWN); 250 exit(STATE_UNKNOWN);
@@ -193,124 +255,153 @@ int process_arguments (int argc, char **argv) {
193 verbose++; 255 verbose++;
194 break; 256 break;
195 case 't': 257 case 't':
196 timeout_interval=atoi(optarg); 258 timeout_interval = atoi(optarg);
197 break; 259 break;
198 case 'd': 260 case 'd':
199 upgrade=DIST_UPGRADE; 261 result.config.upgrade = DIST_UPGRADE;
200 if(optarg!=NULL){ 262 if (optarg != NULL) {
201 upgrade_opts=strdup(optarg); 263 result.config.upgrade_opts = strdup(optarg);
202 if(upgrade_opts==NULL) die(STATE_UNKNOWN, "strdup failed"); 264 if (result.config.upgrade_opts == NULL) {
265 die(STATE_UNKNOWN, "strdup failed");
266 }
203 } 267 }
204 break; 268 break;
205 case 'U': 269 case 'U':
206 upgrade=UPGRADE; 270 result.config.upgrade = UPGRADE;
207 if(optarg!=NULL){ 271 if (optarg != NULL) {
208 upgrade_opts=strdup(optarg); 272 result.config.upgrade_opts = strdup(optarg);
209 if(upgrade_opts==NULL) die(STATE_UNKNOWN, "strdup failed"); 273 if (result.config.upgrade_opts == NULL) {
274 die(STATE_UNKNOWN, "strdup failed");
275 }
210 } 276 }
211 break; 277 break;
212 case 'n': 278 case 'n':
213 upgrade=NO_UPGRADE; 279 result.config.upgrade = NO_UPGRADE;
214 break; 280 break;
215 case 'u': 281 case 'u':
216 do_update=true; 282 result.config.do_update = true;
217 if(optarg!=NULL){ 283 if (optarg != NULL) {
218 update_opts=strdup(optarg); 284 result.config.update_opts = strdup(optarg);
219 if(update_opts==NULL) die(STATE_UNKNOWN, "strdup failed"); 285 if (result.config.update_opts == NULL) {
286 die(STATE_UNKNOWN, "strdup failed");
287 }
220 } 288 }
221 break; 289 break;
222 case 'l': 290 case 'l':
223 list=true; 291 result.config.list = true;
224 break; 292 break;
225 case 'i': 293 case 'i':
226 do_include=add_to_regexp(do_include, optarg); 294 result.config.do_include = add_to_regexp(result.config.do_include, optarg);
227 break; 295 break;
228 case 'e': 296 case 'e':
229 do_exclude=add_to_regexp(do_exclude, optarg); 297 result.config.do_exclude = add_to_regexp(result.config.do_exclude, optarg);
230 break; 298 break;
231 case 'c': 299 case 'c':
232 do_critical=add_to_regexp(do_critical, optarg); 300 result.config.do_critical = add_to_regexp(result.config.do_critical, optarg);
233 break; 301 break;
234 case 'o': 302 case 'o':
235 only_critical=true; 303 result.config.only_critical = true;
236 break; 304 break;
237 case INPUT_FILE_OPT: 305 case INPUT_FILE_OPT:
238 input_filename = optarg; 306 result.config.input_filename = optarg;
239 break; 307 break;
240 case 'w': 308 case 'w':
241 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;
242 break; 321 break;
322 }
243 default: 323 default:
244 /* print short usage statement if args not parsable */ 324 /* print short usage statement if args not parsable */
245 usage5(); 325 usage5();
246 } 326 }
247 } 327 }
248 328
249 return OK; 329 return result;
250} 330}
251 331
252
253/* run an apt-get upgrade */ 332/* run an apt-get upgrade */
254int run_upgrade(int *pkgcount, int *secpkgcount, char ***pkglist, char ***secpkglist){ 333run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_include,
255 int result=STATE_UNKNOWN, regres=0, pc=0, spc=0; 334 const char *do_exclude, const char *do_critical,
256 struct output chld_out, chld_err; 335 const char *upgrade_opts, const char *input_filename) {
257 regex_t ireg, ereg, sreg; 336 regex_t exclude_regex;
258 char *cmdline=NULL, rerrbuf[64];
259
260 /* initialize ereg as it is possible it is printed while uninitialized */ 337 /* initialize ereg as it is possible it is printed while uninitialized */
261 memset(&ereg, '\0', sizeof(ereg.buffer)); 338 memset(&exclude_regex, '\0', sizeof(exclude_regex.buffer));
262 339
263 if(upgrade==NO_UPGRADE) return STATE_OK; 340 run_upgrade_result result = {
341 .errorcode = OK,
342 };
264 343
344 if (upgrade == NO_UPGRADE) {
345 result.errorcode = OK;
346 return result;
347 }
348
349 int regres = 0;
350 regex_t include_regex;
351 char rerrbuf[64];
265 /* compile the regexps */ 352 /* compile the regexps */
266 if (do_include != NULL) { 353 if (do_include != NULL) {
267 regres=regcomp(&ireg, do_include, REG_EXTENDED); 354 regres = regcomp(&include_regex, do_include, REG_EXTENDED);
268 if (regres!=0) { 355 if (regres != 0) {
269 regerror(regres, &ireg, rerrbuf, 64); 356 regerror(regres, &include_regex, rerrbuf, 64);
270 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); 357 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
271 } 358 }
272 } 359 }
273 360
274 if(do_exclude!=NULL){ 361 if (do_exclude != NULL) {
275 regres=regcomp(&ereg, do_exclude, REG_EXTENDED); 362 regres = regcomp(&exclude_regex, do_exclude, REG_EXTENDED);
276 if(regres!=0) { 363 if (regres != 0) {
277 regerror(regres, &ereg, rerrbuf, 64); 364 regerror(regres, &exclude_regex, rerrbuf, 64);
278 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), 365 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
279 progname, rerrbuf);
280 } 366 }
281 } 367 }
282 368
369 regex_t sreg;
283 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE; 370 const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE;
284 regres=regcomp(&sreg, crit_ptr, REG_EXTENDED); 371 regres = regcomp(&sreg, crit_ptr, REG_EXTENDED);
285 if(regres!=0) { 372 if (regres != 0) {
286 regerror(regres, &ereg, rerrbuf, 64); 373 regerror(regres, &exclude_regex, rerrbuf, 64);
287 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), 374 die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf);
288 progname, rerrbuf);
289 } 375 }
290 376
291 cmdline=construct_cmdline(upgrade, upgrade_opts); 377 output chld_out;
378 output chld_err;
379 char *cmdline = NULL;
380 cmdline = construct_cmdline(upgrade, upgrade_opts);
292 if (input_filename != NULL) { 381 if (input_filename != NULL) {
293 /* read input from a file for testing */ 382 /* read input from a file for testing */
294 result = cmd_file_read(input_filename, &chld_out, 0); 383 result.errorcode = cmd_file_read(input_filename, &chld_out, 0);
295 } else { 384 } else {
296 /* run the upgrade */ 385 /* run the upgrade */
297 result = np_runcmd(cmdline, &chld_out, &chld_err, 0); 386 result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0);
298 } 387 }
299 388
300 /* apt-get upgrade only changes exit status if there is an 389 // apt-get upgrade only changes exit status if there is an
301 * internal error when run in dry-run mode. therefore we will 390 // internal error when run in dry-run mode.
302 * treat such an error as UNKNOWN */ 391 if (result.errorcode != 0) {
303 if(result != 0){ 392 result.exec_warning = true;
304 exec_warning=1; 393 result.errorcode = ERROR;
305 result = STATE_UNKNOWN; 394 // fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline);
306 fprintf(stderr, _("'%s' exited with non-zero status.\n"),
307 cmdline);
308 } 395 }
309 396
310 *pkglist=malloc(sizeof(char *) * chld_out.lines); 397 char **pkglist = malloc(sizeof(char *) * chld_out.lines);
311 if(!pkglist) die(STATE_UNKNOWN, "malloc failed!\n"); 398 if (!pkglist) {
312 *secpkglist=malloc(sizeof(char *) * chld_out.lines); 399 die(STATE_UNKNOWN, "malloc failed!\n");
313 if(!secpkglist) die(STATE_UNKNOWN, "malloc failed!\n"); 400 }
401 char **secpkglist = malloc(sizeof(char *) * chld_out.lines);
402 if (!secpkglist) {
403 die(STATE_UNKNOWN, "malloc failed!\n");
404 }
314 405
315 /* parse the output, which should only consist of lines like 406 /* parse the output, which should only consist of lines like
316 * 407 *
@@ -321,242 +412,293 @@ int run_upgrade(int *pkgcount, int *secpkgcount, char ***pkglist, char ***secpkg
321 * we may need to switch to the --print-uris output format, 412 * we may need to switch to the --print-uris output format,
322 * in which case the logic here will slightly change. 413 * in which case the logic here will slightly change.
323 */ 414 */
324 for(size_t i = 0; i < chld_out.lines; i++) { 415 size_t package_counter = 0;
325 if(verbose){ 416 size_t security_package_counter = 0;
417 for (size_t i = 0; i < chld_out.lines; i++) {
418 if (verbose) {
326 printf("%s\n", chld_out.line[i]); 419 printf("%s\n", chld_out.line[i]);
327 } 420 }
421
328 /* if it is a package we care about */ 422 /* if it is a package we care about */
329 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 &&
330 (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)) {
331 /* if we're not excluding, or it's not in the 425 /* if we're not excluding, or it's not in the
332 * list of stuff to exclude */ 426 * list of stuff to exclude */
333 if(do_exclude==NULL || 427 if (do_exclude == NULL || regexec(&exclude_regex, chld_out.line[i], 0, NULL, 0) != 0) {
334 regexec(&ereg, chld_out.line[i], 0, NULL, 0)!=0){ 428 package_counter++;
335 pc++; 429 if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) {
336 if(regexec(&sreg, chld_out.line[i], 0, NULL, 0)==0){ 430 security_package_counter++;
337 spc++; 431
338 if(verbose) printf("*"); 432 if (verbose) {
339 (*secpkglist)[spc-1] = pkg_name(chld_out.line[i]); 433 printf("*");
434 }
435
436 (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]);
340 } else { 437 } else {
341 (*pkglist)[pc-spc-1] = pkg_name(chld_out.line[i]); 438 (pkglist)[package_counter - security_package_counter - 1] =
439 pkg_name(chld_out.line[i]);
342 } 440 }
343 if(verbose){ 441 if (verbose) {
344 printf("*%s\n", chld_out.line[i]); 442 printf("*%s\n", chld_out.line[i]);
345 } 443 }
346 } 444 }
347 } 445 }
348 } 446 }
349 *pkgcount=pc; 447
350 *secpkgcount=spc; 448 result.package_count = package_counter;
449 result.security_package_count = security_package_counter;
450 result.packages_list = pkglist;
451 result.secpackages_list = secpkglist;
351 452
352 /* If we get anything on stderr, at least set warning */ 453 /* If we get anything on stderr, at least set warning */
353 if (input_filename == NULL && chld_err.buflen) { 454 if (input_filename == NULL && chld_err.buflen) {
354 stderr_warning=1; 455 stderr_warning = true;
355 result = max_state(result, STATE_WARNING); 456 result.errorcode = ERROR;
356 if(verbose){ 457
357 for(size_t i = 0; i < chld_err.lines; i++) { 458 if (verbose) {
459 for (size_t i = 0; i < chld_err.lines; i++) {
358 fprintf(stderr, "%s\n", chld_err.line[i]); 460 fprintf(stderr, "%s\n", chld_err.line[i]);
359 } 461 }
360 } 462 }
361 } 463 }
362 if (do_include != NULL) regfree(&ireg); 464
465 if (do_include != NULL) {
466 regfree(&include_regex);
467 }
468
363 regfree(&sreg); 469 regfree(&sreg);
364 if(do_exclude!=NULL) regfree(&ereg); 470
471 if (do_exclude != NULL) {
472 regfree(&exclude_regex);
473 }
474
365 free(cmdline); 475 free(cmdline);
476
366 return result; 477 return result;
367} 478}
368 479
369/* run an apt-get update (needs root) */ 480/* run an apt-get update (needs root) */
370int run_update(void){ 481run_update_result run_update(char *update_opts) {
371 int result=STATE_UNKNOWN;
372 struct output chld_out, chld_err;
373 char *cmdline; 482 char *cmdline;
374
375 /* run the update */ 483 /* run the update */
376 cmdline = construct_cmdline(NO_UPGRADE, update_opts); 484 cmdline = construct_cmdline(NO_UPGRADE, update_opts);
377 result = np_runcmd(cmdline, &chld_out, &chld_err, 0); 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
495 output chld_out;
496 output chld_err;
497 int cmd_error = np_runcmd(cmdline, &chld_out, &chld_err, 0);
378 /* 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.
379 * 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
380 * a critical error. */ 500 * a critical error. */
381 if(result != 0){ 501 if (cmd_error != 0) {
382 exec_warning=1; 502 exec_warning = true;
383 result = STATE_CRITICAL; 503 result.sc = mp_set_subcheck_state(result.sc, STATE_CRITICAL);
384 fprintf(stderr, _("'%s' exited with non-zero status.\n"), 504 xasprintf(&result.sc.output, _("'%s' exited with non-zero status.\n"), cmdline);
385 cmdline);
386 } 505 }
387 506
388 if(verbose){ 507 if (verbose) {
389 for(size_t i = 0; i < chld_out.lines; i++) { 508 for (size_t i = 0; i < chld_out.lines; i++) {
390 printf("%s\n", chld_out.line[i]); 509 printf("%s\n", chld_out.line[i]);
391 } 510 }
392 } 511 }
393 512
394 /* If we get anything on stderr, at least set warning */ 513 /* If we get anything on stderr, at least set warning */
395 if(chld_err.buflen){ 514 if (chld_err.buflen) {
396 stderr_warning=1; 515 stderr_warning = true;
397 result = max_state(result, STATE_WARNING); 516 result.sc = mp_set_subcheck_state(
398 if(verbose){ 517 result.sc, max_state(mp_compute_subcheck_state(result.sc), STATE_WARNING));
399 for(size_t i = 0; i < chld_err.lines; i++) { 518 if (verbose) {
519 for (size_t i = 0; i < chld_err.lines; i++) {
400 fprintf(stderr, "%s\n", chld_err.line[i]); 520 fprintf(stderr, "%s\n", chld_err.line[i]);
401 } 521 }
402 } 522 }
403 } 523 }
524
404 free(cmdline); 525 free(cmdline);
526
405 return result; 527 return result;
406} 528}
407 529
408char* pkg_name(char *line){ 530char *pkg_name(char *line) {
409 char *start=NULL, *space=NULL, *pkg=NULL; 531 char *start = line + strlen(PKGINST_PREFIX);
410 int len=0;
411 532
412 start = line + strlen(PKGINST_PREFIX); 533 size_t len = strlen(start);
413 len = strlen(start);
414 534
415 space = index(start, ' '); 535 char *space = index(start, ' ');
416 if(space!=NULL){ 536 if (space != NULL) {
417 len = space - start; 537 len = space - start;
418 } 538 }
419 539
420 pkg=malloc(sizeof(char)*(len+1)); 540 char *pkg = malloc(sizeof(char) * (len + 1));
421 if(!pkg) die(STATE_UNKNOWN, "malloc failed!\n"); 541 if (!pkg) {
542 die(STATE_UNKNOWN, "malloc failed!\n");
543 }
422 544
423 strncpy(pkg, start, len); 545 strncpy(pkg, start, len);
424 pkg[len]='\0'; 546 pkg[len] = '\0';
425 547
426 return pkg; 548 return pkg;
427} 549}
428 550
429int cmpstringp(const void *p1, const void *p2){ 551int cmpstringp(const void *left_string, const void *right_string) {
430 return strcmp(* (char * const *) p1, * (char * const *) p2); 552 return strcmp(*(char *const *)left_string, *(char *const *)right_string);
431} 553}
432 554
433char* add_to_regexp(char *expr, const char *next){ 555char *add_to_regexp(char *expr, const char *next) {
434 char *re=NULL; 556 char *regex_string = NULL;
435 557
436 if(expr==NULL){ 558 if (expr == NULL) {
437 re=malloc(sizeof(char)*(strlen("()")+strlen(next)+1)); 559 regex_string = malloc(sizeof(char) * (strlen("()") + strlen(next) + 1));
438 if(!re) die(STATE_UNKNOWN, "malloc failed!\n"); 560 if (!regex_string) {
439 sprintf(re, "(%s)", next); 561 die(STATE_UNKNOWN, "malloc failed!\n");
562 }
563 sprintf(regex_string, "(%s)", next);
440 } else { 564 } else {
441 /* resize it, adding an extra char for the new '|' separator */ 565 /* resize it, adding an extra char for the new '|' separator */
442 re=realloc(expr, sizeof(char)*(strlen(expr)+1+strlen(next)+1)); 566 regex_string = realloc(expr, sizeof(char) * (strlen(expr) + 1 + strlen(next) + 1));
443 if(!re) die(STATE_UNKNOWN, "realloc failed!\n"); 567 if (!regex_string) {
568 die(STATE_UNKNOWN, "realloc failed!\n");
569 }
444 /* append it starting at ')' in the old re */ 570 /* append it starting at ')' in the old re */
445 sprintf((char*)(re+strlen(re)-1), "|%s)", next); 571 sprintf((char *)(regex_string + strlen(regex_string) - 1), "|%s)", next);
446 } 572 }
447 573
448 return re; 574 return regex_string;
449} 575}
450 576
451char* construct_cmdline(upgrade_type u, const char *opts){ 577char *construct_cmdline(upgrade_type upgrade, const char *opts) {
452 int len=0; 578 const char *opts_ptr = NULL;
453 const char *opts_ptr=NULL, *aptcmd=NULL; 579 const char *aptcmd = NULL;
454 char *cmd=NULL;
455 580
456 switch(u){ 581 switch (upgrade) {
457 case UPGRADE: 582 case UPGRADE:
458 if(opts==NULL) opts_ptr=UPGRADE_DEFAULT_OPTS; 583 if (opts == NULL) {
459 else opts_ptr=opts; 584 opts_ptr = UPGRADE_DEFAULT_OPTS;
460 aptcmd="upgrade"; 585 } else {
586 opts_ptr = opts;
587 }
588 aptcmd = "upgrade";
461 break; 589 break;
462 case DIST_UPGRADE: 590 case DIST_UPGRADE:
463 if(opts==NULL) opts_ptr=UPGRADE_DEFAULT_OPTS; 591 if (opts == NULL) {
464 else opts_ptr=opts; 592 opts_ptr = UPGRADE_DEFAULT_OPTS;
465 aptcmd="dist-upgrade"; 593 } else {
594 opts_ptr = opts;
595 }
596 aptcmd = "dist-upgrade";
466 break; 597 break;
467 case NO_UPGRADE: 598 case NO_UPGRADE:
468 if(opts==NULL) opts_ptr=UPDATE_DEFAULT_OPTS; 599 if (opts == NULL) {
469 else opts_ptr=opts; 600 opts_ptr = UPDATE_DEFAULT_OPTS;
470 aptcmd="update"; 601 } else {
602 opts_ptr = opts;
603 }
604 aptcmd = "update";
471 break; 605 break;
472 } 606 }
473 607
474 len+=strlen(PATH_TO_APTGET)+1; /* "/usr/bin/apt-get " */ 608 size_t len = 0;
475 len+=strlen(opts_ptr)+1; /* "opts " */ 609 len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */
476 len+=strlen(aptcmd)+1; /* "upgrade\0" */ 610 len += strlen(opts_ptr) + 1; /* "opts " */
611 len += strlen(aptcmd) + 1; /* "upgrade\0" */
477 612
478 cmd=(char*)malloc(sizeof(char)*len); 613 char *cmd = (char *)malloc(sizeof(char) * len);
479 if(cmd==NULL) die(STATE_UNKNOWN, "malloc failed"); 614 if (cmd == NULL) {
615 die(STATE_UNKNOWN, "malloc failed");
616 }
480 sprintf(cmd, "%s %s %s", PATH_TO_APTGET, opts_ptr, aptcmd); 617 sprintf(cmd, "%s %s %s", PATH_TO_APTGET, opts_ptr, aptcmd);
481 return cmd; 618 return cmd;
482} 619}
483 620
484/* informative help message */ 621/* informative help message */
485void 622void print_help(void) {
486print_help (void) 623 print_revision(progname, NP_VERSION);
487{ 624
488 print_revision(progname, NP_VERSION); 625 printf(_(COPYRIGHT), copyright, email);
489 626
490 printf(_(COPYRIGHT), copyright, email); 627 printf("%s\n", _("This plugin checks for software updates on systems that use"));
491 628 printf("%s\n", _("package management systems based on the apt-get(8) command"));
492 printf("%s\n", _("This plugin checks for software updates on systems that use")); 629 printf("%s\n", _("found in Debian GNU/Linux"));
493 printf("%s\n", _("package management systems based on the apt-get(8) command")); 630
494 printf("%s\n", _("found in Debian GNU/Linux")); 631 printf("\n\n");
495 632
496 printf ("\n\n"); 633 print_usage();
497 634
498 print_usage(); 635 printf(UT_HELP_VRSN);
499 636 printf(UT_EXTRA_OPTS);
500 printf(UT_HELP_VRSN); 637
501 printf(UT_EXTRA_OPTS); 638 printf(UT_PLUG_TIMEOUT, timeout_interval);
502 639
503 printf(UT_PLUG_TIMEOUT, timeout_interval); 640 printf(" %s\n", "-n, --no-upgrade");
504 641 printf(" %s\n", _("Do not run the upgrade. Probably not useful (without -u at least)."));
505 printf (" %s\n", "-n, --no-upgrade"); 642 printf(" %s\n", "-l, --list");
506 printf (" %s\n", _("Do not run the upgrade. Probably not useful (without -u at least).")); 643 printf(" %s\n", _("List packages available for upgrade. Packages are printed sorted by"));
507 printf (" %s\n", "-l, --list"); 644 printf(" %s\n", _("name with security packages listed first."));
508 printf (" %s\n", _("List packages available for upgrade. Packages are printed sorted by")); 645 printf(" %s\n", "-i, --include=REGEXP");
509 printf (" %s\n", _("name with security packages listed first.")); 646 printf(" %s\n",
510 printf (" %s\n", "-i, --include=REGEXP"); 647 _("Include only packages matching REGEXP. Can be specified multiple times"));
511 printf (" %s\n", _("Include only packages matching REGEXP. Can be specified multiple times")); 648 printf(" %s\n", _("the values will be combined together. Any packages matching this list"));
512 printf (" %s\n", _("the values will be combined together. Any packages matching this list")); 649 printf(" %s\n", _("cause the plugin to return WARNING status. Others will be ignored."));
513 printf (" %s\n", _("cause the plugin to return WARNING status. Others will be ignored.")); 650 printf(" %s\n", _("Default is to include all packages."));
514 printf (" %s\n", _("Default is to include all packages.")); 651 printf(" %s\n", "-e, --exclude=REGEXP");
515 printf (" %s\n", "-e, --exclude=REGEXP"); 652 printf(" %s\n", _("Exclude packages matching REGEXP from the list of packages that would"));
516 printf (" %s\n", _("Exclude packages matching REGEXP from the list of packages that would")); 653 printf(" %s\n", _("otherwise be included. Can be specified multiple times; the values"));
517 printf (" %s\n", _("otherwise be included. Can be specified multiple times; the values")); 654 printf(" %s\n", _("will be combined together. Default is to exclude no packages."));
518 printf (" %s\n", _("will be combined together. Default is to exclude no packages.")); 655 printf(" %s\n", "-c, --critical=REGEXP");
519 printf (" %s\n", "-c, --critical=REGEXP"); 656 printf(" %s\n",
520 printf (" %s\n", _("If the full package information of any of the upgradable packages match")); 657 _("If the full package information of any of the upgradable packages match"));
521 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"));
522 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"));
523 printf (" %s\n", _("upgrades for Debian and Ubuntu:")); 660 printf(" %s\n", _("upgrades for Debian and Ubuntu:"));
524 printf (" \t%s\n", SECURITY_RE); 661 printf(" \t%s\n", SECURITY_RE);
525 printf (" %s\n", _("Note that the package must first match the include list before its")); 662 printf(" %s\n", _("Note that the package must first match the include list before its"));
526 printf (" %s\n", _("information is compared against the critical list.")); 663 printf(" %s\n", _("information is compared against the critical list."));
527 printf (" %s\n", "-o, --only-critical"); 664 printf(" %s\n", "-o, --only-critical");
528 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"));
529 printf (" %s\n", _("of upgrades will be printed, but any non-critical upgrades will not cause")); 666 printf(" %s\n",
530 printf (" %s\n", _("the plugin to return WARNING status.")); 667 _("of upgrades will be printed, but any non-critical upgrades will not cause"));
531 printf (" %s\n", "-w, --packages-warning"); 668 printf(" %s\n", _("the plugin to return WARNING status."));
532 printf (" %s\n", _("Minimum number of packages available for upgrade to return WARNING status.")); 669 printf(" %s\n", "-w, --packages-warning");
533 printf (" %s\n\n", _("Default is 1 package.")); 670 printf(" %s\n",
534 671 _("Minimum number of packages available for upgrade to return WARNING status."));
535 printf ("%s\n\n", _("The following options require root privileges and should be used with care:")); 672 printf(" %s\n\n", _("Default is 1 package."));
536 printf (" %s\n", "-u, --update=OPTS"); 673
537 printf (" %s\n", _("First perform an 'apt-get update'. An optional OPTS parameter overrides")); 674 printf(UT_OUTPUT_FORMAT);
538 printf (" %s\n", _("the default options. Note: you may also need to adjust the global")); 675
539 printf (" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get")); 676 printf("%s\n\n",
540 printf (" %s\n", _("upgrade is expected to take longer than the default timeout.")); 677 _("The following options require root privileges and should be used with care:"));
541 printf (" %s\n", "-U, --upgrade=OPTS"); 678 printf(" %s\n", "-u, --update=OPTS");
542 printf (" %s\n", _("Perform an upgrade. If an optional OPTS argument is provided,")); 679 printf(" %s\n",
543 printf (" %s\n", _("apt-get will be run with these command line options instead of the")); 680 _("First perform an 'apt-get update'. An optional OPTS parameter overrides"));
544 printf (" %s", _("default ")); 681 printf(" %s\n", _("the default options. Note: you may also need to adjust the global"));
545 printf ("(%s).\n", UPGRADE_DEFAULT_OPTS); 682 printf(" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get"));
546 printf (" %s\n", _("Note that you may be required to have root privileges if you do not use")); 683 printf(" %s\n", _("upgrade is expected to take longer than the default timeout."));
547 printf (" %s\n", _("the default options, which will only run a simulation and NOT perform the upgrade")); 684 printf(" %s\n", "-U, --upgrade=OPTS");
548 printf (" %s\n", "-d, --dist-upgrade=OPTS"); 685 printf(" %s\n", _("Perform an upgrade. If an optional OPTS argument is provided,"));
549 printf (" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS")); 686 printf(" %s\n", _("apt-get will be run with these command line options instead of the"));
550 printf (" %s\n", _("can be provided to override the default options.")); 687 printf(" %s", _("default "));
551 688 printf("(%s).\n", UPGRADE_DEFAULT_OPTS);
552 printf(UT_SUPPORT); 689 printf(" %s\n",
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"));
693 printf(" %s\n", "-d, --dist-upgrade=OPTS");
694 printf(" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS"));
695 printf(" %s\n", _("can be provided to override the default options."));
696
697 printf(UT_SUPPORT);
553} 698}
554 699
555
556/* simple usage heading */ 700/* simple usage heading */
557void 701void print_usage(void) {
558print_usage(void) 702 printf("%s\n", _("Usage:"));
559{ 703 printf("%s [[-d|-u|-U]opts] [-n] [-l] [-t timeout] [-w packages-warning]\n", progname);
560 printf ("%s\n", _("Usage:"));
561 printf ("%s [[-d|-u|-U]opts] [-n] [-l] [-t timeout] [-w packages-warning]\n", progname);
562} 704}