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