]> granicus.if.org Git - icinga2/blob - lib/base/utility.cpp
Merge pull request #7185 from Icinga/bugfix/gelfwriter-wrong-log-facility
[icinga2] / lib / base / utility.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "base/utility.hpp"
4 #include "base/convert.hpp"
5 #include "base/application.hpp"
6 #include "base/logger.hpp"
7 #include "base/exception.hpp"
8 #include "base/socket.hpp"
9 #include "base/utility.hpp"
10 #include "base/json.hpp"
11 #include "base/objectlock.hpp"
12 #include <cstdint>
13 #include <mmatch.h>
14 #include <boost/filesystem/path.hpp>
15 #include <boost/filesystem/operations.hpp>
16 #include <boost/lexical_cast.hpp>
17 #include <boost/system/error_code.hpp>
18 #include <boost/thread/tss.hpp>
19 #include <boost/algorithm/string/trim.hpp>
20 #include <boost/algorithm/string/replace.hpp>
21 #include <boost/uuid/uuid_io.hpp>
22 #include <boost/uuid/uuid_generators.hpp>
23 #include <ios>
24 #include <fstream>
25 #include <iostream>
26 #include <iterator>
27 #include <stdlib.h>
28 #include <future>
29 #include <utf8.h>
30 #include <vector>
31
32 #ifdef __FreeBSD__
33 #       include <pthread_np.h>
34 #endif /* __FreeBSD__ */
35
36 #ifdef HAVE_CXXABI_H
37 #       include <cxxabi.h>
38 #endif /* HAVE_CXXABI_H */
39
40 #ifndef _WIN32
41 #       include <sys/types.h>
42 #       include <sys/utsname.h>
43 #       include <pwd.h>
44 #       include <grp.h>
45 #       include <errno.h>
46 #endif /* _WIN32 */
47
48 #ifdef _WIN32
49 #       include <VersionHelpers.h>
50 #       include <windows.h>
51 #       include <io.h>
52 #       include <msi.h>
53 #       include <shlobj.h>
54 #endif /*_WIN32*/
55
56 using namespace icinga;
57
58 boost::thread_specific_ptr<String> Utility::m_ThreadName;
59 boost::thread_specific_ptr<unsigned int> Utility::m_RandSeed;
60
61 #ifdef I2_DEBUG
62 double Utility::m_DebugTime = -1;
63 #endif /* I2_DEBUG */
64
65 /**
66  * Demangles a symbol name.
67  *
68  * @param sym The symbol name.
69  * @returns A human-readable version of the symbol name.
70  */
71 String Utility::DemangleSymbolName(const String& sym)
72 {
73         String result = sym;
74
75 #ifdef HAVE_CXXABI_H
76         int status;
77         char *realname = abi::__cxa_demangle(sym.CStr(), nullptr, nullptr, &status);
78
79         if (realname) {
80                 result = String(realname);
81                 free(realname);
82         }
83 #elif defined(_MSC_VER) /* HAVE_CXXABI_H */
84         CHAR output[256];
85
86         if (UnDecorateSymbolName(sym.CStr(), output, sizeof(output), UNDNAME_COMPLETE) > 0)
87                 result = output;
88 #else /* _MSC_VER */
89         /* We're pretty much out of options here. */
90 #endif /* _MSC_VER */
91
92         return result;
93 }
94
95 /**
96  * Returns a human-readable type name of a type_info object.
97  *
98  * @param ti A type_info object.
99  * @returns The type name of the object.
100  */
101 String Utility::GetTypeName(const std::type_info& ti)
102 {
103         return DemangleSymbolName(ti.name());
104 }
105
106 String Utility::GetSymbolName(const void *addr)
107 {
108 #ifdef HAVE_DLADDR
109         Dl_info dli;
110
111         if (dladdr(const_cast<void *>(addr), &dli) > 0)
112                 return dli.dli_sname;
113 #endif /* HAVE_DLADDR */
114
115 #ifdef _WIN32
116         char buffer[sizeof(SYMBOL_INFO)+MAX_SYM_NAME * sizeof(TCHAR)];
117         PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
118         pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
119         pSymbol->MaxNameLen = MAX_SYM_NAME;
120
121         DWORD64 dwAddress = (DWORD64)addr;
122         DWORD64 dwDisplacement;
123
124         IMAGEHLP_LINE64 line;
125         line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
126
127         if (SymFromAddr(GetCurrentProcess(), dwAddress, &dwDisplacement, pSymbol)) {
128                 char output[256];
129                 if (UnDecorateSymbolName(pSymbol->Name, output, sizeof(output), UNDNAME_COMPLETE))
130                         return String(output) + "+" + Convert::ToString(dwDisplacement);
131                 else
132                         return String(pSymbol->Name) + "+" + Convert::ToString(dwDisplacement);
133         }
134 #endif /* _WIN32 */
135
136         return "(unknown function)";
137 }
138
139 /**
140  * Performs wildcard pattern matching.
141  *
142  * @param pattern The wildcard pattern.
143  * @param text The String that should be checked.
144  * @returns true if the wildcard pattern matches, false otherwise.
145  */
146 bool Utility::Match(const String& pattern, const String& text)
147 {
148         return (match(pattern.CStr(), text.CStr()) == 0);
149 }
150
151 static bool ParseIp(const String& ip, char addr[16], int *proto)
152 {
153         if (inet_pton(AF_INET, ip.CStr(), addr + 12) == 1) {
154                 /* IPv4-mapped IPv6 address (::ffff:<ipv4-bits>) */
155                 memset(addr, 0, 10);
156                 memset(addr + 10, 0xff, 2);
157                 *proto = AF_INET;
158
159                 return true;
160         }
161
162         if (inet_pton(AF_INET6, ip.CStr(), addr) == 1) {
163                 *proto = AF_INET6;
164
165                 return true;
166         }
167
168         return false;
169 }
170
171 static void ParseIpMask(const String& ip, char mask[16], int *bits)
172 {
173         String::SizeType slashp = ip.FindFirstOf("/");
174         String uip;
175
176         if (slashp == String::NPos) {
177                 uip = ip;
178                 *bits = 0;
179         } else {
180                 uip = ip.SubStr(0, slashp);
181                 *bits = Convert::ToLong(ip.SubStr(slashp + 1));
182         }
183
184         int proto;
185
186         if (!ParseIp(uip, mask, &proto))
187                 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid IP address specified."));
188
189         if (proto == AF_INET) {
190                 if (*bits > 32 || *bits < 0)
191                         BOOST_THROW_EXCEPTION(std::invalid_argument("Mask must be between 0 and 32 for IPv4 CIDR masks."));
192
193                 *bits += 96;
194         }
195
196         if (slashp == String::NPos)
197                 *bits = 128;
198
199         if (*bits > 128 || *bits < 0)
200                 BOOST_THROW_EXCEPTION(std::invalid_argument("Mask must be between 0 and 128 for IPv6 CIDR masks."));
201
202         for (int i = 0; i < 16; i++) {
203                 int lbits = std::max(0, *bits - i * 8);
204
205                 if (lbits >= 8)
206                         continue;
207
208                 if (mask[i] & (0xff >> lbits))
209                         BOOST_THROW_EXCEPTION(std::invalid_argument("Masked-off bits must all be zero."));
210         }
211 }
212
213 static bool IpMaskCheck(char addr[16], char mask[16], int bits)
214 {
215         for (int i = 0; i < 16; i++) {
216                 if (bits < 8)
217                         return !((addr[i] ^ mask[i]) >> (8 - bits));
218
219                 if (mask[i] != addr[i])
220                         return false;
221
222                 bits -= 8;
223
224                 if (bits == 0)
225                         return true;
226         }
227
228         return true;
229 }
230
231 bool Utility::CidrMatch(const String& pattern, const String& ip)
232 {
233         char mask[16];
234         int bits;
235
236         ParseIpMask(pattern, mask, &bits);
237
238         char addr[16];
239         int proto;
240
241         if (!ParseIp(ip, addr, &proto))
242                 return false;
243
244         return IpMaskCheck(addr, mask, bits);
245 }
246
247 /**
248  * Returns the directory component of a path. See dirname(3) for details.
249  *
250  * @param path The full path.
251  * @returns The directory.
252  */
253 String Utility::DirName(const String& path)
254 {
255         return boost::filesystem::path(path.Begin(), path.End()).parent_path().string();
256 }
257
258 /**
259  * Returns the file component of a path. See basename(3) for details.
260  *
261  * @param path The full path.
262  * @returns The filename.
263  */
264 String Utility::BaseName(const String& path)
265 {
266         return boost::filesystem::path(path.Begin(), path.End()).filename().string();
267 }
268
269 /**
270  * Null deleter. Used as a parameter for the shared_ptr constructor.
271  *
272  * @param - The object that should be deleted.
273  */
274 void Utility::NullDeleter(void *)
275 {
276         /* Nothing to do here. */
277 }
278
279 #ifdef I2_DEBUG
280 /**
281  * (DEBUG / TESTING ONLY) Sets the current system time to a static value,
282  * that will be be retrieved by any component of Icinga, when using GetTime().
283  *
284  * This should be only used for testing purposes, e.g. unit tests and debugging of certain functionalities.
285  */
286 void Utility::SetTime(double time)
287 {
288         m_DebugTime = time;
289 }
290
291 /**
292  * (DEBUG / TESTING ONLY) Increases the set debug system time by X seconds.
293  *
294  * This should be only used for testing purposes, e.g. unit tests and debugging of certain functionalities.
295  */
296 void Utility::IncrementTime(double diff)
297 {
298         m_DebugTime += diff;
299 }
300 #endif /* I2_DEBUG */
301
302 /**
303  * Returns the current UNIX timestamp including fractions of seconds.
304  *
305  * @returns The current time.
306  */
307 double Utility::GetTime()
308 {
309 #ifdef I2_DEBUG
310         if (m_DebugTime >= 0) {
311                 // (DEBUG / TESTING ONLY) this will return a *STATIC* system time, if the value has been set!
312                 return m_DebugTime;
313         }
314 #endif /* I2_DEBUG */
315 #ifdef _WIN32
316         FILETIME cft;
317         GetSystemTimeAsFileTime(&cft);
318
319         ULARGE_INTEGER ucft;
320         ucft.HighPart = cft.dwHighDateTime;
321         ucft.LowPart = cft.dwLowDateTime;
322
323         SYSTEMTIME est = { 1970, 1, 4, 1, 0, 0, 0, 0};
324         FILETIME eft;
325         SystemTimeToFileTime(&est, &eft);
326
327         ULARGE_INTEGER ueft;
328         ueft.HighPart = eft.dwHighDateTime;
329         ueft.LowPart = eft.dwLowDateTime;
330
331         return ((ucft.QuadPart - ueft.QuadPart) / 10000) / 1000.0;
332 #else /* _WIN32 */
333         struct timeval tv;
334
335         int rc = gettimeofday(&tv, nullptr);
336         VERIFY(rc >= 0);
337
338         return tv.tv_sec + tv.tv_usec / 1000000.0;
339 #endif /* _WIN32 */
340 }
341
342 /**
343  * Returns the ID of the current process.
344  *
345  * @returns The PID.
346  */
347 pid_t Utility::GetPid()
348 {
349 #ifndef _WIN32
350         return getpid();
351 #else /* _WIN32 */
352         return GetCurrentProcessId();
353 #endif /* _WIN32 */
354 }
355
356 /**
357  * Sleeps for the specified amount of time.
358  *
359  * @param timeout The timeout in seconds.
360  */
361 void Utility::Sleep(double timeout)
362 {
363 #ifndef _WIN32
364         unsigned long micros = timeout * 1000000u;
365         if (timeout >= 1.0)
366                 sleep((unsigned)timeout);
367
368         usleep(micros % 1000000u);
369 #else /* _WIN32 */
370         ::Sleep(timeout * 1000);
371 #endif /* _WIN32 */
372 }
373
374 /**
375  * Generates a new unique ID.
376  *
377  * @returns The new unique ID.
378  */
379 String Utility::NewUniqueID()
380 {
381         return boost::lexical_cast<std::string>(boost::uuids::random_generator()());
382 }
383
384 #ifdef _WIN32
385 static bool GlobHelper(const String& pathSpec, int type, std::vector<String>& files, std::vector<String>& dirs)
386 {
387         HANDLE handle;
388         WIN32_FIND_DATA wfd;
389
390         handle = FindFirstFile(pathSpec.CStr(), &wfd);
391
392         if (handle == INVALID_HANDLE_VALUE) {
393                 DWORD errorCode = GetLastError();
394
395                 if (errorCode == ERROR_FILE_NOT_FOUND)
396                         return false;
397
398                 BOOST_THROW_EXCEPTION(win32_error()
399                         << boost::errinfo_api_function("FindFirstFile")
400                         << errinfo_win32_error(errorCode)
401                         << boost::errinfo_file_name(pathSpec));
402         }
403
404         do {
405                 if (strcmp(wfd.cFileName, ".") == 0 || strcmp(wfd.cFileName, "..") == 0)
406                         continue;
407
408                 String path = Utility::DirName(pathSpec) + "/" + wfd.cFileName;
409
410                 if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (type & GlobDirectory))
411                         dirs.push_back(path);
412                 else if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (type & GlobFile))
413                         files.push_back(path);
414         } while (FindNextFile(handle, &wfd));
415
416         if (!FindClose(handle)) {
417                 BOOST_THROW_EXCEPTION(win32_error()
418                         << boost::errinfo_api_function("FindClose")
419                         << errinfo_win32_error(GetLastError()));
420         }
421
422         return true;
423 }
424 #endif /* _WIN32 */
425
426 #ifndef _WIN32
427 static int GlobErrorHandler(const char *epath, int eerrno)
428 {
429         if (eerrno == ENOTDIR)
430                 return 0;
431
432         return eerrno;
433 }
434 #endif /* _WIN32 */
435
436 /**
437  * Calls the specified callback for each file matching the path specification.
438  *
439  * @param pathSpec The path specification.
440  * @param callback The callback which is invoked for each matching file.
441  * @param type The file type (a combination of GlobFile and GlobDirectory)
442  */
443 bool Utility::Glob(const String& pathSpec, const std::function<void (const String&)>& callback, int type)
444 {
445         std::vector<String> files, dirs;
446
447 #ifdef _WIN32
448         std::vector<String> tokens = pathSpec.Split("\\/");
449
450         String part1;
451
452         for (std::vector<String>::size_type i = 0; i < tokens.size() - 1; i++) {
453                 const String& token = tokens[i];
454
455                 if (!part1.IsEmpty())
456                         part1 += "/";
457
458                 part1 += token;
459
460                 if (token.FindFirstOf("?*") != String::NPos) {
461                         String part2;
462
463                         for (std::vector<String>::size_type k = i + 1; k < tokens.size(); k++) {
464                                 if (!part2.IsEmpty())
465                                         part2 += "/";
466
467                                 part2 += tokens[k];
468                         }
469
470                         std::vector<String> files2, dirs2;
471
472                         if (!GlobHelper(part1, GlobDirectory, files2, dirs2))
473                                 return false;
474
475                         for (const String& dir : dirs2) {
476                                 if (!Utility::Glob(dir + "/" + part2, callback, type))
477                                         return false;
478                         }
479
480                         return true;
481                 }
482         }
483
484         if (!GlobHelper(part1 + "/" + tokens[tokens.size() - 1], type, files, dirs))
485                 return false;
486 #else /* _WIN32 */
487         glob_t gr;
488
489         int rc = glob(pathSpec.CStr(), GLOB_NOSORT, GlobErrorHandler, &gr);
490
491         if (rc) {
492                 if (rc == GLOB_NOMATCH)
493                         return false;
494
495                 BOOST_THROW_EXCEPTION(posix_error()
496                         << boost::errinfo_api_function("glob")
497                         << boost::errinfo_errno(errno)
498                         << boost::errinfo_file_name(pathSpec));
499         }
500
501         if (gr.gl_pathc == 0) {
502                 globfree(&gr);
503                 return false;
504         }
505
506         size_t left;
507         char **gp;
508         for (gp = gr.gl_pathv, left = gr.gl_pathc; left > 0; gp++, left--) {
509                 struct stat statbuf;
510
511                 if (stat(*gp, &statbuf) < 0)
512                         continue;
513
514                 if (!S_ISDIR(statbuf.st_mode) && !S_ISREG(statbuf.st_mode))
515                         continue;
516
517                 if (S_ISDIR(statbuf.st_mode) && (type & GlobDirectory))
518                         dirs.emplace_back(*gp);
519                 else if (!S_ISDIR(statbuf.st_mode) && (type & GlobFile))
520                         files.emplace_back(*gp);
521         }
522
523         globfree(&gr);
524 #endif /* _WIN32 */
525
526         std::sort(files.begin(), files.end());
527         for (const String& cpath : files) {
528                 callback(cpath);
529         }
530
531         std::sort(dirs.begin(), dirs.end());
532         for (const String& cpath : dirs) {
533                 callback(cpath);
534         }
535
536         return true;
537 }
538
539 /**
540  * Calls the specified callback for each file in the specified directory
541  * or any of its child directories if the file name matches the specified
542  * pattern.
543  *
544  * @param path The path.
545  * @param pattern The pattern.
546  * @param callback The callback which is invoked for each matching file.
547  * @param type The file type (a combination of GlobFile and GlobDirectory)
548  */
549 bool Utility::GlobRecursive(const String& path, const String& pattern, const std::function<void (const String&)>& callback, int type)
550 {
551         std::vector<String> files, dirs, alldirs;
552
553 #ifdef _WIN32
554         HANDLE handle;
555         WIN32_FIND_DATA wfd;
556
557         String pathSpec = path + "/*";
558
559         handle = FindFirstFile(pathSpec.CStr(), &wfd);
560
561         if (handle == INVALID_HANDLE_VALUE) {
562                 DWORD errorCode = GetLastError();
563
564                 if (errorCode == ERROR_FILE_NOT_FOUND)
565                         return false;
566
567                 BOOST_THROW_EXCEPTION(win32_error()
568                         << boost::errinfo_api_function("FindFirstFile")
569                         << errinfo_win32_error(errorCode)
570                         << boost::errinfo_file_name(pathSpec));
571         }
572
573         do {
574                 if (strcmp(wfd.cFileName, ".") == 0 || strcmp(wfd.cFileName, "..") == 0)
575                         continue;
576
577                 String cpath = path + "/" + wfd.cFileName;
578
579                 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
580                         alldirs.push_back(cpath);
581
582                 if (!Utility::Match(pattern, wfd.cFileName))
583                         continue;
584
585                 if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (type & GlobFile))
586                         files.push_back(cpath);
587
588                 if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (type & GlobDirectory))
589                         dirs.push_back(cpath);
590         } while (FindNextFile(handle, &wfd));
591
592         if (!FindClose(handle)) {
593                 BOOST_THROW_EXCEPTION(win32_error()
594                         << boost::errinfo_api_function("FindClose")
595                         << errinfo_win32_error(GetLastError()));
596         }
597 #else /* _WIN32 */
598         DIR *dirp;
599
600         dirp = opendir(path.CStr());
601
602         if (!dirp)
603                 BOOST_THROW_EXCEPTION(posix_error()
604                         << boost::errinfo_api_function("opendir")
605                         << boost::errinfo_errno(errno)
606                         << boost::errinfo_file_name(path));
607
608         while (dirp) {
609                 dirent *pent;
610
611                 errno = 0;
612                 pent = readdir(dirp);
613                 if (!pent && errno != 0) {
614                         closedir(dirp);
615
616                         BOOST_THROW_EXCEPTION(posix_error()
617                                 << boost::errinfo_api_function("readdir")
618                                 << boost::errinfo_errno(errno)
619                                 << boost::errinfo_file_name(path));
620                 }
621
622                 if (!pent)
623                         break;
624
625                 if (strcmp(pent->d_name, ".") == 0 || strcmp(pent->d_name, "..") == 0)
626                         continue;
627
628                 String cpath = path + "/" + pent->d_name;
629
630                 struct stat statbuf;
631
632                 if (stat(cpath.CStr(), &statbuf) < 0)
633                         continue;
634
635                 if (S_ISDIR(statbuf.st_mode))
636                         alldirs.push_back(cpath);
637
638                 if (!Utility::Match(pattern, pent->d_name))
639                         continue;
640
641                 if (S_ISDIR(statbuf.st_mode) && (type & GlobDirectory))
642                         dirs.push_back(cpath);
643
644                 if (!S_ISDIR(statbuf.st_mode) && (type & GlobFile))
645                         files.push_back(cpath);
646         }
647
648         closedir(dirp);
649
650 #endif /* _WIN32 */
651
652         std::sort(files.begin(), files.end());
653         for (const String& cpath : files) {
654                 callback(cpath);
655         }
656
657         std::sort(dirs.begin(), dirs.end());
658         for (const String& cpath : dirs) {
659                 callback(cpath);
660         }
661
662         std::sort(alldirs.begin(), alldirs.end());
663         for (const String& cpath : alldirs) {
664                 GlobRecursive(cpath, pattern, callback, type);
665         }
666
667         return true;
668 }
669
670
671 void Utility::MkDir(const String& path, int mode)
672 {
673
674 #ifndef _WIN32
675         if (mkdir(path.CStr(), mode) < 0 && errno != EEXIST) {
676 #else /*_ WIN32 */
677         if (mkdir(path.CStr()) < 0 && errno != EEXIST) {
678 #endif /* _WIN32 */
679
680                 BOOST_THROW_EXCEPTION(posix_error()
681                         << boost::errinfo_api_function("mkdir")
682                         << boost::errinfo_errno(errno)
683                         << boost::errinfo_file_name(path));
684         }
685 }
686
687 void Utility::MkDirP(const String& path, int mode)
688 {
689         size_t pos = 0;
690
691         while (pos != String::NPos) {
692 #ifndef _WIN32
693                 pos = path.Find("/", pos + 1);
694 #else /*_ WIN32 */
695                 pos = path.FindFirstOf("/\\", pos + 1);
696 #endif /* _WIN32 */
697
698                 String spath = path.SubStr(0, pos + 1);
699                 struct stat statbuf;
700                 if (stat(spath.CStr(), &statbuf) < 0 && errno == ENOENT)
701                         MkDir(path.SubStr(0, pos), mode);
702         }
703 }
704
705 void Utility::Remove(const String& path)
706 {
707         namespace fs = boost::filesystem;
708
709         (void)fs::remove(fs::path(path.Begin(), path.End()));
710 }
711
712 void Utility::RemoveDirRecursive(const String& path)
713 {
714         namespace fs = boost::filesystem;
715
716         (void)fs::remove_all(fs::path(path.Begin(), path.End()));
717 }
718
719 /*
720  * Copies a source file to a target location.
721  * Caller must ensure that the target's base directory exists and is writable.
722  */
723 void Utility::CopyFile(const String& source, const String& target)
724 {
725         namespace fs = boost::filesystem;
726
727         fs::copy_file(fs::path(source.Begin(), source.End()), fs::path(target.Begin(), target.End()), fs::copy_option::overwrite_if_exists);
728 }
729
730 /*
731  * Renames a source file to a target location.
732  * Caller must ensure that the target's base directory exists and is writable.
733  */
734 void Utility::RenameFile(const String& source, const String& target)
735 {
736         namespace fs = boost::filesystem;
737
738         fs::rename(fs::path(source.Begin(), source.End()), fs::path(target.Begin(), target.End()));
739 }
740
741 /*
742  * Set file permissions
743  */
744 bool Utility::SetFileOwnership(const String& file, const String& user, const String& group)
745 {
746 #ifndef _WIN32
747         errno = 0;
748         struct passwd *pw = getpwnam(user.CStr());
749
750         if (!pw) {
751                 if (errno == 0) {
752                         Log(LogCritical, "cli")
753                                 << "Invalid user specified: " << user;
754                         return false;
755                 } else {
756                         Log(LogCritical, "cli")
757                                 << "getpwnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
758                         return false;
759                 }
760         }
761
762         errno = 0;
763         struct group *gr = getgrnam(group.CStr());
764
765         if (!gr) {
766                 if (errno == 0) {
767                         Log(LogCritical, "cli")
768                                 << "Invalid group specified: " << group;
769                         return false;
770                 } else {
771                         Log(LogCritical, "cli")
772                                 << "getgrnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
773                         return false;
774                 }
775         }
776
777         if (chown(file.CStr(), pw->pw_uid, gr->gr_gid) < 0) {
778                 Log(LogCritical, "cli")
779                         << "chown() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
780                 return false;
781         }
782 #endif /* _WIN32 */
783
784         return true;
785 }
786
787 #ifndef _WIN32
788 void Utility::SetNonBlocking(int fd, bool nb)
789 {
790         int flags = fcntl(fd, F_GETFL, 0);
791
792         if (flags < 0) {
793                 BOOST_THROW_EXCEPTION(posix_error()
794                         << boost::errinfo_api_function("fcntl")
795                         << boost::errinfo_errno(errno));
796         }
797
798         if (nb)
799                 flags |= O_NONBLOCK;
800         else
801                 flags &= ~O_NONBLOCK;
802
803         if (fcntl(fd, F_SETFL, flags) < 0) {
804                 BOOST_THROW_EXCEPTION(posix_error()
805                         << boost::errinfo_api_function("fcntl")
806                         << boost::errinfo_errno(errno));
807         }
808 }
809
810 void Utility::SetCloExec(int fd, bool cloexec)
811 {
812         int flags = fcntl(fd, F_GETFD, 0);
813
814         if (flags < 0) {
815                 BOOST_THROW_EXCEPTION(posix_error()
816                         << boost::errinfo_api_function("fcntl")
817                         << boost::errinfo_errno(errno));
818         }
819
820         if (cloexec)
821                 flags |= FD_CLOEXEC;
822         else
823                 flags &= ~FD_CLOEXEC;
824
825         if (fcntl(fd, F_SETFD, flags) < 0) {
826                 BOOST_THROW_EXCEPTION(posix_error()
827                         << boost::errinfo_api_function("fcntl")
828                         << boost::errinfo_errno(errno));
829         }
830 }
831 #endif /* _WIN32 */
832
833 void Utility::SetNonBlockingSocket(SOCKET s, bool nb)
834 {
835 #ifndef _WIN32
836         SetNonBlocking(s, nb);
837 #else /* _WIN32 */
838         unsigned long lflag = nb;
839         ioctlsocket(s, FIONBIO, &lflag);
840 #endif /* _WIN32 */
841 }
842
843 void Utility::QueueAsyncCallback(const std::function<void ()>& callback, SchedulerPolicy policy)
844 {
845         Application::GetTP().Post(callback, policy);
846 }
847
848 String Utility::NaturalJoin(const std::vector<String>& tokens)
849 {
850         String result;
851
852         for (std::vector<String>::size_type i = 0; i < tokens.size(); i++) {
853                 result += tokens[i];
854
855                 if (tokens.size() > i + 1) {
856                         if (i < tokens.size() - 2)
857                                 result += ", ";
858                         else if (i == tokens.size() - 2)
859                                 result += " and ";
860                 }
861         }
862
863         return result;
864 }
865
866 String Utility::Join(const Array::Ptr& tokens, char separator, bool escapeSeparator)
867 {
868         String result;
869         bool first = true;
870
871         ObjectLock olock(tokens);
872         for (const Value& vtoken : tokens) {
873                 String token = Convert::ToString(vtoken);
874
875                 if (escapeSeparator) {
876                         boost::algorithm::replace_all(token, "\\", "\\\\");
877
878                         char sep_before[2], sep_after[3];
879                         sep_before[0] = separator;
880                         sep_before[1] = '\0';
881                         sep_after[0] = '\\';
882                         sep_after[1] = separator;
883                         sep_after[2] = '\0';
884                         boost::algorithm::replace_all(token, sep_before, sep_after);
885                 }
886
887                 if (first)
888                         first = false;
889                 else
890                         result += String(1, separator);
891
892                 result += token;
893         }
894
895         return result;
896 }
897
898 String Utility::FormatDuration(double duration)
899 {
900         std::vector<String> tokens;
901         String result;
902
903         if (duration >= 86400) {
904                 int days = duration / 86400;
905                 tokens.emplace_back(Convert::ToString(days) + (days != 1 ? " days" : " day"));
906                 duration = static_cast<int>(duration) % 86400;
907         }
908
909         if (duration >= 3600) {
910                 int hours = duration / 3600;
911                 tokens.emplace_back(Convert::ToString(hours) + (hours != 1 ? " hours" : " hour"));
912                 duration = static_cast<int>(duration) % 3600;
913         }
914
915         if (duration >= 60) {
916                 int minutes = duration / 60;
917                 tokens.emplace_back(Convert::ToString(minutes) + (minutes != 1 ? " minutes" : " minute"));
918                 duration = static_cast<int>(duration) % 60;
919         }
920
921         if (duration >= 1) {
922                 int seconds = duration;
923                 tokens.emplace_back(Convert::ToString(seconds) + (seconds != 1 ? " seconds" : " second"));
924         }
925
926         if (tokens.size() == 0) {
927                 int milliseconds = std::floor(duration * 1000);
928                 if (milliseconds >= 1)
929                         tokens.emplace_back(Convert::ToString(milliseconds) + (milliseconds != 1 ? " milliseconds" : " millisecond"));
930                 else
931                         tokens.emplace_back("less than 1 millisecond");
932         }
933
934         return NaturalJoin(tokens);
935 }
936
937 String Utility::FormatDateTime(const char *format, double ts)
938 {
939         char timestamp[128];
940         auto tempts = (time_t)ts; /* We don't handle sub-second timestamps here just yet. */
941         tm tmthen;
942
943 #ifdef _MSC_VER
944         tm *temp = localtime(&tempts);
945
946         if (!temp) {
947                 BOOST_THROW_EXCEPTION(posix_error()
948                         << boost::errinfo_api_function("localtime")
949                         << boost::errinfo_errno(errno));
950         }
951
952         tmthen = *temp;
953 #else /* _MSC_VER */
954         if (!localtime_r(&tempts, &tmthen)) {
955                 BOOST_THROW_EXCEPTION(posix_error()
956                         << boost::errinfo_api_function("localtime_r")
957                         << boost::errinfo_errno(errno));
958         }
959 #endif /* _MSC_VER */
960
961         strftime(timestamp, sizeof(timestamp), format, &tmthen);
962
963         return timestamp;
964 }
965
966 String Utility::FormatErrorNumber(int code) {
967         std::ostringstream msgbuf;
968
969 #ifdef _WIN32
970         char *message;
971         String result = "Unknown error.";
972
973         DWORD rc = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
974                 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, code, 0, (char *)&message,
975                 0, nullptr);
976
977         if (rc != 0) {
978                 result = String(message);
979                 LocalFree(message);
980
981                 /* remove trailing new-line characters */
982                 boost::algorithm::trim_right(result);
983         }
984
985         msgbuf << code << ", \"" << result << "\"";
986 #else
987         msgbuf << strerror(code);
988 #endif
989         return msgbuf.str();
990 }
991
992 String Utility::EscapeShellCmd(const String& s)
993 {
994         String result;
995         size_t prev_quote = String::NPos;
996         int index = -1;
997
998         for (char ch : s) {
999                 bool escape = false;
1000
1001                 index++;
1002
1003 #ifdef _WIN32
1004                 if (ch == '%' || ch == '"' || ch == '\'')
1005                         escape = true;
1006 #else /* _WIN32 */
1007                 if (ch == '"' || ch == '\'') {
1008                         /* Find a matching closing quotation character. */
1009                         if (prev_quote == String::NPos && (prev_quote = s.FindFirstOf(ch, index + 1)) != String::NPos)
1010                                 ; /* Empty statement. */
1011                         else if (prev_quote != String::NPos && s[prev_quote] == ch)
1012                                 prev_quote = String::NPos;
1013                         else
1014                                 escape = true;
1015                 }
1016 #endif /* _WIN32 */
1017
1018                 if (ch == '#' || ch == '&' || ch == ';' || ch == '`' || ch == '|' ||
1019                         ch == '*' || ch == '?' || ch == '~' || ch == '<' || ch == '>' ||
1020                         ch == '^' || ch == '(' || ch == ')' || ch == '[' || ch == ']' ||
1021                         ch == '{' || ch == '}' || ch == '$' || ch == '\\' || ch == '\x0A' ||
1022                         ch == '\xFF')
1023                         escape = true;
1024
1025                 if (escape)
1026 #ifdef _WIN32
1027                         result += '^';
1028 #else /* _WIN32 */
1029                         result += '\\';
1030 #endif /* _WIN32 */
1031
1032                 result += ch;
1033         }
1034
1035         return result;
1036 }
1037
1038 String Utility::EscapeShellArg(const String& s)
1039 {
1040         String result;
1041
1042 #ifdef _WIN32
1043         result = "\"";
1044 #else /* _WIN32 */
1045         result = "'";
1046 #endif /* _WIN32 */
1047
1048         for (char ch : s) {
1049 #ifdef _WIN32
1050                 if (ch == '"' || ch == '%') {
1051                         result += ' ';
1052                 }
1053 #else /* _WIN32 */
1054                 if (ch == '\'')
1055                         result += "'\\'";
1056 #endif
1057                 result += ch;
1058         }
1059
1060 #ifdef _WIN32
1061         result += '"';
1062 #else /* _WIN32 */
1063         result += '\'';
1064 #endif /* _WIN32 */
1065
1066         return result;
1067 }
1068
1069 #ifdef _WIN32
1070 String Utility::EscapeCreateProcessArg(const String& arg)
1071 {
1072         if (arg.FindFirstOf(" \t\n\v\"") == String::NPos)
1073                 return arg;
1074
1075         String result = "\"";
1076
1077         for (String::ConstIterator it = arg.Begin(); ; it++) {
1078                 int numBackslashes = 0;
1079
1080                 while (it != arg.End() && *it == '\\') {
1081                         it++;
1082                         numBackslashes++;
1083                 }
1084
1085                 if (it == arg.End()) {
1086                         result.Append(numBackslashes * 2, '\\');
1087                         break;
1088                 } else if (*it == '"') {
1089                         result.Append(numBackslashes * 2, '\\');
1090                         result.Append(1, *it);
1091                 } else {
1092                         result.Append(numBackslashes, '\\');
1093                         result.Append(1, *it);
1094                 }
1095         }
1096
1097         result += "\"";
1098
1099         return result;
1100 }
1101 #endif /* _WIN32 */
1102
1103 #ifdef _WIN32
1104 static void WindowsSetThreadName(const char *name)
1105 {
1106         THREADNAME_INFO info;
1107         info.dwType = 0x1000;
1108         info.szName = name;
1109         info.dwThreadID = -1;
1110         info.dwFlags = 0;
1111
1112         __try {
1113                 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info);
1114         } __except(EXCEPTION_EXECUTE_HANDLER) {
1115                 /* Nothing to do here. */
1116         }
1117 }
1118 #endif /* _WIN32 */
1119
1120 void Utility::SetThreadName(const String& name, bool os)
1121 {
1122         m_ThreadName.reset(new String(name));
1123
1124         if (!os)
1125                 return;
1126
1127 #ifdef _WIN32
1128         WindowsSetThreadName(name.CStr());
1129 #endif /* _WIN32 */
1130
1131 #ifdef HAVE_PTHREAD_SET_NAME_NP
1132         pthread_set_name_np(pthread_self(), name.CStr());
1133 #endif /* HAVE_PTHREAD_SET_NAME_NP */
1134
1135 #ifdef HAVE_PTHREAD_SETNAME_NP
1136 #       ifdef __APPLE__
1137         pthread_setname_np(name.CStr());
1138 #       else /* __APPLE__ */
1139         String tname = name.SubStr(0, 15);
1140         pthread_setname_np(pthread_self(), tname.CStr());
1141 #       endif /* __APPLE__ */
1142 #endif /* HAVE_PTHREAD_SETNAME_NP */
1143 }
1144
1145 String Utility::GetThreadName()
1146 {
1147         String *name = m_ThreadName.get();
1148
1149         if (!name) {
1150                 std::ostringstream idbuf;
1151                 idbuf << std::this_thread::get_id();
1152                 return idbuf.str();
1153         }
1154
1155         return *name;
1156 }
1157
1158 unsigned long Utility::SDBM(const String& str, size_t len)
1159 {
1160         unsigned long hash = 0;
1161         size_t current = 0;
1162
1163         for (char c : str) {
1164                 if (current >= len)
1165                         break;
1166
1167                 hash = c + (hash << 6) + (hash << 16) - hash;
1168
1169                 current++;
1170         }
1171
1172         return hash;
1173 }
1174
1175 int Utility::CompareVersion(const String& v1, const String& v2)
1176 {
1177         std::vector<String> tokensv1 = v1.Split(".");
1178         std::vector<String> tokensv2 = v2.Split(".");
1179
1180         for (std::vector<String>::size_type i = 0; i < tokensv2.size() - tokensv1.size(); i++)
1181                 tokensv1.emplace_back("0");
1182
1183         for (std::vector<String>::size_type i = 0; i < tokensv1.size() - tokensv2.size(); i++)
1184                 tokensv2.emplace_back("0");
1185
1186         for (std::vector<String>::size_type i = 0; i < tokensv1.size(); i++) {
1187                 if (Convert::ToLong(tokensv2[i]) > Convert::ToLong(tokensv1[i]))
1188                         return 1;
1189                 else if (Convert::ToLong(tokensv2[i]) < Convert::ToLong(tokensv1[i]))
1190                         return -1;
1191         }
1192
1193         return 0;
1194 }
1195
1196 String Utility::GetHostName()
1197 {
1198         char name[255];
1199
1200         if (gethostname(name, sizeof(name)) < 0)
1201                 return "localhost";
1202
1203         return name;
1204 }
1205
1206 /**
1207  * Returns the fully-qualified domain name for the host
1208  * we're running on.
1209  *
1210  * @returns The FQDN.
1211  */
1212 String Utility::GetFQDN()
1213 {
1214         String hostname = GetHostName();
1215
1216         addrinfo hints;
1217         memset(&hints, 0, sizeof(hints));
1218         hints.ai_family = AF_UNSPEC;
1219         hints.ai_socktype = SOCK_DGRAM;
1220         hints.ai_flags = AI_CANONNAME;
1221
1222         addrinfo *result;
1223         int rc = getaddrinfo(hostname.CStr(), nullptr, &hints, &result);
1224
1225         if (rc != 0)
1226                 result = nullptr;
1227
1228         if (result) {
1229                 if (strcmp(result->ai_canonname, "localhost") != 0)
1230                         hostname = result->ai_canonname;
1231
1232                 freeaddrinfo(result);
1233         }
1234
1235         return hostname;
1236 }
1237
1238 int Utility::Random()
1239 {
1240 #ifdef _WIN32
1241         return rand();
1242 #else /* _WIN32 */
1243         unsigned int *seed = m_RandSeed.get();
1244
1245         if (!seed) {
1246                 seed = new unsigned int(Utility::GetTime());
1247                 m_RandSeed.reset(seed);
1248         }
1249
1250         return rand_r(seed);
1251 #endif /* _WIN32 */
1252 }
1253
1254 tm Utility::LocalTime(time_t ts)
1255 {
1256 #ifdef _MSC_VER
1257         tm *result = localtime(&ts);
1258
1259         if (!result) {
1260                 BOOST_THROW_EXCEPTION(posix_error()
1261                         << boost::errinfo_api_function("localtime")
1262                         << boost::errinfo_errno(errno));
1263         }
1264
1265         return *result;
1266 #else /* _MSC_VER */
1267         tm result;
1268
1269         if (!localtime_r(&ts, &result)) {
1270                 BOOST_THROW_EXCEPTION(posix_error()
1271                         << boost::errinfo_api_function("localtime_r")
1272                         << boost::errinfo_errno(errno));
1273         }
1274
1275         return result;
1276 #endif /* _MSC_VER */
1277 }
1278
1279 bool Utility::PathExists(const String& path)
1280 {
1281         namespace fs = boost::filesystem;
1282
1283         boost::system::error_code ec;
1284
1285         return fs::exists(fs::path(path.Begin(), path.End()), ec) && !ec;
1286 }
1287
1288 Value Utility::LoadJsonFile(const String& path)
1289 {
1290         std::ifstream fp;
1291         fp.open(path.CStr());
1292
1293         String json((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
1294
1295         fp.close();
1296
1297         if (fp.fail())
1298                 BOOST_THROW_EXCEPTION(std::runtime_error("Could not read JSON file '" + path + "'."));
1299
1300         return JsonDecode(json);
1301 }
1302
1303 void Utility::SaveJsonFile(const String& path, int mode, const Value& value)
1304 {
1305         namespace fs = boost::filesystem;
1306
1307         std::fstream fp;
1308         String tempFilename = Utility::CreateTempFile(path + ".XXXXXX", mode, fp);
1309
1310         fp.exceptions(std::ofstream::failbit | std::ofstream::badbit);
1311         fp << JsonEncode(value);
1312         fp.close();
1313
1314         RenameFile(tempFilename, path);
1315 }
1316
1317 static void HexEncode(char ch, std::ostream& os)
1318 {
1319         const char *hex_chars = "0123456789ABCDEF";
1320
1321         os << hex_chars[ch >> 4 & 0x0f];
1322         os << hex_chars[ch & 0x0f];
1323 }
1324
1325 static int HexDecode(char hc)
1326 {
1327         if (hc >= '0' && hc <= '9')
1328                 return hc - '0';
1329         else if (hc >= 'a' && hc <= 'f')
1330                 return hc - 'a' + 10;
1331         else if (hc >= 'A' && hc <= 'F')
1332                 return hc - 'A' + 10;
1333         else
1334                 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid hex character."));
1335 }
1336
1337 String Utility::EscapeString(const String& s, const String& chars, const bool illegal)
1338 {
1339         std::ostringstream result;
1340         if (illegal) {
1341                 for (char ch : s) {
1342                         if (chars.FindFirstOf(ch) != String::NPos || ch == '%') {
1343                                 result << '%';
1344                                 HexEncode(ch, result);
1345                         } else
1346                                 result << ch;
1347                 }
1348         } else {
1349                 for (char ch : s) {
1350                         if (chars.FindFirstOf(ch) == String::NPos || ch == '%') {
1351                                 result << '%';
1352                                 HexEncode(ch, result);
1353                         } else
1354                                 result << ch;
1355                 }
1356         }
1357
1358         return result.str();
1359 }
1360
1361 String Utility::UnescapeString(const String& s)
1362 {
1363         std::ostringstream result;
1364
1365         for (String::SizeType i = 0; i < s.GetLength(); i++) {
1366                 if (s[i] == '%') {
1367                         if (i + 2 > s.GetLength() - 1)
1368                                 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid escape sequence."));
1369
1370                         char ch = HexDecode(s[i + 1]) * 16 + HexDecode(s[i + 2]);
1371                         result << ch;
1372
1373                         i += 2;
1374                 } else
1375                         result << s[i];
1376         }
1377
1378         return result.str();
1379 }
1380
1381 #ifndef _WIN32
1382 static String UnameHelper(char type)
1383 {
1384         struct utsname name;
1385         uname(&name);
1386
1387         switch (type) {
1388                 case 'm':
1389                         return (char*)name.machine;
1390                 case 'n':
1391                         return (char*)name.nodename;
1392                 case 'r':
1393                         return (char*)name.release;
1394                 case 's':
1395                         return (char*)name.sysname;
1396                 case 'v':
1397                         return (char*)name.version;
1398                 default:
1399                         VERIFY(!"Invalid uname query.");
1400         }
1401 }
1402 #endif /* _WIN32 */
1403 static bool ReleaseHelper(String *platformName, String *platformVersion)
1404 {
1405 #ifdef _WIN32
1406         if (platformName)
1407                 *platformName = "Windows";
1408
1409         if (platformVersion) {
1410                 *platformVersion = "Vista";
1411                 if (IsWindowsVistaSP1OrGreater())
1412                         *platformVersion = "Vista SP1";
1413                 if (IsWindowsVistaSP2OrGreater())
1414                         *platformVersion = "Vista SP2";
1415                 if (IsWindows7OrGreater())
1416                         *platformVersion = "7";
1417                 if (IsWindows7SP1OrGreater())
1418                         *platformVersion = "7 SP1";
1419                 if (IsWindows8OrGreater())
1420                         *platformVersion = "8";
1421                 if (IsWindows8Point1OrGreater())
1422                         *platformVersion = "8.1 or greater";
1423                 if (IsWindowsServer())
1424                         *platformVersion += " (Server)";
1425         }
1426
1427         return true;
1428 #else /* _WIN32 */
1429         if (platformName)
1430                 *platformName = "Unknown";
1431
1432         if (platformVersion)
1433                 *platformVersion = "Unknown";
1434
1435         /* You have systemd or Ubuntu etc. */
1436         std::ifstream release("/etc/os-release");
1437         if (release.is_open()) {
1438                 std::string release_line;
1439                 while (getline(release, release_line)) {
1440                         std::string::size_type pos = release_line.find("=");
1441
1442                         if (pos == std::string::npos)
1443                                 continue;
1444
1445                         std::string key = release_line.substr(0, pos);
1446                         std::string value = release_line.substr(pos + 1);
1447
1448                         std::string::size_type firstQuote = value.find("\"");
1449
1450                         if (firstQuote != std::string::npos)
1451                                 value.erase(0, firstQuote + 1);
1452
1453                         std::string::size_type lastQuote = value.rfind("\"");
1454
1455                         if (lastQuote != std::string::npos)
1456                                 value.erase(lastQuote);
1457
1458                         if (platformName && key == "NAME")
1459                                 *platformName = value;
1460
1461                         if (platformVersion && key == "VERSION")
1462                                 *platformVersion = value;
1463                 }
1464
1465                 return true;
1466         }
1467
1468         /* You are using a distribution which supports LSB. */
1469         FILE *fp = popen("type lsb_release >/dev/null 2>&1 && lsb_release -s -i 2>&1", "r");
1470
1471         if (fp) {
1472                 std::ostringstream msgbuf;
1473                 char line[1024];
1474                 while (fgets(line, sizeof(line), fp))
1475                         msgbuf << line;
1476                 int status = pclose(fp);
1477                 if (WEXITSTATUS(status) == 0) {
1478                         if (platformName)
1479                                 *platformName = msgbuf.str();
1480                 }
1481         }
1482
1483         fp = popen("type lsb_release >/dev/null 2>&1 && lsb_release -s -r 2>&1", "r");
1484
1485         if (fp) {
1486                 std::ostringstream msgbuf;
1487                 char line[1024];
1488                 while (fgets(line, sizeof(line), fp))
1489                         msgbuf << line;
1490                 int status = pclose(fp);
1491                 if (WEXITSTATUS(status) == 0) {
1492                         if (platformVersion)
1493                                 *platformVersion = msgbuf.str();
1494                 }
1495         }
1496
1497         /* OS X */
1498         fp = popen("type sw_vers >/dev/null 2>&1 && sw_vers -productName 2>&1", "r");
1499
1500         if (fp) {
1501                 std::ostringstream msgbuf;
1502                 char line[1024];
1503                 while (fgets(line, sizeof(line), fp))
1504                         msgbuf << line;
1505                 int status = pclose(fp);
1506                 if (WEXITSTATUS(status) == 0) {
1507                         String info = msgbuf.str();
1508                         info = info.Trim();
1509
1510                         if (platformName)
1511                                 *platformName = info;
1512                 }
1513         }
1514
1515         fp = popen("type sw_vers >/dev/null 2>&1 && sw_vers -productVersion 2>&1", "r");
1516
1517         if (fp) {
1518                 std::ostringstream msgbuf;
1519                 char line[1024];
1520                 while (fgets(line, sizeof(line), fp))
1521                         msgbuf << line;
1522                 int status = pclose(fp);
1523                 if (WEXITSTATUS(status) == 0) {
1524                         String info = msgbuf.str();
1525                         info = info.Trim();
1526
1527                         if (platformVersion)
1528                                 *platformVersion = info;
1529
1530                         return true;
1531                 }
1532         }
1533
1534         /* Centos/RHEL < 7 */
1535         release.close();
1536         release.open("/etc/redhat-release");
1537         if (release.is_open()) {
1538                 std::string release_line;
1539                 getline(release, release_line);
1540
1541                 String info = release_line;
1542
1543                 /* example: Red Hat Enterprise Linux Server release 6.7 (Santiago) */
1544                 if (platformName)
1545                         *platformName = info.SubStr(0, info.Find("release") - 1);
1546
1547                 if (platformVersion)
1548                         *platformVersion = info.SubStr(info.Find("release") + 8);
1549
1550                 return true;
1551         }
1552
1553         /* sles 11 sp3, opensuse w/e */
1554         release.close();
1555         release.open("/etc/SuSE-release");
1556         if (release.is_open()) {
1557                 std::string release_line;
1558                 getline(release, release_line);
1559
1560                 String info = release_line;
1561
1562                 if (platformName)
1563                         *platformName = info.SubStr(0, info.FindFirstOf(" "));
1564
1565                 if (platformVersion)
1566                         *platformVersion = info.SubStr(info.FindFirstOf(" ") + 1);
1567
1568                 return true;
1569         }
1570
1571         /* Just give up */
1572         return false;
1573 #endif /* _WIN32 */
1574 }
1575
1576 String Utility::GetPlatformKernel()
1577 {
1578 #ifdef _WIN32
1579         return "Windows";
1580 #else /* _WIN32 */
1581         return UnameHelper('s');
1582 #endif /* _WIN32 */
1583 }
1584
1585 String Utility::GetPlatformKernelVersion()
1586 {
1587 #ifdef _WIN32
1588         OSVERSIONINFO info;
1589         info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1590         GetVersionEx(&info);
1591
1592         std::ostringstream msgbuf;
1593         msgbuf << info.dwMajorVersion << "." << info.dwMinorVersion;
1594
1595         return msgbuf.str();
1596 #else /* _WIN32 */
1597         return UnameHelper('r');
1598 #endif /* _WIN32 */
1599 }
1600
1601 String Utility::GetPlatformName()
1602 {
1603         String platformName;
1604         if (!ReleaseHelper(&platformName, nullptr))
1605                 return "Unknown";
1606         return platformName;
1607 }
1608
1609 String Utility::GetPlatformVersion()
1610 {
1611         String platformVersion;
1612         if (!ReleaseHelper(nullptr, &platformVersion))
1613                 return "Unknown";
1614         return platformVersion;
1615 }
1616
1617 String Utility::GetPlatformArchitecture()
1618 {
1619 #ifdef _WIN32
1620         SYSTEM_INFO info;
1621         GetNativeSystemInfo(&info);
1622         switch (info.wProcessorArchitecture) {
1623                 case PROCESSOR_ARCHITECTURE_AMD64:
1624                         return "x86_64";
1625                 case PROCESSOR_ARCHITECTURE_ARM:
1626                         return "arm";
1627                 case PROCESSOR_ARCHITECTURE_INTEL:
1628                         return "x86";
1629                 default:
1630                         return "unknown";
1631         }
1632 #else /* _WIN32 */
1633         return UnameHelper('m');
1634 #endif /* _WIN32 */
1635 }
1636
1637 const char l_Utf8Replacement[] = "\xEF\xBF\xBD";
1638
1639 String Utility::ValidateUTF8(const String& input)
1640 {
1641         std::vector<char> output;
1642         output.reserve(input.GetLength() * 3u);
1643
1644         try {
1645                 utf8::replace_invalid(input.Begin(), input.End(), std::back_inserter(output));
1646         } catch (const utf8::not_enough_room&) {
1647                 output.insert(output.end(), (const char*)l_Utf8Replacement, (const char*)l_Utf8Replacement + 3);
1648         }
1649
1650         return String(output.begin(), output.end());
1651 }
1652
1653 String Utility::CreateTempFile(const String& path, int mode, std::fstream& fp)
1654 {
1655         std::vector<char> targetPath(path.Begin(), path.End());
1656         targetPath.push_back('\0');
1657
1658         int fd;
1659 #ifndef _WIN32
1660         fd = mkstemp(&targetPath[0]);
1661 #else /* _WIN32 */
1662         fd = MksTemp(&targetPath[0]);
1663 #endif /*_WIN32*/
1664
1665         if (fd < 0) {
1666                 BOOST_THROW_EXCEPTION(posix_error()
1667                         << boost::errinfo_api_function("mkstemp")
1668                         << boost::errinfo_errno(errno)
1669                         << boost::errinfo_file_name(path));
1670         }
1671
1672         try {
1673                 fp.open(&targetPath[0], std::ios_base::trunc | std::ios_base::out);
1674         } catch (const std::fstream::failure&) {
1675                 close(fd);
1676                 throw;
1677         }
1678
1679         close(fd);
1680
1681         String resultPath = String(targetPath.begin(), targetPath.end() - 1);
1682
1683         if (chmod(resultPath.CStr(), mode) < 0) {
1684                 BOOST_THROW_EXCEPTION(posix_error()
1685                         << boost::errinfo_api_function("chmod")
1686                         << boost::errinfo_errno(errno)
1687                         << boost::errinfo_file_name(resultPath));
1688         }
1689
1690         return resultPath;
1691 }
1692
1693 #ifdef _WIN32
1694 /* mkstemp extracted from libc/sysdeps/posix/tempname.c.  Copyright
1695  * (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.
1696  *
1697  * The GNU C Library is free software; you can redistribute it and/or
1698  * modify it under the terms of the GNU Lesser General Public
1699  * License as published by the Free Software Foundation; either
1700  * version 2.1 of the License, or (at your option) any later version.
1701  */
1702
1703 #define _O_EXCL 0x0400
1704 #define _O_CREAT 0x0100
1705 #define _O_RDWR 0x0002
1706 #define O_EXCL _O_EXCL
1707 #define O_CREAT _O_CREAT
1708 #define O_RDWR _O_RDWR
1709
1710 static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1711
1712 /* Generate a temporary file name based on TMPL.  TMPL must match the
1713  * rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
1714  * does not exist at the time of the call to mkstemp.  TMPL is
1715  * overwritten with the result.
1716  */
1717 int Utility::MksTemp(char *tmpl)
1718 {
1719         int len;
1720         char *XXXXXX;
1721         static unsigned long long value;
1722         unsigned long long random_time_bits;
1723         unsigned int count;
1724         int fd = -1;
1725         int save_errno = errno;
1726
1727         /* A lower bound on the number of temporary files to attempt to
1728          * generate.  The maximum total number of temporary file names that
1729          * can exist for a given template is 62**6.  It should never be
1730          * necessary to try all these combinations.  Instead if a reasonable
1731          * number of names is tried (we define reasonable as 62**3) fail to
1732          * give the system administrator the chance to remove the problems.
1733          */
1734 #define ATTEMPTS_MIN (62 * 62 * 62)
1735
1736         /* The number of times to attempt to generate a temporary file
1737          * To conform to POSIX, this must be no smaller than TMP_MAX.
1738          */
1739 #if ATTEMPTS_MIN < TMP_MAX
1740         unsigned int attempts = TMP_MAX;
1741 #else
1742         unsigned int attempts = ATTEMPTS_MIN;
1743 #endif
1744
1745         len = strlen (tmpl);
1746         if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) {
1747                 errno = EINVAL;
1748                 return -1;
1749         }
1750
1751         /* This is where the Xs start.  */
1752         XXXXXX = &tmpl[len - 6];
1753
1754         /* Get some more or less random data.  */
1755         {
1756                 SYSTEMTIME stNow;
1757                 FILETIME ftNow;
1758
1759                 // get system time
1760                 GetSystemTime(&stNow);
1761                 stNow.wMilliseconds = 500;
1762                 if (!SystemTimeToFileTime(&stNow, &ftNow)) {
1763                         errno = -1;
1764                         return -1;
1765                 }
1766
1767                 random_time_bits = (((unsigned long long)ftNow.dwHighDateTime << 32) | (unsigned long long)ftNow.dwLowDateTime);
1768         }
1769
1770         value += random_time_bits ^ (unsigned long long)GetCurrentThreadId();
1771
1772         for (count = 0; count < attempts; value += 7777, ++count) {
1773                 unsigned long long v = value;
1774
1775                 /* Fill in the random bits.  */
1776                 XXXXXX[0] = letters[v % 62];
1777                 v /= 62;
1778                 XXXXXX[1] = letters[v % 62];
1779                 v /= 62;
1780                 XXXXXX[2] = letters[v % 62];
1781                 v /= 62;
1782                 XXXXXX[3] = letters[v % 62];
1783                 v /= 62;
1784                 XXXXXX[4] = letters[v % 62];
1785                 v /= 62;
1786                 XXXXXX[5] = letters[v % 62];
1787
1788                 fd = open(tmpl, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE);
1789                 if (fd >= 0) {
1790                         errno = save_errno;
1791                         return fd;
1792                 } else if (errno != EEXIST)
1793                         return -1;
1794         }
1795
1796         /* We got out of the loop because we ran out of combinations to try.  */
1797         errno = EEXIST;
1798         return -1;
1799 }
1800
1801 String Utility::GetIcingaInstallPath()
1802 {
1803         char szProduct[39];
1804
1805         for (int i = 0; MsiEnumProducts(i, szProduct) == ERROR_SUCCESS; i++) {
1806                 char szName[128];
1807                 DWORD cbName = sizeof(szName);
1808                 if (MsiGetProductInfo(szProduct, INSTALLPROPERTY_INSTALLEDPRODUCTNAME, szName, &cbName) != ERROR_SUCCESS)
1809                         continue;
1810
1811                 if (strcmp(szName, "Icinga 2") != 0)
1812                         continue;
1813
1814                 char szLocation[1024];
1815                 DWORD cbLocation = sizeof(szLocation);
1816                 if (MsiGetProductInfo(szProduct, INSTALLPROPERTY_INSTALLLOCATION, szLocation, &cbLocation) == ERROR_SUCCESS)
1817                         return szLocation;
1818         }
1819
1820         return "";
1821 }
1822
1823 String Utility::GetIcingaDataPath()
1824 {
1825         char path[MAX_PATH];
1826         if (!SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_COMMON_APPDATA, nullptr, 0, path)))
1827                 return "";
1828         return String(path) + "\\icinga2";
1829 }
1830
1831 #endif /* _WIN32 */
1832
1833 /**
1834  * Retrieve the environment variable value by given key.
1835  *
1836  * @param env Environment variable name.
1837  */
1838
1839 String Utility::GetFromEnvironment(const String& env)
1840 {
1841         const char *envValue = getenv(env.CStr());
1842
1843         if (envValue == NULL)
1844                 return String();
1845         else
1846                 return String(envValue);
1847 }
1848
1849 /**
1850  * Compare the password entered by a client with the actual password.
1851  * The comparision is safe against timing attacks.
1852  */
1853 bool Utility::ComparePasswords(const String& enteredPassword, const String& actualPassword)
1854 {
1855         volatile const char * volatile enteredPasswordCStr = enteredPassword.CStr();
1856         volatile size_t enteredPasswordLen = enteredPassword.GetLength();
1857
1858         volatile const char * volatile actualPasswordCStr = actualPassword.CStr();
1859         volatile size_t actualPasswordLen = actualPassword.GetLength();
1860
1861         volatile uint_fast8_t result = enteredPasswordLen == actualPasswordLen;
1862
1863         if (result) {
1864                 auto cStr (actualPasswordCStr);
1865                 auto len (actualPasswordLen);
1866
1867                 actualPasswordCStr = cStr;
1868                 actualPasswordLen = len;
1869         } else {
1870                 auto cStr (enteredPasswordCStr);
1871                 auto len (enteredPasswordLen);
1872
1873                 actualPasswordCStr = cStr;
1874                 actualPasswordLen = len;
1875         }
1876
1877         for (volatile size_t i = 0; i < enteredPasswordLen; ++i) {
1878                 result &= uint_fast8_t(enteredPasswordCStr[i] == actualPasswordCStr[i]);
1879         }
1880
1881         return result;
1882 }