summaryrefslogtreecommitdiffstats
path: root/icinga-installer
diff options
context:
space:
mode:
Diffstat (limited to 'icinga-installer')
-rw-r--r--icinga-installer/CMakeLists.txt47
-rw-r--r--icinga-installer/bannrbmp.bmpbin0 -> 29914 bytes
-rw-r--r--icinga-installer/dlgbmp.bmpbin0 -> 615402 bytes
-rw-r--r--icinga-installer/icinga-installer.cpp312
-rw-r--r--icinga-installer/icinga2.wixpatch.cmake52
5 files changed, 411 insertions, 0 deletions
diff --git a/icinga-installer/CMakeLists.txt b/icinga-installer/CMakeLists.txt
new file mode 100644
index 0000000..6ac5e1f
--- /dev/null
+++ b/icinga-installer/CMakeLists.txt
@@ -0,0 +1,47 @@
+# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+
+foreach(flag_var
+ CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+ CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+ if(${flag_var} MATCHES "/MD")
+ string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
+ endif(${flag_var} MATCHES "/MD")
+endforeach(flag_var)
+
+set(icinga_installer_SOURCES
+ icinga-installer.cpp
+)
+
+add_executable(icinga-installer ${icinga_installer_SOURCES})
+
+set_target_properties(
+ icinga-installer PROPERTIES
+ FOLDER Bin
+ OUTPUT_NAME icinga2-installer
+ LINK_FLAGS "/SUBSYSTEM:WINDOWS"
+)
+
+target_link_libraries(icinga-installer shlwapi)
+
+install(CODE "
+ execute_process(COMMAND \${CMAKE_COMMAND} -E copy \"${CMAKE_CURRENT_BINARY_DIR}/icinga2.wixpatch.\${BUILD_TYPE}\"
+ \"${CMAKE_CURRENT_BINARY_DIR}/icinga2.wixpatch\"
+ RESULT_VARIABLE copy_result
+ ERROR_VARIABLE error_output)
+ if(copy_result)
+ message(FATAL_ERROR \${error_output})
+ endif()"
+)
+
+file(
+ GENERATE
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/icinga2.wixpatch.$<CONFIG>"
+ INPUT "${CMAKE_CURRENT_SOURCE_DIR}/icinga2.wixpatch.cmake"
+)
+
+set(InstallPath "${CMAKE_INSTALL_SBINDIR}")
+
+install(
+ TARGETS icinga-installer
+ RUNTIME DESTINATION ${InstallPath}
+)
diff --git a/icinga-installer/bannrbmp.bmp b/icinga-installer/bannrbmp.bmp
new file mode 100644
index 0000000..0f68e62
--- /dev/null
+++ b/icinga-installer/bannrbmp.bmp
Binary files differ
diff --git a/icinga-installer/dlgbmp.bmp b/icinga-installer/dlgbmp.bmp
new file mode 100644
index 0000000..e46566f
--- /dev/null
+++ b/icinga-installer/dlgbmp.bmp
Binary files differ
diff --git a/icinga-installer/icinga-installer.cpp b/icinga-installer/icinga-installer.cpp
new file mode 100644
index 0000000..4dc050d
--- /dev/null
+++ b/icinga-installer/icinga-installer.cpp
@@ -0,0 +1,312 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#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>
+
+static std::string GetIcingaInstallPath(void)
+{
+ char szFileName[MAX_PATH];
+ if (!GetModuleFileName(nullptr, szFileName, sizeof(szFileName)))
+ return "";
+
+ 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(nullptr, CSIDL_COMMON_APPDATA, nullptr, 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;
+ //TODO: Change hardcoded key
+ 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, nullptr, nullptr, &lType, pvData, &cbData) == ERROR_SUCCESS && lType == REG_SZ) {
+ pvData[cbData] = '\0';
+
+ return (char *)pvData;
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ return "";
+}
+
+static bool CopyDirectory(const std::string& source, const std::string& destination)
+{
+ // 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 bool DeleteDirectory(const std::string& dir)
+{
+ // 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 int UpgradeNSIS(void)
+{
+ std::string installPath = GetNSISInstallPath();
+
+ if (installPath.empty())
+ return 0;
+
+ std::string uninstallerPath = installPath + "\\uninstall.exe";
+
+ if (!PathExists(uninstallerPath))
+ return 0;
+
+ std::string dataPath = GetIcingaDataPath();
+
+ 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)
+{
+ std::string installDir = GetIcingaInstallPath();
+ std::string skelDir = installDir + "\\share\\skel";
+ std::string dataDir = GetIcingaDataPath();
+
+ if (!PathExists(dataDir)) {
+ std::string sourceDir = skelDir + 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");
+ }
+
+ // Upgrade from versions older than 2.13 by making the windowseventlog feature available,
+ // enable it by default and disable the old mainlog feature.
+ if (!PathExists(dataDir + "/etc/icinga2/features-available/windowseventlog.conf")) {
+ // Disable the old mainlog feature as it is replaced by windowseventlog by default.
+ std::string mainlogEnabledFile = dataDir + "/etc/icinga2/features-enabled/mainlog.conf";
+ if (PathExists(mainlogEnabledFile)) {
+ if (DeleteFileA(mainlogEnabledFile.c_str()) == 0) {
+ throw std::runtime_error("deleting '" + mainlogEnabledFile + "' failed");
+ }
+ }
+
+ // Install the new windowseventlog feature. As features-available/windowseventlog.conf is used as a marker file,
+ // copy it as the last step, so that this is run again should the upgrade be interrupted.
+ for (const std::string& d : {"features-enabled", "features-available"}) {
+ std::string sourceFile = skelDir + "/etc/icinga2/" + d + "/windowseventlog.conf";
+ std::string destinationFile = dataDir + "/etc/icinga2/" + d + "/windowseventlog.conf";
+
+ if (CopyFileA(sourceFile.c_str(), destinationFile.c_str(), false) == 0) {
+ throw std::runtime_error("copying '" + sourceFile + "' to '" + destinationFile + "' failed");
+ }
+ }
+ }
+
+ // TODO: In Icinga 2.14, rename features-available/mainlog.conf to mainlog.conf.deprecated
+ // so that it's no longer listed as an available feature.
+
+ 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");
+
+ return 0;
+}
+
+static int UninstallIcinga(void)
+{
+ ExecuteIcingaCommand("--scm-uninstall");
+
+ return 0;
+}
+
+/**
+* Entry point for the installer application.
+*/
+int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
+{
+ CoInitializeEx(nullptr, 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(nullptr, "This application should only be run by the MSI installer package.", "Icinga 2 Installer", MB_ICONWARNING);
+ rc = 1;
+ }
+
+ //::Sleep(3000s);
+
+ return rc;
+}
diff --git a/icinga-installer/icinga2.wixpatch.cmake b/icinga-installer/icinga2.wixpatch.cmake
new file mode 100644
index 0000000..eee4027
--- /dev/null
+++ b/icinga-installer/icinga2.wixpatch.cmake
@@ -0,0 +1,52 @@
+<CPackWiXPatch>
+ <CPackWiXFragment Id="#PRODUCT">
+ <Property Id="ALLUSERS">1</Property>
+ <Property Id="MSIRESTARTMANAGERCONTROL">Disable</Property>
+
+ <PropertyRef Id="WIX_IS_NETFRAMEWORK_46_OR_LATER_INSTALLED" />
+ <Condition Message='This application requires .NET Framework 4.6 or higher. Please install the .NET Framework then run this installer again.'>
+ <![CDATA[Installed OR WIX_IS_NETFRAMEWORK_46_OR_LATER_INSTALLED]]>
+ </Condition>
+
+ <CustomAction Id="XtraUpgradeNSIS" BinaryKey="icinga2_installer" ExeCommand="upgrade-nsis" Execute="deferred" Impersonate="no" />
+ <CustomAction Id="XtraInstall" FileKey="CM_FP_sbin.icinga2_installer.exe" ExeCommand="install" Execute="deferred" Impersonate="no" />
+ <CustomAction Id="XtraUninstall" FileKey="CM_FP_sbin.icinga2_installer.exe" ExeCommand="uninstall" Execute="deferred" Impersonate="no" />
+
+ <Binary Id="icinga2_installer" SourceFile="$<TARGET_FILE:icinga-installer>" />
+
+ <InstallExecuteSequence>
+ <Custom Action="XtraUpgradeNSIS" After="InstallInitialize">$CM_CP_sbin.icinga2_installer.exe&gt;2 AND NOT SUPPRESS_XTRA</Custom>
+ <Custom Action="XtraInstall" Before="InstallFinalize">$CM_CP_sbin.icinga2_installer.exe&gt;2 AND NOT SUPPRESS_XTRA</Custom>
+ <Custom Action="XtraUninstall" Before="RemoveExistingProducts">$CM_CP_sbin.icinga2_installer.exe=2 AND NOT SUPPRESS_XTRA</Custom>
+ </InstallExecuteSequence>
+
+ <!--
+ Write the path to eventprovider.dll to the registry so that the Event Viewer is able to find
+ the message definitions and properly displays our log messages.
+
+ See also: https://docs.microsoft.com/en-us/windows/win32/eventlog/reporting-an-event
+ -->
+ <FeatureRef Id="ProductFeature" IgnoreParent="yes">
+ <Component Id="EventProviderRegistryEntry" Guid="*" Directory="INSTALL_ROOT">
+ <RegistryKey Root="HKLM" Key="SYSTEM\CurrentControlSet\Services\EventLog\Application\Icinga 2" Action="createAndRemoveOnUninstall">
+ <RegistryValue Name="EventMessageFile" Type="string" Value="[#CM_FP_sbin.eventprovider.dll]" />
+ </RegistryKey>
+ </Component>
+ </FeatureRef>
+
+ <Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Run Icinga 2 setup wizard" />
+
+ <Property Id="WixShellExecTarget" Value="[#CM_FP_sbin.Icinga2SetupAgent.exe]" />
+ <CustomAction Id="LaunchIcinga2Wizard"
+ BinaryKey="WixCA"
+ DllEntry="WixShellExec"
+ Impersonate="no" />
+
+ <UI>
+ <Publish Dialog="ExitDialog"
+ Control="Finish"
+ Event="DoAction"
+ Value="LaunchIcinga2Wizard">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
+ </UI>
+ </CPackWiXFragment>
+</CPackWiXPatch>