1 /******************************************************************************
3 * Copyright (C) 2012-2014 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;
46 struct printInfoStruct
49 vector<wstring> drives;
53 static int parseArguments(int, wchar_t **, po::variables_map&, printInfoStruct&);
54 static int printOutput(printInfoStruct&, vector<drive>&);
55 static int check_drives(vector<drive>&);
56 static int check_drives(vector<drive>&, printInfoStruct&);
57 static bool getFreeAndCap(drive&, const Bunit&);
59 int wmain(int argc, wchar_t **argv)
61 vector<drive> vDrives;
62 printInfoStruct printInfo{ };
67 ret = parseArguments(argc, argv, vm, printInfo);
71 printInfo.warn.legal = !printInfo.warn.legal;
72 printInfo.crit.legal = !printInfo.crit.legal;
74 if (printInfo.drives.empty())
75 ret = check_drives(vDrives);
77 ret = check_drives(vDrives, printInfo);
82 for (vector<drive>::iterator it = vDrives.begin(); it != vDrives.end(); ++it) {
83 if (!getFreeAndCap(*it, printInfo.unit)) {
84 wcout << L"Failed to access drive at " << it->name << endl;
89 return printOutput(printInfo, vDrives);
92 int parseArguments(int ac, wchar_t **av, po::variables_map& vm, printInfoStruct& printInfo)
94 wchar_t namePath[MAX_PATH];
95 GetModuleFileName(NULL, namePath, MAX_PATH);
96 wchar_t *progName = PathFindFileName(namePath);
98 po::options_description desc("Options");
101 ("help,h", "print usage message and exit")
102 ("version,V", "print version and exit")
103 ("warning,w", po::wvalue<wstring>(), "warning threshold")
104 ("critical,c", po::wvalue<wstring>(), "critical threshold")
105 ("path,p", po::wvalue<vector<std::wstring>>()->multitoken(), "declare explicitly which drives to check (default checks all)")
106 ("unit,u", po::wvalue<wstring>(), "assign unit possible are: B, kB, MB, GB, TB")
109 po::basic_command_line_parser<wchar_t> parser(ac, av);
116 po::command_line_style::unix_style |
117 po::command_line_style::allow_long_disguise)
121 } catch (std::exception& e) {
122 cout << e.what() << endl << desc << endl;
126 if (vm.count("help")) {
127 wcout << progName << " Help\n\tVersion: " << VERSION << endl;
129 L"%s is a simple program to check a machines free disk space.\n"
130 L"You can use the following options to define its behaviour:\n\n", progName);
133 L"\nIt will then output a string looking something like this:\n\n"
134 L"\tDISK WARNING 29GB|disk=29GB;50%%;5;0;120\n\n"
135 L"\"DISK\" being the type of the check, \"WARNING\" the returned status\n"
136 L"and \"23.8304%%\" is the returned value.\n"
137 L"The performance data is found behind the \"|\", in order:\n"
138 L"returned value, warning threshold, critical threshold, minimal value and,\n"
139 L"if applicable, the maximal value.\n"
140 L"This program will also print out additional performance data disk by disk\n\n"
141 L"%s' exit codes denote the following:\n\n"
142 L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n"
143 L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n"
144 L" 2\tCRITICAL,\n\tThe critical threshold was broken\n"
145 L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n"
146 L"Threshold syntax:\n\n"
148 L"warn if threshold is broken, which means VALUE < THRESHOLD\n\n"
150 L"inverts threshold check, VALUE > THRESHOLD (analogous to above)\n\n"
152 L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n"
154 L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n"
156 L"if the plugin accepts percentage based thresholds those will be used.\n"
157 L"Does nothing if the plugin does not accept percentages, or only uses\n"
158 L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n"
159 L"to end with a percentage sign.\n\n"
160 L"All of these options work with the critical threshold \"-c\" too."
166 if (vm.count("version"))
167 cout << "Version: " << VERSION << endl;
169 if (vm.count("warning")) {
171 printInfo.warn = threshold(vm["warning"].as<wstring>());
172 } catch (std::invalid_argument& e) {
173 cout << e.what() << endl;
177 if (vm.count("critical")) {
179 printInfo.crit = threshold(vm["critical"].as<wstring>());
180 } catch (std::invalid_argument& e) {
181 cout << e.what() << endl;
186 if (vm.count("path"))
187 printInfo.drives = vm["path"].as<vector<wstring>>();
189 if (vm.count("unit")) {
191 printInfo.unit = parseBUnit(vm["unit"].as<wstring>());
192 } catch (std::invalid_argument) {
193 wcout << L"Unknown unit Type " << vm["unit"].as<wstring>() << endl;
197 printInfo.unit = BunitB;
202 int printOutput(printInfoStruct& printInfo, vector<drive>& vDrives)
205 double tCap = 0, tFree = 0;
206 std::wstringstream perf, prePerf;
207 wstring unit = BunitStr(printInfo.unit);
209 for (vector<drive>::iterator it = vDrives.begin(); it != vDrives.end(); ++it) {
210 tCap += it->cap; tFree += it->free;
211 perf << std::fixed << L" " << it->name << L"=" << removeZero(it->free) << unit << L";"
212 << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";0;" << removeZero(tCap);
215 prePerf << L" | disk=" << removeZero(tFree) << unit << L";" << printInfo.warn.pString() << L";"
216 << printInfo.crit.pString() << L";0;" << removeZero(tCap);
218 if (printInfo.warn.perc) {
219 if (printInfo.warn.rend((tFree / tCap) * 100.0))
222 if (printInfo.warn.rend(tFree))
226 if (printInfo.crit.perc) {
227 if (printInfo.crit.rend((tFree / tCap) * 100.0))
230 if (printInfo.crit.rend(tFree))
236 wcout << L"DISK OK " << tFree << unit << prePerf.str() << perf.str() << endl;
239 wcout << L"DISK WARNING " << tFree << unit << prePerf.str() << perf.str() << endl;
242 wcout << L"DISK CRITICAL " << tFree << unit << prePerf.str() << perf.str() << endl;
249 int check_drives(vector<drive>& vDrives)
251 DWORD dwResult, dwSize = 0, dwVolumePathNamesLen = MAX_PATH + 1;
252 wchar_t szLogicalDrives[MAX_PATH], szVolumeName[MAX_PATH], *szVolumePathNames;
254 wstring wsLogicalDrives;
255 size_t volumeNameEnd = 0;
257 set<wstring> sDrives;
259 dwResult = GetLogicalDriveStrings(MAX_PATH, szLogicalDrives);
260 if (dwResult < 0 || dwResult > MAX_PATH)
263 LPTSTR szSingleDrive = szLogicalDrives;
264 while (*szSingleDrive) {
265 wstring drname = szSingleDrive;
266 sDrives.insert(drname);
267 szSingleDrive += wcslen(szSingleDrive) + 1;
270 hVolume = FindFirstVolume(szVolumeName, MAX_PATH);
271 if (hVolume == INVALID_HANDLE_VALUE)
274 while (GetLastError() != ERROR_NO_MORE_FILES) {
275 volumeNameEnd = wcslen(szVolumeName) - 1;
276 szVolumePathNames = reinterpret_cast<wchar_t*>(new WCHAR[dwVolumePathNamesLen]);
278 while (!GetVolumePathNamesForVolumeName(szVolumeName, szVolumePathNames, dwVolumePathNamesLen, &dwVolumePathNamesLen)) {
279 if (GetLastError() != ERROR_MORE_DATA)
281 delete[] reinterpret_cast<wchar_t*>(szVolumePathNames);
282 szVolumePathNames = reinterpret_cast<wchar_t*>(new WCHAR[dwVolumePathNamesLen]);
286 sDrives.insert(wstring(szVolumePathNames));
287 FindNextVolume(hVolume, szVolumeName, MAX_PATH);
290 for (set<wstring>::iterator it = sDrives.begin(); it != sDrives.end(); ++it) {
291 UINT type = GetDriveType(it->c_str());
292 if (type == DRIVE_FIXED || type == DRIVE_REMOTE) {
293 vDrives.push_back(drive(*it));
300 FindVolumeClose(hVolume);
305 int check_drives(vector<drive>& vDrives, printInfoStruct& printInfo)
307 wchar_t *slash = L"\\";
309 for (vector<wstring>::iterator it = printInfo.drives.begin();
310 it != printInfo.drives.end(); ++it) {
311 if (it->at(it->length() - 1) != *slash)
314 if (std::wstring::npos == it->find(L":\\")) {
315 wcout << "A \":\" is required after the drive name of " << *it << endl;
318 vDrives.push_back(drive(*it));
323 bool getFreeAndCap(drive& drive, const Bunit& unit)
325 ULARGE_INTEGER tempFree, tempTotal;
326 if (!GetDiskFreeSpaceEx(drive.name.c_str(), NULL, &tempTotal, &tempFree)) {
330 drive.cap = round((tempTotal.QuadPart / pow(1024.0, unit)));
331 drive.free = round((tempFree.QuadPart / pow(1024.0, unit)));