]> granicus.if.org Git - icinga2/blob - plugins/check_update.cpp
Add summarized performance data to check_network
[icinga2] / plugins / check_update.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "plugins/thresholds.hpp"
4 #include <boost/program_options.hpp>
5 #include <iostream>
6 #include <windows.h>
7 #include <shlwapi.h>
8 #include <wuapi.h>
9 #include <wuerror.h>
10
11 #define VERSION 1.0
12
13 #define CRITERIA L"(IsInstalled = 0 and CategoryIDs contains '0fa1201d-4330-4fa8-8ae9-b877473b6441') or (IsInstalled = 0 and CategoryIDs contains 'E6CF1350-C01B-414D-A61F-263D14D133B4')"
14
15 namespace po = boost::program_options;
16
17 struct printInfoStruct
18 {
19         int warn{0};
20         int crit{0};
21         LONG numUpdates{0};
22         bool ignoreReboot{false};
23         int reboot{0};
24         bool careForCanRequest{false};
25 };
26
27 static bool l_Debug;
28
29 static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo)
30 {
31         WCHAR namePath[MAX_PATH];
32         GetModuleFileName(NULL, namePath, MAX_PATH);
33         WCHAR *progName = PathFindFileName(namePath);
34
35         po::options_description desc;
36
37         desc.add_options()
38                 ("help,h", "Print help message and exit")
39                 ("version,V", "Print version and exit")
40                 ("debug,d", "Verbose/Debug output")
41                 ("warning,w", po::value<int>(), "Number of updates to trigger a warning.")
42                 ("critical,c", po::value<int>(), "Number of updates to trigger a critical.")
43                 ("possible-reboot", "Treat \"update may need reboot\" as \"update needs reboot\"")
44                 ("no-reboot-critical", "Do not automatically return critical if an update requiring reboot is present.")
45                 ;
46
47         po::wcommand_line_parser parser(ac, av);
48
49         try {
50                 po::store(
51                         parser
52                         .options(desc)
53                         .style(
54                                 po::command_line_style::unix_style |
55                                 po::command_line_style::allow_long_disguise)
56                         .run(),
57                         vm);
58                 vm.notify();
59         } catch (const std::exception& e) {
60                 std::cout << e.what() << '\n' << desc << '\n';
61                 return 3;
62         }
63
64         if (vm.count("help")) {
65                 std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n';
66                 wprintf(
67                         L"%s is a simple program to check a machines required updates.\n"
68                         L"You can use the following options to define its behaviour:\n\n", progName);
69                 std::cout << desc;
70                 wprintf(
71                         L"\nAfter some time, it will then output a string like this one:\n\n"
72                         L"\tUPDATE WARNING 8 | updates=8;1;1;0\n\n"
73                         L"\"UPDATE\" being the type of the check, \"WARNING\" the returned status\n"
74                         L"and \"8\" is the number of important updates.\n"
75                         L"The performance data is found behind the \"|\", in order:\n"
76                         L"returned value, warning threshold, critical threshold, minimal value and,\n"
77                         L"if applicable, the maximal value.\n\n"
78                         L"An update counts as important when it is part of the Security- or\n"
79                         L"CriticalUpdates group.\n"
80                         L"Consult the msdn on WSUS Classification GUIDs for more information.\n"
81                         L"%s' exit codes denote the following:\n"
82                         L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n"
83                         L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n"
84                         L" 2\tCRITICAL,\n\tThe critical threshold was broken or an update required reboot.\n"
85                         L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n"
86                         L"If a warning threshold is set but not a critical threshold, the critical\n"
87                         L"threshold will be set to one greater than the set warning threshold.\n\n"
88                         L"The \"possible-reboot\" option is not recommended since this true for nearly\n"
89                         L"every update."
90                         , progName);
91                 std::cout << '\n';
92                 return 0;
93         } if (vm.count("version")) {
94                 std::cout << "Version: " << VERSION << '\n';
95                 return 0;
96         }
97         if(vm.count("warning"))
98                 printInfo.warn = vm["warning"].as<int>();
99         if (vm.count("critical"))
100                 printInfo.crit = vm["critical"].as<int>();
101         else if (vm.count("warning"))
102                 printInfo.crit = printInfo.warn + 1;
103         printInfo.careForCanRequest = vm.count("possible-reboot") > 0;
104         printInfo.ignoreReboot = vm.count("no-reboot-critical") > 0;
105
106         l_Debug = vm.count("debug") > 0;
107
108         return -1;
109 }
110
111 static int printOutput(const printInfoStruct& printInfo)
112 {
113         if (l_Debug)
114                 std::wcout << L"Constructing output string" << '\n';
115
116         state state = OK;
117         std::wstring output = L"UPDATE ";
118
119         if (printInfo.numUpdates >= printInfo.warn && printInfo.warn)
120                 state = WARNING;
121
122         if ((printInfo.reboot && !printInfo.ignoreReboot) || (printInfo.numUpdates >= printInfo.crit && printInfo.crit))
123                 state = CRITICAL;
124
125         switch (state) {
126         case OK:
127                 output.append(L"OK ");
128                 break;
129         case WARNING:
130                 output.append(L"WARNING ");
131                 break;
132         case CRITICAL:
133                 output.append(L"CRITICAL ");
134                 break;
135         }
136         output.append(std::to_wstring(printInfo.numUpdates));
137         if (printInfo.reboot) {
138                 output.append(L"; ");
139                 output.append(std::to_wstring(printInfo.reboot));
140                 output.append(L" NEED REBOOT ");
141         }
142         std::wcout << output << L" | 'update'=" << printInfo.numUpdates << L";"
143                 << printInfo.warn << L";" << printInfo.crit << L";0;" << '\n';
144
145         return state;
146 }
147
148 static int check_update(printInfoStruct& printInfo)
149 {
150         if (l_Debug)
151                 std::wcout << "Initializing COM library" << '\n';
152         CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
153         ISearchResult *pResult;
154         IUpdateSession *pSession;
155         IUpdateSearcher *pSearcher;
156         BSTR criteria = NULL;
157
158         HRESULT err;
159         if (l_Debug)
160                 std::wcout << "Creating UpdateSession and UpdateSearcher" << '\n';
161         CoCreateInstance(CLSID_UpdateSession, NULL, CLSCTX_INPROC_SERVER, IID_IUpdateSession, (void **)&pSession);
162         pSession->CreateUpdateSearcher(&pSearcher);
163
164         /*
165         * IsInstalled = 0: All updates, including languagepacks and features
166         * BrowseOnly = 0: No features or languagepacks, security and unnamed
167         * BrowseOnly = 1: Nothing, broken
168         * RebootRequired = 1: Reboot required
169         */
170
171         criteria = SysAllocString(CRITERIA);
172         // https://msdn.microsoft.com/en-us/library/windows/desktop/aa386526%28v=vs.85%29.aspx
173         // https://msdn.microsoft.com/en-us/library/ff357803%28v=vs.85%29.aspx
174
175         if (l_Debug)
176                 std::wcout << L"Querying updates from server" << '\n';
177
178         err = pSearcher->Search(criteria, &pResult);
179         if (!SUCCEEDED(err))
180                 goto die;
181         SysFreeString(criteria);
182
183         IUpdateCollection *pCollection;
184         IUpdate *pUpdate;
185
186         LONG updateSize;
187         pResult->get_Updates(&pCollection);
188         pCollection->get_Count(&updateSize);
189
190         if (updateSize == 0)
191                 return -1;
192
193         printInfo.numUpdates = updateSize;
194         //      printInfo.important = printInfo.warn;
195
196         IInstallationBehavior *pIbehav;
197         InstallationRebootBehavior updateReboot;
198
199         for (LONG i = 0; i < updateSize; i++) {
200                 pCollection->get_Item(i, &pUpdate);
201                 if (l_Debug) {
202                         std::wcout << L"Checking reboot behaviour of update number " << i << '\n';
203                 }
204                 pUpdate->get_InstallationBehavior(&pIbehav);
205                 pIbehav->get_RebootBehavior(&updateReboot);
206                 if (updateReboot == irbAlwaysRequiresReboot) {
207                         printInfo.reboot++;
208                         if (l_Debug)
209                                 std::wcout << L"It requires reboot" << '\n';
210                         continue;
211                 }
212                 if (printInfo.careForCanRequest && updateReboot == irbCanRequestReboot) {
213                         if (l_Debug)
214                                 std::wcout << L"It requires reboot" << '\n';
215                         printInfo.reboot++;
216                 }
217         }
218
219         if (l_Debug)
220                 std::wcout << L"Cleaning up and returning" << '\n';
221
222         SysFreeString(criteria);
223         CoUninitialize();
224         return -1;
225
226 die:
227         printErrorInfo(err);
228         CoUninitialize();
229         if (criteria)
230                 SysFreeString(criteria);
231         return 3;
232 }
233
234 int wmain(int argc, WCHAR **argv)
235 {
236         printInfoStruct printInfo;
237         po::variables_map vm;
238
239         int ret = parseArguments(argc, argv, vm, printInfo);
240         if (ret != -1)
241                 return ret;
242
243         ret = check_update(printInfo);
244         if (ret != -1)
245                 return ret;
246
247         return printOutput(printInfo);
248 }