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