]> granicus.if.org Git - icinga2/blobdiff - icinga-installer/icinga-installer.cpp
Merge pull request #5855 from Icinga/fix/vs2017
[icinga2] / icinga-installer / icinga-installer.cpp
index b4587f37ceb88190adea5507f26406c73c1bae90..438ddc37db7eba7ddc1a0f8299293ef382ba918d 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  * Icinga 2                                                                   *
- * Copyright (C) 2012-2016 Icinga Development Team (https://www.icinga.org/)  *
+ * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/)  *
  *                                                                            *
  * This program is free software; you can redistribute it and/or              *
  * modify it under the terms of the GNU General Public License                *
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#include "base/utility.hpp"
-#include "base/application.hpp"
+#include <string>
+#include <vector>
+#include <fstream>
+#include <direct.h>
+#include <windows.h>
+#include <shlwapi.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
-using namespace icinga;
-
-static String GetIcingaInstallDir(void)
+static std::string GetIcingaInstallPath(void)
 {
        char szFileName[MAX_PATH];
        if (!GetModuleFileName(NULL, szFileName, sizeof(szFileName)))
                return "";
-       return Utility::DirName(Utility::DirName(szFileName));
+
+       if (!PathRemoveFileSpec(szFileName))
+               return "";
+
+       if (!PathRemoveFileSpec(szFileName))
+               return "";
+
+       return szFileName;
+}
+
+
+static bool ExecuteCommand(const std::string& app, const std::string& arguments)
+{
+       SHELLEXECUTEINFO sei = {};
+       sei.cbSize = sizeof(sei);
+       sei.fMask = SEE_MASK_NOCLOSEPROCESS;
+       sei.lpFile = app.c_str();
+       sei.lpParameters = arguments.c_str();
+       sei.nShow = SW_HIDE;
+       if (!ShellExecuteEx(&sei))
+               return false;
+       
+       if (!sei.hProcess)
+               return false;
+
+       WaitForSingleObject(sei.hProcess, INFINITE);
+
+       DWORD exitCode;
+       BOOL res = GetExitCodeProcess(sei.hProcess, &exitCode);
+       CloseHandle(sei.hProcess);
+
+       if (!res)
+               return false;
+
+       return exitCode == 0;
+}
+
+static bool ExecuteIcingaCommand(const std::string& arguments)
+{
+       return ExecuteCommand(GetIcingaInstallPath() + "\\sbin\\icinga2.exe", arguments);
+}
+
+static std::string DirName(const std::string& path)
+{
+       char *spath = strdup(path.c_str());
+
+       if (!PathRemoveFileSpec(spath)) {
+               free(spath);
+               throw std::runtime_error("PathRemoveFileSpec failed");
+       }
+
+       std::string result = spath;
+
+       free(spath);
+
+       return result;
+}
+
+static bool PathExists(const std::string& path)
+{
+       struct _stat statbuf;
+       return (_stat(path.c_str(), &statbuf) >= 0);
+}
+
+static std::string GetIcingaDataPath(void)
+{
+       char path[MAX_PATH];
+       if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, path)))
+               throw std::runtime_error("SHGetFolderPath failed");
+       return std::string(path) + "\\icinga2";
+}
+
+static void MkDir(const std::string& path)
+{
+       if (mkdir(path.c_str()) < 0 && errno != EEXIST)
+               throw std::runtime_error("mkdir failed");
+}
+
+static void MkDirP(const std::string& path)
+{
+       size_t pos = 0;
+
+       while (pos != std::string::npos) {
+               pos = path.find_first_of("/\\", pos + 1);
+
+               std::string spath = path.substr(0, pos + 1);
+               struct _stat statbuf;
+               if (_stat(spath.c_str(), &statbuf) < 0 && errno == ENOENT)
+                       MkDir(path.substr(0, pos));
+       }
+}
+
+static std::string GetNSISInstallPath(void)
+{
+       HKEY hKey;
+       if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Icinga Development Team\\ICINGA2", 0,
+               KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS) {
+               BYTE pvData[MAX_PATH];
+               DWORD cbData = sizeof(pvData) - 1;
+               DWORD lType;
+               if (RegQueryValueEx(hKey, NULL, NULL, &lType, pvData, &cbData) == ERROR_SUCCESS && lType == REG_SZ) {
+                       pvData[cbData] = '\0';
+
+                       return (char *)pvData;
+               }
+
+               RegCloseKey(hKey);
+       }
+
+       return "";
 }
 
