1 /******************************************************************************
3 * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License *
7 * as published by the Free Software Foundation; either version 2 *
8 * of the License, or (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software Foundation *
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ******************************************************************************/
28 #include <sys/types.h>
31 static std::string GetIcingaInstallPath(void)
33 char szFileName[MAX_PATH];
34 if (!GetModuleFileName(nullptr, szFileName, sizeof(szFileName)))
37 if (!PathRemoveFileSpec(szFileName))
40 if (!PathRemoveFileSpec(szFileName))
47 static bool ExecuteCommand(const std::string& app, const std::string& arguments)
49 SHELLEXECUTEINFO sei = {};
50 sei.cbSize = sizeof(sei);
51 sei.fMask = SEE_MASK_NOCLOSEPROCESS;
52 sei.lpFile = app.c_str();
53 sei.lpParameters = arguments.c_str();
55 if (!ShellExecuteEx(&sei))
61 WaitForSingleObject(sei.hProcess, INFINITE);
64 BOOL res = GetExitCodeProcess(sei.hProcess, &exitCode);
65 CloseHandle(sei.hProcess);
73 static bool ExecuteIcingaCommand(const std::string& arguments)
75 return ExecuteCommand(GetIcingaInstallPath() + "\\sbin\\icinga2.exe", arguments);
78 static std::string DirName(const std::string& path)
80 char *spath = strdup(path.c_str());
82 if (!PathRemoveFileSpec(spath)) {
84 throw std::runtime_error("PathRemoveFileSpec failed");
87 std::string result = spath;
94 static bool PathExists(const std::string& path)
97 return (_stat(path.c_str(), &statbuf) >= 0);
100 static std::string GetIcingaDataPath(void)
103 if (!SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_COMMON_APPDATA, nullptr, 0, path)))
104 throw std::runtime_error("SHGetFolderPath failed");
105 return std::string(path) + "\\icinga2";
108 static void MkDir(const std::string& path)
110 if (mkdir(path.c_str()) < 0 && errno != EEXIST)
111 throw std::runtime_error("mkdir failed");
114 static void MkDirP(const std::string& path)
118 while (pos != std::string::npos) {
119 pos = path.find_first_of("/\\", pos + 1);
121 std::string spath = path.substr(0, pos + 1);
122 struct _stat statbuf;
123 if (_stat(spath.c_str(), &statbuf) < 0 && errno == ENOENT)
124 MkDir(path.substr(0, pos));
128 static std::string GetNSISInstallPath(void)
131 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Icinga Development Team\\ICINGA2", 0,
132 KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS) {
133 BYTE pvData[MAX_PATH];
134 DWORD cbData = sizeof(pvData) - 1;
136 if (RegQueryValueEx(hKey, nullptr, nullptr, &lType, pvData, &cbData) == ERROR_SUCCESS && lType == REG_SZ) {
137 pvData[cbData] = '\0';
139 return (char *)pvData;
148 static bool CopyDirectory(const std::string& source, const std::string& destination)
150 // SHFileOperation requires file names to be terminated with two \0s
151 std::string tmpSource = source + std::string(1, '\0');
152 std::string tmpDestination = destination + std::string(1, '\0');
156 fop.pFrom = tmpSource.c_str();
157 fop.pTo = tmpDestination.c_str();
158 fop.fFlags = FOF_NO_UI;
160 return (SHFileOperation(&fop) == 0);
163 static bool DeleteDirectory(const std::string& dir)
165 // SHFileOperation requires file names to be terminated with two \0s
166 std::string tmpDir = dir + std::string(1, '\0');
169 fop.wFunc = FO_DELETE;
170 fop.pFrom = tmpDir.c_str();
171 fop.fFlags = FOF_NO_UI;
173 return (SHFileOperation(&fop) == 0);
176 static int UpgradeNSIS(void)
178 std::string installPath = GetNSISInstallPath();
180 if (installPath.empty())
183 std::string uninstallerPath = installPath + "\\uninstall.exe";
185 if (!PathExists(uninstallerPath))
188 std::string dataPath = GetIcingaDataPath();
190 if (dataPath.empty())
193 bool moveUserData = !PathExists(dataPath);
195 /* perform open heart surgery on the user's data dirs - yay */
197 MkDir(dataPath.c_str());
199 std::string oldNameEtc = installPath + "\\etc";
200 std::string newNameEtc = dataPath + "\\etc";
201 if (!CopyDirectory(oldNameEtc, newNameEtc))
204 std::string oldNameVar = installPath + "\\var";
205 std::string newNameVar = dataPath + "\\var";
206 if (!CopyDirectory(oldNameVar, newNameVar))
210 ExecuteCommand(uninstallerPath, "/S _?=" + installPath);
212 _unlink(uninstallerPath.c_str());
215 std::string oldNameEtc = installPath + "\\etc";
216 if (!DeleteDirectory(oldNameEtc))
219 std::string oldNameVar = installPath + "\\var";
220 if (!DeleteDirectory(oldNameVar))
223 _rmdir(installPath.c_str());
229 static int InstallIcinga(void)
231 std::string installDir = GetIcingaInstallPath();
232 std::string dataDir = GetIcingaDataPath();
234 if (!PathExists(dataDir)) {
235 std::string sourceDir = installDir + "\\share\\skel" + std::string(1, '\0');
236 std::string destinationDir = dataDir + std::string(1, '\0');
240 fop.pFrom = sourceDir.c_str();
241 fop.pTo = destinationDir.c_str();
242 fop.fFlags = FOF_NO_UI | FOF_NOCOPYSECURITYATTRIBS;
244 if (SHFileOperation(&fop) != 0)
247 MkDirP(dataDir + "/etc/icinga2/pki");
248 MkDirP(dataDir + "/var/cache/icinga2");
249 MkDirP(dataDir + "/var/lib/icinga2/certs");
250 MkDirP(dataDir + "/var/lib/icinga2/certificate-requests");
251 MkDirP(dataDir + "/var/lib/icinga2/agent/inventory");
252 MkDirP(dataDir + "/var/lib/icinga2/api/config");
253 MkDirP(dataDir + "/var/lib/icinga2/api/log");
254 MkDirP(dataDir + "/var/lib/icinga2/api/zones");
255 MkDirP(dataDir + "/var/log/icinga2/compat/archive");
256 MkDirP(dataDir + "/var/log/icinga2/crash");
257 MkDirP(dataDir + "/var/run/icinga2/cmd");
258 MkDirP(dataDir + "/var/spool/icinga2/perfdata");
259 MkDirP(dataDir + "/var/spool/icinga2/tmp");
262 ExecuteCommand("icacls", "\"" + dataDir + "\" /grant *S-1-5-20:(oi)(ci)m");
263 ExecuteCommand("icacls", "\"" + dataDir + "\\etc\" /inheritance:r /grant:r *S-1-5-20:(oi)(ci)m *S-1-5-32-544:(oi)(ci)f");
265 ExecuteIcingaCommand("--scm-install daemon");
270 static int UninstallIcinga(void)
272 ExecuteIcingaCommand("--scm-uninstall");
278 * Entry point for the installer application.
280 int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
282 CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
287 if (strcmp(lpCmdLine, "install") == 0) {
288 rc = InstallIcinga();
289 } else if (strcmp(lpCmdLine, "uninstall") == 0) {
290 rc = UninstallIcinga();
291 } else if (strcmp(lpCmdLine, "upgrade-nsis") == 0) {
294 MessageBox(nullptr, "This application should only be run by the MSI installer package.", "Icinga 2 Installer", MB_ICONWARNING);