1 /******************************************************************************
3 * Copyright (C) 2012-2018 Icinga Development Team (https://icinga.com/) *
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 ******************************************************************************/
20 #include "plugins/thresholds.hpp"
21 #include <boost/program_options.hpp>
30 using namespace std::placeholders;
34 namespace po = boost::program_options;
48 struct printInfoStruct
52 std::vector<std::wstring> drives;
53 std::vector<std::wstring> exclude_drives;
60 static int check_drives(std::vector<drive>& vDrives, std::vector<std::wstring>& vExclude_Drives)
62 DWORD dwResult, dwSize = 0, dwVolumePathNamesLen = MAX_PATH + 1;
63 WCHAR szLogicalDrives[1024], szVolumeName[MAX_PATH], *szVolumePathNames = NULL;
64 HANDLE hVolume = NULL;
65 std::wstring wsLogicalDrives;
66 size_t volumeNameEnd = 0;
68 std::set<std::wstring> sDrives;
71 std::wcout << "Getting logic drive string (includes network drives)\n";
73 dwResult = GetLogicalDriveStrings(MAX_PATH, szLogicalDrives);
74 if (dwResult > MAX_PATH)
77 std::wcout << "Splitting string into single drive names\n";
79 LPTSTR szSingleDrive = szLogicalDrives;
80 while (*szSingleDrive) {
81 std::wstring drname = szSingleDrive;
82 sDrives.insert(drname);
83 szSingleDrive += wcslen(szSingleDrive) + 1;
85 std::wcout << "Got: " << drname << '\n';
89 std::wcout << "Getting volume mountpoints (includes NTFS folders)\n"
90 << "Getting first volume\n";
92 hVolume = FindFirstVolume(szVolumeName, MAX_PATH);
93 if (hVolume == INVALID_HANDLE_VALUE)
97 std::wcout << "Traversing through list of drives\n";
99 while (GetLastError() != ERROR_NO_MORE_FILES) {
101 std::wcout << "Path name for " << szVolumeName << "= \"";
102 volumeNameEnd = wcslen(szVolumeName) - 1;
103 szVolumePathNames = new WCHAR[dwVolumePathNamesLen];
105 while (!GetVolumePathNamesForVolumeName(szVolumeName, szVolumePathNames, dwVolumePathNamesLen,
106 &dwVolumePathNamesLen)) {
107 if (GetLastError() != ERROR_MORE_DATA)
109 delete[] szVolumePathNames;
110 szVolumePathNames = new WCHAR[dwVolumePathNamesLen];
114 std::wcout << szVolumePathNames << "\"\n";
116 sDrives.insert(std::wstring(szVolumePathNames));
117 FindNextVolume(hVolume, szVolumeName, MAX_PATH);
120 std::wcout << "Creating vector from found volumes, ignoring CD drives etc.:\n";
121 for (const auto& driveName : sDrives) {
122 unsigned int type = GetDriveType(driveName.c_str());
123 if (type == DRIVE_FIXED || type == DRIVE_REMOTE) {
125 std::wcout << "\t" << driveName << '\n';
126 vDrives.push_back(drive(driveName));
130 FindVolumeClose(hVolume);
131 if (szVolumePathNames)
132 delete[] szVolumePathNames;
135 std::wcout << "Removing excluded drives\n";
137 for (const auto& driveName : vExclude_Drives) {
138 vDrives.erase(std::remove_if(vDrives.begin(), vDrives.end(),
139 [&driveName](const drive& d) { return d.name == driveName + L'\\'; }),
147 FindVolumeClose(hVolume);
152 static int check_drives(std::vector<drive>& vDrives, printInfoStruct& printInfo)
155 std::wcout << "Removing excluded drives from user input drives\n";
157 for (const auto& driveName : printInfo.exclude_drives) {
158 printInfo.drives.erase(std::remove(printInfo.drives.begin(), printInfo.drives.end(), driveName),
159 printInfo.drives.end());
163 std::wcout << "Parsing user input drive names\n";
165 for (auto& driveName : printInfo.drives) {
166 if (driveName.at(driveName.length() - 1) != *L"\\")
167 driveName.append(L"\\");
169 if (std::wstring::npos == driveName.find(L":\\")) {
170 std::wcout << "A \":\" is required after the drive name of " << driveName << '\n';
175 std::wcout << "Added " << driveName << '\n';
177 vDrives.emplace_back(driveName);
183 static bool getDriveSpaceValues(drive& drive, const Bunit& unit)
186 std::wcout << "Getting free and used disk space for drive " << drive.name << '\n';
188 ULARGE_INTEGER tempFree, tempTotal;
189 if (!GetDiskFreeSpaceEx(drive.name.c_str(), NULL, &tempTotal, &tempFree))
192 ULARGE_INTEGER tempUsed;
193 tempUsed.QuadPart = tempTotal.QuadPart - tempFree.QuadPart;
196 std::wcout << "\tcap: " << tempFree.QuadPart << '\n';
198 drive.cap = round((tempTotal.QuadPart / pow(1024.0, unit)));
201 std::wcout << "\tAfter conversion: " << drive.cap << '\n'
202 << "\tfree: " << tempFree.QuadPart << '\n';
204 drive.free = round((tempFree.QuadPart / pow(1024.0, unit)));
207 std::wcout << "\tAfter conversion: " << drive.free << '\n'
208 << "\tused: " << tempUsed.QuadPart << '\n';
210 drive.used = round((tempUsed.QuadPart / pow(1024.0, unit)));
213 std::wcout << "\tAfter conversion: " << drive.used << '\n' << '\n';
218 static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo)
220 WCHAR namePath[MAX_PATH];
221 GetModuleFileName(NULL, namePath, MAX_PATH);
222 WCHAR *progName = PathFindFileName(namePath);
224 po::options_description desc("Options");
227 ("help,h", "Print usage message and exit")
228 ("version,V", "Print version and exit")
229 ("debug,d", "Verbose/Debug output")
230 ("warning,w", po::wvalue<std::wstring>(), "Warning threshold")
231 ("critical,c", po::wvalue<std::wstring>(), "Critical threshold")
232 ("path,p", po::wvalue<std::vector<std::wstring>>()->multitoken(), "Declare explicitly which drives to check (default checks all)")
233 ("exclude_device,x", po::wvalue<std::vector<std::wstring>>()->multitoken(), "Exclude these drives from check")
234 ("exclude-type,X", po::wvalue<std::vector<std::wstring>>()->multitoken(), "Exclude partition types (ignored)")
235 ("iwarning,W", po::wvalue<std::wstring>(), "Warning threshold for inodes (ignored)")
236 ("icritical,K", po::wvalue<std::wstring>(), "Critical threshold for inodes (ignored)")
237 ("unit,u", po::wvalue<std::wstring>(), "Assign unit possible are: B, kB, MB, GB, TB")
238 ("show-used,U", "Show used space instead of the free space")
239 ("megabytes,m", "use megabytes, overridden by -unit")
242 po::wcommand_line_parser parser(ac, av);
249 po::command_line_style::unix_style |
250 po::command_line_style::allow_long_disguise)
254 } catch (const std::exception& e) {
255 std::cout << e.what() << '\n' << desc << '\n';
259 if (vm.count("help")) {
260 std::wcout << progName << " Help\n\tVersion: " << VERSION << '\n';
262 L"%s is a simple program to check a machines disk space usage.\n"
263 L"You can use the following options to define its behaviour:\n\n", progName);
266 L"\nIt will then output a string looking something like this:\n\n"
267 L"\tDISK WARNING 29GB | disk=29GB;50%%;5;0;120\n\n"
268 L"\"DISK\" being the type of the check, \"WARNING\" the returned status\n"
269 L"and \"23.8304%%\" is the returned value.\n"
270 L"The performance data is found behind the \"|\", in order:\n"
271 L"returned value, warning threshold, critical threshold, minimal value and,\n"
272 L"if applicable, the maximal value.\n"
273 L"This program will also print out additional performance data disk by disk\n\n"
274 L"%s' exit codes denote the following:\n\n"
275 L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n"
276 L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n"
277 L" 2\tCRITICAL,\n\tThe critical threshold was broken\n"
278 L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n"
279 L"Threshold syntax:\n\n"
281 L"warn if threshold is broken, which means VALUE < THRESHOLD\n\n"
283 L"inverts threshold check, VALUE > THRESHOLD (analogous to above)\n\n"
285 L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n"
287 L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n"
289 L"if the plugin accepts percentage based thresholds those will be used.\n"
290 L"Does nothing if the plugin does not accept percentages, or only uses\n"
291 L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n"
292 L"to end with a percentage sign.\n\n"
293 L"All of these options work with the critical threshold \"-c\" too."
299 if (vm.count("version"))
300 std::cout << "Version: " << VERSION << '\n';
302 if (vm.count("warning")) {
304 printInfo.warn = threshold(vm["warning"].as<std::wstring>());
305 } catch (const std::invalid_argument& e) {
306 std::cout << e.what() << '\n';
310 if (vm.count("critical")) {
312 printInfo.crit = threshold(vm["critical"].as<std::wstring>());
313 } catch (const std::invalid_argument& e) {
314 std::cout << e.what() << '\n';
319 if (vm.count("path"))
320 printInfo.drives = vm["path"].as<std::vector<std::wstring>>();
322 if (vm.count("exclude_device"))
323 printInfo.exclude_drives = vm["exclude_device"].as<std::vector<std::wstring>>();
325 if (vm.count("unit")) {
327 printInfo.unit = parseBUnit(vm["unit"].as<std::wstring>());
328 } catch (const std::invalid_argument&) {
329 std::wcout << "Unknown unit Type " << vm["unit"].as<std::wstring>() << '\n';
333 if (vm.count("megabytes"))
334 printInfo.unit = BunitMB;
336 printInfo.unit = BunitB;
339 printInfo.showUsed = vm.count("show-used") > 0;
341 l_Debug = vm.count("debug") > 0;
346 static int printOutput(printInfoStruct& printInfo, std::vector<drive>& vDrives)
349 std::wcout << "Constructing output string\n";
351 std::vector<std::wstring> wsDrives, wsPerf;
352 std::wstring unit = BunitStr(printInfo.unit);
356 std::wstring output = L"DISK OK - free space:";
358 if (printInfo.showUsed)
359 output = L"DISK OK - used space:";
361 double tCap = 0, tFree = 0, tUsed = 0;
363 for (std::vector<drive>::iterator it = vDrives.begin(); it != vDrives.end(); it++) {
368 if (printInfo.showUsed) {
369 wsDrives.push_back(it->name + L" " + removeZero(it->used) + L" " + unit + L" (" +
370 removeZero(std::round(it->used / it->cap * 100.0)) + L"%); ");
372 wsPerf.push_back(L" " + it->name + L"=" + removeZero(it->used) + unit + L";" +
373 printInfo.warn.pString(it->cap) + L";" + printInfo.crit.pString(it->cap) + L";0;"
374 + removeZero(it->cap));
376 if (printInfo.crit.set && !printInfo.crit.rend(it->used, it->cap))
379 if (state == OK && printInfo.warn.set && !printInfo.warn.rend(it->used, it->cap))
382 wsDrives.push_back(it->name + L" " + removeZero(it->free) + L" " + unit + L" (" +
383 removeZero(std::round(it->free / it->cap * 100.0)) + L"%); ");
385 wsPerf.push_back(L" '" + it->name + L"'=" + removeZero(it->free) + unit + L";" +
386 printInfo.warn.pString(it->cap) + L";" + printInfo.crit.pString(it->cap) + L";0;"
387 + removeZero(it->cap));
389 if (printInfo.crit.rend(it->free, it->cap))
392 if (state == OK && printInfo.warn.rend(it->free, it->cap))
397 if (state == WARNING) {
398 output = L"DISK WARNING - free space:";
400 if (printInfo.showUsed)
401 output = L"DISK WARNING - used space:";
404 if (state == CRITICAL) {
405 output = L"DISK CRITICAL - free space:";
407 if (printInfo.showUsed)
408 output = L"DISK CRITICAL - used space:";
411 std::wcout << output;
413 if (vDrives.size() > 1 && printInfo.showUsed) {
414 std::wcout << "Total " << (printInfo.showUsed ? tUsed : tFree) << unit
415 << " (" << removeZero(std::round(tUsed / tCap * 100.0)) << "%); ";
418 for (const auto& driveName : wsDrives)
419 std::wcout << driveName;
423 for (const auto& perf : wsPerf)
431 int wmain(int argc, WCHAR **argv)
433 std::vector<drive> vDrives;
434 printInfoStruct printInfo;
435 po::variables_map vm;
439 ret = parseArguments(argc, argv, vm, printInfo);
443 printInfo.warn.legal = !printInfo.warn.legal;
444 printInfo.crit.legal = !printInfo.crit.legal;
446 if (printInfo.drives.empty())
447 ret = check_drives(vDrives, printInfo.exclude_drives);
449 ret = check_drives(vDrives, printInfo);
454 for (std::vector<drive>::iterator it = vDrives.begin(); it != vDrives.end(); ++it) {
455 if (!getDriveSpaceValues(*it, printInfo.unit)) {
456 std::wcout << "Failed to access drive at " << it->name << '\n';
461 return printOutput(printInfo, vDrives);