-static void ExecuteCommand(const String& command)
+static bool CopyDirectory(const std::string& source, const std::string& destination)
 {
-       std::cerr << "Executing command: " << command << std::endl;
-       system(command.CStr());
+       // SHFileOperation requires file names to be terminated with two \0s
+       std::string tmpSource = source + std::string(1, '\0');
+       std::string tmpDestination = destination + std::string(1, '\0');
+
+       SHFILEOPSTRUCT fop;
+       fop.wFunc = FO_COPY;
+       fop.pFrom = tmpSource.c_str();
+       fop.pTo = tmpDestination.c_str();
+       fop.fFlags = FOF_NO_UI;
+
+       return (SHFileOperation(&fop) == 0);
 }
 
-static void ExecuteIcingaCommand(const String& args)
+static bool DeleteDirectory(const std::string& dir)
 {
-       ExecuteCommand("\"" + GetIcingaInstallDir() + "\\sbin\\icinga2.exe\" " + args);
+       // SHFileOperation requires file names to be terminated with two \0s
+       std::string tmpDir = dir + std::string(1, '\0');
+
+       SHFILEOPSTRUCT fop;
+       fop.wFunc = FO_DELETE;
+       fop.pFrom = tmpDir.c_str();
+       fop.fFlags = FOF_NO_UI;
+
+       return (SHFileOperation(&fop) == 0);
 }
 
