]> granicus.if.org Git - icinga2/blob - icinga-installer/icinga-installer.cpp
Merge pull request #5746 from sni/livestatus_should_return_empty_lists
[icinga2] / icinga-installer / icinga-installer.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/)  *
4  *                                                                            *
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.                     *
9  *                                                                            *
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.                               *
14  *                                                                            *
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  ******************************************************************************/
19
20 #include <string>
21 #include <vector>
22 #include <fstream>
23 #include <direct.h>
24 #include <windows.h>
25 #include <shlwapi.h>
26 #include <shellapi.h>
27 #include <shlobj.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30
31 static std::string GetIcingaInstallPath(void)
32 {
33         char szFileName[MAX_PATH];
34         if (!GetModuleFileName(NULL, szFileName, sizeof(szFileName)))
35                 return "";
36
37         if (!PathRemoveFileSpec(szFileName))
38                 return "";
39
40         if (!PathRemoveFileSpec(szFileName))
41                 return "";
42
43         return szFileName;
44 }
45
46
47 static bool ExecuteCommand(const std::string& app, const std::string& arguments)
48 {
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();
54         sei.nShow = SW_HIDE;
55         if (!ShellExecuteEx(&sei))
56                 return false;
57         
58         if (!sei.hProcess)
59                 return false;
60
61         WaitForSingleObject(sei.hProcess, INFINITE);
62
63         DWORD exitCode;
64         BOOL res = GetExitCodeProcess(sei.hProcess, &exitCode);
65         CloseHandle(sei.hProcess);
66
67         if (!res)
68                 return false;
69
70         return exitCode == 0;
71 }
72
73 static bool ExecuteIcingaCommand(const std::string& arguments)
74 {
75         return ExecuteCommand(GetIcingaInstallPath() + "\\sbin\\icinga2.exe", arguments);
76 }
77
78 static std::string DirName(const std::string& path)
79 {
80         char *spath = strdup(path.c_str());
81
82         if (!PathRemoveFileSpec(spath)) {
83                 free(spath);
84                 throw std::runtime_error("PathRemoveFileSpec failed");
85         }
86
87         std::string result = spath;
88
89         free(spath);
90
91         return result;
92 }
93
94 static bool PathExists(const std::string& path)
95 {
96         struct _stat statbuf;
97         return (_stat(path.c_str(), &statbuf) >= 0);
98 }
99
100 static std::string GetIcingaDataPath(void)
101 {
102         char path[MAX_PATH];
103         if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, path)))
104                 throw std::runtime_error("SHGetFolderPath failed");
105         return std::string(path) + "\\icinga2";
106 }
107
108 static void MkDir(const std::string& path)
109 {
110         if (mkdir(path.c_str()) < 0 && errno != EEXIST)
111                 throw std::runtime_error("mkdir failed");
112 }
113
114 static void MkDirP(const std::string& path)
115 {
116         size_t pos = 0;
117
118         while (pos != std::string::npos) {
119                 pos = path.find_first_of("/\\", pos + 1);
120
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));
125         }
126 }
127
128 static std::string GetNSISInstallPath(void)
129 {
130         HKEY hKey;
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;
135                 DWORD lType;
136                 if (RegQueryValueEx(hKey, NULL, NULL, &lType, pvData, &cbData) == ERROR_SUCCESS && lType == REG_SZ) {
137                         pvData[cbData] = '\0';
138
139                         return (char *)pvData;
140                 }
141
142                 RegCloseKey(hKey);
143         }
144
145         return "";
146 }
147
148 static bool CopyDirectory(const std::string& source, const std::string& destination)
149 {
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');
153
154         SHFILEOPSTRUCT fop;
155         fop.wFunc = FO_COPY;
156         fop.pFrom = tmpSource.c_str();
157         fop.pTo = tmpDestination.c_str();
158         fop.fFlags = FOF_NO_UI;
159
160         return (SHFileOperation(&fop) == 0);
161 }
162
163 static bool DeleteDirectory(const std::string& dir)
164 {
165         // SHFileOperation requires file names to be terminated with two \0s
166         std::string tmpDir = dir + std::string(1, '\0');
167
168         SHFILEOPSTRUCT fop;
169         fop.wFunc = FO_DELETE;
170         fop.pFrom = tmpDir.c_str();
171         fop.fFlags = FOF_NO_UI;
172
173         return (SHFileOperation(&fop) == 0);
174 }
175
176 static int UpgradeNSIS(void)
177 {
178         std::string installPath = GetNSISInstallPath();
179
180         if (installPath.empty())
181                 return 0;
182
183         std::string uninstallerPath = installPath + "\\uninstall.exe";
184
185         if (!PathExists(uninstallerPath))
186                 return 0;
187
188         std::string dataPath = GetIcingaDataPath();
189
190         if (dataPath.empty())
191                 return 1;
192
193         bool moveUserData = !PathExists(dataPath);
194
195         /* perform open heart surgery on the user's data dirs - yay */
196         if (moveUserData) {
197                 MkDir(dataPath.c_str());
198
199                 std::string oldNameEtc = installPath + "\\etc";
200                 std::string newNameEtc = dataPath + "\\etc";
201                 if (!CopyDirectory(oldNameEtc, newNameEtc))
202                         return 1;
203
204                 std::string oldNameVar = installPath + "\\var";
205                 std::string newNameVar = dataPath + "\\var";
206                 if (!CopyDirectory(oldNameVar, newNameVar))
207                         return 1;
208         }
209
210         ExecuteCommand(uninstallerPath, "/S _?=" + installPath);
211
212         _unlink(uninstallerPath.c_str());
213
214         if (moveUserData) {
215                 std::string oldNameEtc = installPath + "\\etc";
216                 if (!DeleteDirectory(oldNameEtc))
217                         return 1;
218
219                 std::string oldNameVar = installPath + "\\var";
220                 if (!DeleteDirectory(oldNameVar))
221                         return 1;
222
223                 _rmdir(installPath.c_str());
224         }       
225
226         return 0;
227 }
228
229 static int InstallIcinga(void)
230 {
231         std::string installDir = GetIcingaInstallPath();
232         std::string dataDir = GetIcingaDataPath();
233
234         if (!PathExists(dataDir)) {
235                 std::string sourceDir = installDir + "\\share\\skel" + std::string(1, '\0');
236                 std::string destinationDir = dataDir + std::string(1, '\0');
237
238                 SHFILEOPSTRUCT fop;
239                 fop.wFunc = FO_COPY;
240                 fop.pFrom = sourceDir.c_str();
241                 fop.pTo = destinationDir.c_str();
242                 fop.fFlags = FOF_NO_UI | FOF_NOCOPYSECURITYATTRIBS;
243
244                 if (SHFileOperation(&fop) != 0)
245                         return 1;
246
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");
260         }
261
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");
264
265         ExecuteIcingaCommand("--scm-install daemon");
266
267         return 0;
268 }
269
270 static int UninstallIcinga(void)
271 {
272         ExecuteIcingaCommand("--scm-uninstall");
273
274         return 0;
275 }
276
277 /**
278 * Entry point for the installer application.
279 */
280 int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
281 {
282         CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
283
284         //AllocConsole();
285         int rc;
286
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) {
292                 rc = UpgradeNSIS();
293         } else {
294                 MessageBox(NULL, "This application should only be run by the MSI installer package.", "Icinga 2 Installer", MB_ICONWARNING);
295                 rc = 1;
296         }
297
298         //::Sleep(3000s);
299
300         return rc;
301 }