1 /******************************************************************************
3 * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
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. *
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. *
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 ******************************************************************************/
26 #include "thresholds.h"
28 #include "boost/program_options.hpp"
32 namespace po = boost::program_options;
34 using std::cout; using std::endl; using std::set;
35 using std::vector; using std::wstring; using std::wcout;
37 static BOOL debug = FALSE;
48 struct printInfoStruct
51 vector<wstring> drives;
55 static int parseArguments(int, wchar_t **, po::variables_map&, printInfoStruct&);
56 static int printOutput(printInfoStruct&, vector<drive>&);
57 static int check_drives(vector<drive>&);
58 static int check_drives(vector<drive>&, printInfoStruct&);
59 static bool getFreeAndCap(drive&, const Bunit&);
61 int wmain(int argc, wchar_t **argv)
63 vector<drive> vDrives;
64 printInfoStruct printInfo{ };
69 ret = parseArguments(argc, argv, vm, printInfo);
73 printInfo.warn.legal = !printInfo.warn.legal;
74 printInfo.crit.legal = !printInfo.crit.legal;
76 if (printInfo.drives.empty())
77 ret = check_drives(vDrives);
79 ret = check_drives(vDrives, printInfo);
84 for (vector<drive>::iterator it = vDrives.begin(); it != vDrives.end(); ++it) {
85 if (!getFreeAndCap(*it, printInfo.unit)) {
86 wcout << L"Failed to access drive at " << it->name << endl;
91 return printOutput(printInfo, vDrives);
94 int parseArguments(int ac, wchar_t **av, po::variables_map& vm, printInfoStruct& printInfo)
96 wchar_t namePath[MAX_PATH];
97 GetModuleFileName(NULL, namePath, MAX_PATH);
98 wchar_t *progName = PathFindFileName(namePath);
100 po::options_description desc("Options");
103 ("help,h", "print usage message and exit")
104 ("version,V", "print version and exit")
105 ("debug,d", "Verbose/Debug output")
106 ("warning,w", po::wvalue<wstring>(), "warning threshold")
107 ("critical,c", po::wvalue<wstring>(), "critical threshold")
108 ("path,p", po::wvalue<vector<std::wstring>>()->multitoken(), "declare explicitly which drives to check (default checks all)")
109 ("unit,u", po::wvalue<wstring>(), "assign unit possible are: B, kB, MB, GB, TB")
112 po::basic_command_line_parser<wchar_t> parser(ac, av);
115 po::options_description allDesc;
118 allDesc.add_options()
119 ("exclude-type,X", po::wvalue<vector<std::wstring>>()->multitoken(), "exclude partition types (ignored)")
120 ("megabytes,m", "use megabytes")
127 po::command_line_style::unix_style |
128 po::command_line_style::allow_long_disguise)
132 } catch (std::exception& e) {
133 cout << e.what() << endl << desc << endl;
137 if (vm.count("help")) {
138 wcout << progName << " Help\n\tVersion: " << VERSION << endl;
140 L"%s is a simple program to check a machines free disk space.\n"
141 L"You can use the following options to define its behaviour:\n\n", progName);
144 L"\nIt will then output a string looking something like this:\n\n"
145 L"\tDISK WARNING 29GB | disk=29GB;50%%;5;0;120\n\n"
146 L"\"DISK\" being the type of the check, \"WARNING\" the returned status\n"
147 L"and \"23.8304%%\" is the returned value.\n"
148 L"The performance data is found behind the \"|\", in order:\n"
149 L"returned value, warning threshold, critical threshold, minimal value and,\n"
150 L"if applicable, the maximal value.\n"
151 L"This program will also print out additional performance data disk by disk\n\n"
152 L"%s' exit codes denote the following:\n\n"
153 L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n"
154 L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n"
155 L" 2\tCRITICAL,\n\tThe critical threshold was broken\n"
156 L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n"
157 L"Threshold syntax:\n\n"
159 L"warn if threshold is broken, which means VALUE < THRESHOLD\n\n"
161 L"inverts threshold check, VALUE > THRESHOLD (analogous to above)\n\n"
163 L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n"
165 L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n"
167 L"if the plugin accepts percentage based thresholds those will be used.\n"
168 L"Does nothing if the plugin does not accept percentages, or only uses\n"
169 L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n"
170 L"to end with a percentage sign.\n\n"
171 L"All of these options work with the critical threshold \"-c\" too."
177 if (vm.count("version"))
178 cout << "Version: " << VERSION << endl;
180 if (vm.count("warning")) {
182 printInfo.warn = threshold(vm["warning"].as<wstring>());
183 } catch (std::invalid_argument& e) {
184 cout << e.what() << endl;
188 if (vm.count("critical")) {
190 printInfo.crit = threshold(vm["critical"].as<wstring>());
191 } catch (std::invalid_argument& e) {
192 cout << e.what() << endl;
197 if (vm.count("path"))
198 printInfo.drives = vm["path"].as<vector<wstring>>();
200 if (vm.count("unit")) {
202 printInfo.unit = parseBUnit(vm["unit"].as<wstring>());
203 } catch (std::invalid_argument) {
204 wcout << L"Unknown unit Type " << vm["unit"].as<wstring>() << endl;
208 printInfo.unit = BunitB;
210 if (vm.count("debug"))
216 int printOutput(printInfoStruct& printInfo, vector<drive>& vDrives)
219 wcout << L"Constructing output string" << endl;
221 vector<wstring> wsDrives, wsPerf;
222 wstring unit = BunitStr(printInfo.unit);
225 wstring output = L"DISK OK - free space:";
227 double tCap = 0, tFree = 0;
228 for (vector<drive>::iterator it = vDrives.begin(); it != vDrives.end(); it++) {
231 wsDrives.push_back(it->name + L" " + removeZero(it->free) + L" " + unit + L" (" +
232 removeZero(std::round(it->free/it->cap * 100.0)) + L"%); ");
233 wsPerf.push_back(L" " + it->name + L"=" + removeZero(it->free) + unit + L";" +
234 printInfo.warn.pString(it->cap) + L";" + printInfo.crit.pString(it->cap) + L";0;"
235 + removeZero(it->cap));
238 if (printInfo.warn.rend(tFree, tCap)) {
240 output = L"DISK WARNING - free space:";
243 if (printInfo.crit.rend(tFree, tCap)) {
245 output = L"DISK CRITICAL - free space:";
249 if (vDrives.size() > 1)
250 wcout << L"Total " << tFree << unit << L" (" << removeZero(std::round(tFree/tCap * 100.0)) << L"%); ";
252 for (vector<wstring>::const_iterator it = wsDrives.begin(); it != wsDrives.end(); it++)
256 for (vector<wstring>::const_iterator it = wsPerf.begin(); it != wsPerf.end(); it++)
263 int check_drives(vector<drive>& vDrives)
265 DWORD dwResult, dwSize = 0, dwVolumePathNamesLen = MAX_PATH + 1;
266 wchar_t szLogicalDrives[1024], szVolumeName[MAX_PATH], *szVolumePathNames = NULL;
267 HANDLE hVolume = NULL;
268 wstring wsLogicalDrives;
269 size_t volumeNameEnd = 0;
271 set<wstring> sDrives;
274 wcout << L"Getting logic drive string (includes network drives)" << endl;
276 dwResult = GetLogicalDriveStrings(MAX_PATH, szLogicalDrives);
277 if (dwResult > MAX_PATH)
280 wcout << L"Splitting string into single drive names" << endl;
282 LPTSTR szSingleDrive = szLogicalDrives;
283 while (*szSingleDrive) {
284 wstring drname = szSingleDrive;
285 sDrives.insert(drname);
286 szSingleDrive += wcslen(szSingleDrive) + 1;
288 wcout << "Got: " << drname << endl;
292 wcout << L"Getting volume mountpoints (includes NTFS folders)" << endl
293 << L"Getting first volume" << endl;
295 hVolume = FindFirstVolume(szVolumeName, MAX_PATH);
296 if (hVolume == INVALID_HANDLE_VALUE)
300 wcout << L"Traversing through list of drives" << endl;
302 while (GetLastError() != ERROR_NO_MORE_FILES) {
304 wcout << L"Path name for " << szVolumeName << L"= \"";
305 volumeNameEnd = wcslen(szVolumeName) - 1;
306 szVolumePathNames = reinterpret_cast<wchar_t*>(new WCHAR[dwVolumePathNamesLen]);
308 while (!GetVolumePathNamesForVolumeName(szVolumeName, szVolumePathNames, dwVolumePathNamesLen, &dwVolumePathNamesLen)) {
309 if (GetLastError() != ERROR_MORE_DATA)
311 delete[] reinterpret_cast<wchar_t*>(szVolumePathNames);
312 szVolumePathNames = reinterpret_cast<wchar_t*>(new WCHAR[dwVolumePathNamesLen]);
316 wcout << szVolumePathNames << L"\"" << endl;
318 //.insert() does the dublicate checking
319 sDrives.insert(wstring(szVolumePathNames));
320 FindNextVolume(hVolume, szVolumeName, MAX_PATH);
323 wcout << L"Creating vector from found volumes, ignoring cd drives etc.:" << endl;
324 for (set<wstring>::iterator it = sDrives.begin(); it != sDrives.end(); ++it) {
325 UINT type = GetDriveType(it->c_str());
326 if (type == DRIVE_FIXED || type == DRIVE_REMOTE) {
328 wcout << L"\t" << *it << endl;
329 vDrives.push_back(drive(*it));
333 FindVolumeClose(hVolume);
334 if (szVolumePathNames)
335 delete[] reinterpret_cast<wchar_t*>(szVolumePathNames);
340 FindVolumeClose(hVolume);
345 int check_drives(vector<drive>& vDrives, printInfoStruct& printInfo)
347 wchar_t *slash = L"\\";
350 wcout << L"Parsing user input drive names" << endl;
352 for (vector<wstring>::iterator it = printInfo.drives.begin();
353 it != printInfo.drives.end(); ++it) {
354 if (it->at(it->length() - 1) != *slash)
356 if (std::wstring::npos == it->find(L":\\")) {
357 wcout << "A \":\" is required after the drive name of " << *it << endl;
361 wcout << L"Added " << *it << endl;
362 vDrives.push_back(drive(*it));
367 bool getFreeAndCap(drive& drive, const Bunit& unit)
370 wcout << L"Getting free disk space for drive " << drive.name << endl;
371 ULARGE_INTEGER tempFree, tempTotal;
372 if (!GetDiskFreeSpaceEx(drive.name.c_str(), NULL, &tempTotal, &tempFree)) {
376 wcout << L"\tcap: " << tempFree.QuadPart << endl;
377 drive.cap = round((tempTotal.QuadPart / pow(1024.0, unit)));
379 wcout << L"\tAfter conversion: " << drive.cap << endl
380 << L"\tfree: " << tempFree.QuadPart << endl;
381 drive.free = round((tempFree.QuadPart / pow(1024.0, unit)));
383 wcout << L"\tAfter conversion: " << drive.free << endl << endl;