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