-static void CopyConfigFile(const String& installDir, const String& sourceConfigPath, size_t skelPrefixLength)
+static int UpgradeNSIS(void)
 {
-       String relativeConfigPath = sourceConfigPath.SubStr(installDir.GetLength() + skelPrefixLength);
+       std::string installPath = GetNSISInstallPath();
+
+       if (installPath.empty())
+               return 0;
+
+       std::string uninstallerPath = installPath + "\\uninstall.exe";
+
+       if (!PathExists(uninstallerPath))
+               return 0;
 
-       String targetConfigPath = installDir + relativeConfigPath;
+       std::string dataPath = GetIcingaDataPath();
 
-       if (!Utility::PathExists(targetConfigPath)) {
-               Utility::MkDirP(Utility::DirName(targetConfigPath), 0700);
-               Utility::CopyFile(sourceConfigPath, targetConfigPath);
+       if (dataPath.empty())
+               return 1;
+
+       bool moveUserData = !PathExists(dataPath);
+
+       /* perform open heart surgery on the user's data dirs - yay */
+       if (moveUserData) {
+               MkDir(dataPath.c_str());
+
+               std::string oldNameEtc = installPath + "\\etc";
+               std::string newNameEtc = dataPath + "\\etc";
+               if (!CopyDirectory(oldNameEtc, newNameEtc))
+                       return 1;
+
+               std::string oldNameVar = installPath + "\\var";
+               std::string newNameVar = dataPath + "\\var";
+               if (!CopyDirectory(oldNameVar, newNameVar))
+                       return 1;
        }
+
+       ExecuteCommand(uninstallerPath, "/S _?=" + installPath);
+
+       _unlink(uninstallerPath.c_str());
+
+       if (moveUserData) {
+               std::string oldNameEtc = installPath + "\\etc";
+               if (!DeleteDirectory(oldNameEtc))
+                       return 1;
+
+               std::string oldNameVar = installPath + "\\var";
+               if (!DeleteDirectory(oldNameVar))
+                       return 1;
+
+               _rmdir(installPath.c_str());
+       }       
+
+       return 0;
 }
 
 static int InstallIcinga(void)
 {
-       String installDir = GetIcingaInstallDir();
-
-       ExecuteCommand("icacls \"" + installDir + "\" /grant *S-1-5-20:(oi)(ci)m");
-       ExecuteCommand("icacls \"" + installDir + "\\etc\" /inheritance:r /grant:r *S-1-5-20:(oi)(ci)m *S-1-5-32-544:(oi)(ci)f");
-
-       Utility::MkDirP(installDir + "/etc/icinga2/pki", 0700);
-       Utility::MkDirP(installDir + "/var/cache/icinga2", 0700);
-       Utility::MkDirP(installDir + "/var/lib/icinga2/pki", 0700);
-       Utility::MkDirP(installDir + "/var/lib/icinga2/agent/inventory", 0700);
-       Utility::MkDirP(installDir + "/var/lib/icinga2/api/config", 0700);
-       Utility::MkDirP(installDir + "/var/lib/icinga2/api/log", 0700);
-       Utility::MkDirP(installDir + "/var/lib/icinga2/api/zones", 0700);
-       Utility::MkDirP(installDir + "/var/lib/icinga2/api/zones", 0700);
-       Utility::MkDirP(installDir + "/var/log/icinga2/compat/archive", 0700);
-       Utility::MkDirP(installDir + "/var/log/icinga2/crash", 0700);
-       Utility::MkDirP(installDir + "/var/run/icinga2/cmd", 0700);
-       Utility::MkDirP(installDir + "/var/spool/icinga2/perfdata", 0700);
-       Utility::MkDirP(installDir + "/var/spool/icinga2/tmp", 0700);
-
-       String skelDir = "/share/skel";
-       Utility::GlobRecursive(installDir + skelDir, "*", boost::bind(&CopyConfigFile, installDir, _1, skelDir.GetLength()), GlobFile);
+       std::string installDir = GetIcingaInstallPath();
+       std::string dataDir = GetIcingaDataPath();
+
+       if (!PathExists(dataDir)) {
+               std::string sourceDir = installDir + "\\share\\skel" + std::string(1, '\0');
+               std::string destinationDir = dataDir + std::string(1, '\0');
+
+               SHFILEOPSTRUCT fop;
+               fop.wFunc = FO_COPY;
+               fop.pFrom = sourceDir.c_str();
+               fop.pTo = destinationDir.c_str();
+               fop.fFlags = FOF_NO_UI | FOF_NOCOPYSECURITYATTRIBS;
+
+               if (SHFileOperation(&fop) != 0)
+                       return 1;
+
+               MkDirP(dataDir + "/etc/icinga2/pki");
+               MkDirP(dataDir + "/var/cache/icinga2");
+               MkDirP(dataDir + "/var/lib/icinga2/certs");
+               MkDirP(dataDir + "/var/lib/icinga2/certificate-requests");
+               MkDirP(dataDir + "/var/lib/icinga2/agent/inventory");
+               MkDirP(dataDir + "/var/lib/icinga2/api/config");
+               MkDirP(dataDir + "/var/lib/icinga2/api/log");
+               MkDirP(dataDir + "/var/lib/icinga2/api/zones");
+               MkDirP(dataDir + "/var/log/icinga2/compat/archive");
+               MkDirP(dataDir + "/var/log/icinga2/crash");
+               MkDirP(dataDir + "/var/run/icinga2/cmd");
+               MkDirP(dataDir + "/var/spool/icinga2/perfdata");
+               MkDirP(dataDir + "/var/spool/icinga2/tmp");
+       }
+
+       ExecuteCommand("icacls", "\"" + dataDir + "\" /grant *S-1-5-20:(oi)(ci)m");
+       ExecuteCommand("icacls", "\"" + dataDir + "\\etc\" /inheritance:r /grant:r *S-1-5-20:(oi)(ci)m *S-1-5-32-544:(oi)(ci)f");
 
        ExecuteIcingaCommand("--scm-install daemon");
 
@@ -94,23 +279,23 @@ static int UninstallIcinga(void)
 */
 int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
 {
-       /* must be called before using any other libbase functions */
-       Application::InitializeBase();
+       CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
 
        //AllocConsole();
-
        int rc;
 
        if (strcmp(lpCmdLine, "install") == 0) {
                rc = InstallIcinga();
        } else if (strcmp(lpCmdLine, "uninstall") == 0) {
                rc = UninstallIcinga();
+       } else if (strcmp(lpCmdLine, "upgrade-nsis") == 0) {
+               rc = UpgradeNSIS();
        } else {
                MessageBox(NULL, "This application should only be run by the MSI installer package.", "Icinga 2 Installer", MB_ICONWARNING);
                rc = 1;
        }
 
-       //Utility::Sleep(3);
+       //::Sleep(3000s);
 
-       Application::Exit(rc);
+       return rc;
 }