]> granicus.if.org Git - icinga2/blob - plugins/check_update.cpp
Merge pull request #6389 from Mikesch-mp/itl_nscp-local-tasksched
[icinga2] / plugins / check_update.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2018 Icinga Development Team (https://www.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         bool warn{false};
37         bool crit{false};
38         LONG numUpdates{0};
39         bool important{false};
40         bool reboot{false};
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", "Warn if there are important updates available")
59                 ("critical,c", "Critical if there are important updates that require a reboot")
60                 ("possible-reboot", "Treat \"update may need reboot\" as \"update needs reboot\"")
61                 ;
62
63         po::wcommand_line_parser parser(ac, av);
64
65         try {
66                 po::store(
67                         parser
68                         .options(desc)
69                         .style(
70                                 po::command_line_style::unix_style |
71                                 po::command_line_style::allow_long_disguise)
72                         .run(),
73                         vm);
74                 vm.notify();
75         } catch (const std::exception& e) {
76                 std::cout << e.what() << '\n' << desc << '\n';
77                 return 3;
78         }
79
80         if (vm.count("help")) {
81                 std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n';
82                 wprintf(
83                         L"%s is a simple program to check a machines required updates.\n"
84                         L"You can use the following options to define its behaviour:\n\n", progName);
85                 std::cout << desc;
86                 wprintf(
87                         L"\nAfter some time, it will then output a string like this one:\n\n"
88                         L"\tUPDATE WARNING 8 | updates=8;1;1;0\n\n"
89                         L"\"UPDATE\" being the type of the check, \"WARNING\" the returned status\n"
90                         L"and \"8\" is the number of important updates updates.\n"
91                         L"The performance data is found behind the \"|\", in order:\n"
92                         L"returned value, warning threshold, critical threshold, minimal value and,\n"
93                         L"if applicable, the maximal value. Performance data will only be displayed when\n"
94                         L"you set at least one threshold\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\n"
102                         L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n"
103                         L"%s works different from other plugins in that you do not set thresholds\n"
104                         L"but only activate them. Using \"-w\" triggers warning state if there are not\n"
105                         L"installed and non-optional updates. \"-c\" triggers critical if there are\n"
106                         L"non-optional updates that require a reboot.\n"
107                         L"The \"possible-reboot\" option is not recommended since this true for nearly\n"
108                         L"every update."
109                         , progName, progName);
110                 std::cout << '\n';
111                 return 0;
112         } if (vm.count("version")) {
113                 std::cout << "Version: " << VERSION << '\n';
114                 return 0;
115         }
116
117         printInfo.warn = vm.count("warning") > 0;
118         printInfo.crit = vm.count("critical") > 0;
119         printInfo.careForCanRequest = vm.count("possible-reboot") > 0;
120
121         l_Debug = vm.count("debug") > 0;
122
123         return -1;
124 }
125
126 static int printOutput(const printInfoStruct& printInfo)
127 {
128         if (l_Debug)
129                 std::wcout << L"Constructing output string" << '\n';
130
131         state state = OK;
132         std::wstring output = L"UPDATE ";
133
134         if (printInfo.important)
135                 state = WARNING;
136
137         if (printInfo.reboot)
138                 state = CRITICAL;
139
140         switch (state) {
141         case OK:
142                 output.append(L"OK ");
143                 break;
144         case WARNING:
145                 output.append(L"WARNING ");
146                 break;
147         case CRITICAL:
148                 output.append(L"CRITICAL ");
149                 break;
150         }
151
152         std::wcout << output << printInfo.numUpdates << L" | 'update'=" << printInfo.numUpdates << L";"
153                 << printInfo.warn << L";" << printInfo.crit << L";0;" << '\n';
154
155         return state;
156 }
157
158 static int check_update(printInfoStruct& printInfo)
159 {
160         if (l_Debug)
161                 std::wcout << "Initializing COM library" << '\n';
162         CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
163         ISearchResult *pResult;
164         IUpdateSession *pSession;
165         IUpdateSearcher *pSearcher;
166         BSTR criteria = NULL;
167
168         HRESULT err;
169         if (l_Debug)
170                 std::wcout << "Creating UpdateSession and UpdateSearcher" << '\n';
171         CoCreateInstance(CLSID_UpdateSession, NULL, CLSCTX_INPROC_SERVER, IID_IUpdateSession, (void **)&pSession);
172         pSession->CreateUpdateSearcher(&pSearcher);
173
174         /*
175         * IsInstalled = 0: All updates, including languagepacks and features
176         * BrowseOnly = 0: No features or languagepacks, security and unnamed
177         * BrowseOnly = 1: Nothing, broken
178         * RebootRequired = 1: Reboot required
179         */
180
181         criteria = SysAllocString(CRITERIA);
182         // https://msdn.microsoft.com/en-us/library/windows/desktop/aa386526%28v=vs.85%29.aspx
183         // https://msdn.microsoft.com/en-us/library/ff357803%28v=vs.85%29.aspx
184
185         if (l_Debug)
186                 std::wcout << L"Querying updates from server" << '\n';
187
188         err = pSearcher->Search(criteria, &pResult);
189         if (!SUCCEEDED(err))
190                 goto die;
191         SysFreeString(criteria);
192
193         IUpdateCollection *pCollection;
194         IUpdate *pUpdate;
195
196         LONG updateSize;
197         pResult->get_Updates(&pCollection);
198         pCollection->get_Count(&updateSize);
199
200         if (updateSize == 0)
201                 return -1;
202
203         printInfo.numUpdates = updateSize;
204         //      printInfo.important = printInfo.warn;
205
206         IInstallationBehavior *pIbehav;
207         InstallationRebootBehavior updateReboot;
208
209         for (LONG i = 0; i < updateSize; i++) {
210                 pCollection->get_Item(i, &pUpdate);
211                 if (l_Debug) {
212                         std::wcout << L"Checking reboot behaviour of update number " << i << '\n';
213                 }
214                 pUpdate->get_InstallationBehavior(&pIbehav);
215                 pIbehav->get_RebootBehavior(&updateReboot);
216                 if (updateReboot == irbAlwaysRequiresReboot) {
217                         printInfo.reboot = true;
218                         if (l_Debug)
219                                 std::wcout << L"It requires reboot" << '\n';
220                         continue;
221                 }
222                 if (printInfo.careForCanRequest && updateReboot == irbCanRequestReboot)
223                         if (l_Debug)
224                                 std::wcout << L"It requires reboot" << '\n';
225                 printInfo.reboot = true;
226         }
227
228         if (l_Debug)
229                 std::wcout << L"Cleaning up and returning" << '\n';
230
231         SysFreeString(criteria);
232         CoUninitialize();
233         return -1;
234
235 die:
236         printErrorInfo(err);
237         CoUninitialize();
238         if (criteria)
239                 SysFreeString(criteria);
240         return 3;
241 }
242
243 int wmain(int argc, WCHAR **argv)
244 {
245         printInfoStruct printInfo;
246         po::variables_map vm;
247
248         int ret = parseArguments(argc, argv, vm, printInfo);
249         if (ret != -1)
250                 return ret;
251
252         ret = check_update(printInfo);
253         if (ret != -1)
254                 return ret;
255
256         return printOutput(printInfo);
257 }