]> granicus.if.org Git - icinga2/blob - icinga-installer/icinga-installer.cpp
Merge pull request #7591 from Icinga/feature/docs-api-joins
[icinga2] / icinga-installer / icinga-installer.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include <string>
4 #include <vector>
5 #include <fstream>
6 #include <direct.h>
7 #include <windows.h>
8 #include <shlwapi.h>
9 #include <shellapi.h>
10 #include <shlobj.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13
14 static std::string GetIcingaInstallPath(void)
15 {
16         char szFileName[MAX_PATH];
17         if (!GetModuleFileName(nullptr, szFileName, sizeof(szFileName)))
18                 return "";
19
20         if (!PathRemoveFileSpec(szFileName))
21                 return "";
22
23         if (!PathRemoveFileSpec(szFileName))
24                 return "";
25
26         return szFileName;
27 }
28
29
30 static bool ExecuteCommand(const std::string& app, const std::string& arguments)
31 {
32         SHELLEXECUTEINFO sei = {};
33         sei.cbSize = sizeof(sei);
34         sei.fMask = SEE_MASK_NOCLOSEPROCESS;
35         sei.lpFile = app.c_str();
36         sei.lpParameters = arguments.c_str();
37         sei.nShow = SW_HIDE;
38         if (!ShellExecuteEx(&sei))
39                 return false;
40
41         if (!sei.hProcess)
42                 return false;
43
44         WaitForSingleObject(sei.hProcess, INFINITE);
45
46         DWORD exitCode;
47         BOOL res = GetExitCodeProcess(sei.hProcess, &exitCode);
48         CloseHandle(sei.hProcess);
49
50         if (!res)
51                 return false;
52
53         return exitCode == 0;
54 }
55
56 static bool ExecuteIcingaCommand(const std::string& arguments)
57 {
58         return ExecuteCommand(GetIcingaInstallPath() + "\\sbin\\icinga2.exe", arguments);
59 }
60
61 static std::string DirName(const std::string& path)
62 {
63         char *spath = strdup(path.c_str());
64
65         if (!PathRemoveFileSpec(spath)) {
66                 free(spath);
67                 throw std::runtime_error("PathRemoveFileSpec failed");
68         }
69
70         std::string result = spath;
71
72         free(spath);
73
74         return result;
75 }
76
77 static bool PathExists(const std::string& path)
78 {
79         struct _stat statbuf;
80         return (_stat(path.c_str(), &statbuf) >= 0);
81 }
82
83 static std::string GetIcingaDataPath(void)
84 {
85         char path[MAX_PATH];
86         if (!SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_COMMON_APPDATA, nullptr, 0, path)))
87                 throw std::runtime_error("SHGetFolderPath failed");
88         return std::string(path) + "\\icinga2";
89 }
90
91 static void MkDir(const std::string& path)
92 {
93         if (mkdir(path.c_str()) < 0 && errno != EEXIST)
94                 throw std::runtime_error("mkdir failed");
95 }
96
97 static void MkDirP(const std::string& path)
98 {
99         size_t pos = 0;
100
101         while (pos != std::string::npos) {
102                 pos = path.find_first_of("/\\", pos + 1);
103
104                 std::string spath = path.substr(0, pos + 1);
105                 struct _stat statbuf;
106                 if (_stat(spath.c_str(), &statbuf) < 0 && errno == ENOENT)
107                         MkDir(path.substr(0, pos));
108         }
109 }
110
111 static std::string GetNSISInstallPath(void)
112 {
113         HKEY hKey;
114         //TODO: Change hardcoded key
115         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Icinga Development Team\\ICINGA2", 0,
116                 KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS) {
117                 BYTE pvData[MAX_PATH];
118                 DWORD cbData = sizeof(pvData) - 1;
119                 DWORD lType;
120                 if (RegQueryValueEx(hKey, nullptr, nullptr, &lType, pvData, &cbData) == ERROR_SUCCESS && lType == REG_SZ) {
121                         pvData[cbData] = '\0';
122
123                         return (char *)pvData;
124                 }
125
126                 RegCloseKey(hKey);
127         }
128
129         return "";
130 }
131
132 static bool CopyDirectory(const std::string& source, const std::string& destination)
133 {
134         // SHFileOperation requires file names to be terminated with two \0s
135         std::string tmpSource = source + std::string(1, '\0');
136         std::string tmpDestination = destination + std::string(1, '\0');
137
138         SHFILEOPSTRUCT fop;
139         fop.wFunc = FO_COPY;
140         fop.pFrom = tmpSource.c_str();
141         fop.pTo = tmpDestination.c_str();
142         fop.fFlags = FOF_NO_UI;
143
144         return (SHFileOperation(&fop) == 0);
145 }
146
147 static bool DeleteDirectory(const std::string& dir)
148 {
149         // SHFileOperation requires file names to be terminated with two \0s
150         std::string tmpDir = dir + std::string(1, '\0');
151
152         SHFILEOPSTRUCT fop;
153         fop.wFunc = FO_DELETE;
154         fop.pFrom = tmpDir.c_str();
155         fop.fFlags = FOF_NO_UI;
156
157         return (SHFileOperation(&fop) == 0);
158 }
159
160 static int UpgradeNSIS(void)
161 {
162         std::string installPath = GetNSISInstallPath();
163
164         if (installPath.empty())
165                 return 0;
166
167         std::string uninstallerPath = installPath + "\\uninstall.exe";
168
169         if (!PathExists(uninstallerPath))
170                 return 0;
171
172         std::string dataPath = GetIcingaDataPath();
173
174         if (dataPath.empty())
175                 return 1;
176
177         bool moveUserData = !PathExists(dataPath);
178
179         /* perform open heart surgery on the user's data dirs - yay */
180         if (moveUserData) {
181                 MkDir(dataPath.c_str());
182
183                 std::string oldNameEtc = installPath + "\\etc";
184                 std::string newNameEtc = dataPath + "\\etc";
185                 if (!CopyDirectory(oldNameEtc, newNameEtc))
186                         return 1;
187
188                 std::string oldNameVar = installPath + "\\var";
189                 std::string newNameVar = dataPath + "\\var";
190                 if (!CopyDirectory(oldNameVar, newNameVar))
191                         return 1;
192         }
193
194         ExecuteCommand(uninstallerPath, "/S _?=" + installPath);
195
196         _unlink(uninstallerPath.c_str());
197
198         if (moveUserData) {
199                 std::string oldNameEtc = installPath + "\\etc";
200                 if (!DeleteDirectory(oldNameEtc))
201                         return 1;
202
203                 std::string oldNameVar = installPath + "\\var";
204                 if (!DeleteDirectory(oldNameVar))
205                         return 1;
206
207                 _rmdir(installPath.c_str());
208         }
209
210         return 0;
211 }
212
213 static int InstallIcinga(void)
214 {
215         std::string installDir = GetIcingaInstallPath();
216         std::string dataDir = GetIcingaDataPath();
217
218         if (!PathExists(dataDir)) {
219                 std::string sourceDir = installDir + "\\share\\skel" + std::string(1, '\0');
220                 std::string destinationDir = dataDir + std::string(1, '\0');
221
222                 SHFILEOPSTRUCT fop;
223                 fop.wFunc = FO_COPY;
224                 fop.pFrom = sourceDir.c_str();
225                 fop.pTo = destinationDir.c_str();
226                 fop.fFlags = FOF_NO_UI | FOF_NOCOPYSECURITYATTRIBS;
227
228                 if (SHFileOperation(&fop) != 0)
229                         return 1;
230
231                 MkDirP(dataDir + "/etc/icinga2/pki");
232                 MkDirP(dataDir + "/var/cache/icinga2");
233                 MkDirP(dataDir + "/var/lib/icinga2/certs");
234                 MkDirP(dataDir + "/var/lib/icinga2/certificate-requests");
235                 MkDirP(dataDir + "/var/lib/icinga2/agent/inventory");
236                 MkDirP(dataDir + "/var/lib/icinga2/api/config");
237                 MkDirP(dataDir + "/var/lib/icinga2/api/log");
238                 MkDirP(dataDir + "/var/lib/icinga2/api/zones");
239                 MkDirP(dataDir + "/var/log/icinga2/compat/archive");
240                 MkDirP(dataDir + "/var/log/icinga2/crash");
241                 MkDirP(dataDir + "/var/run/icinga2/cmd");
242                 MkDirP(dataDir + "/var/spool/icinga2/perfdata");
243                 MkDirP(dataDir + "/var/spool/icinga2/tmp");
244         }
245
246         ExecuteCommand("icacls", "\"" + dataDir + "\" /grant *S-1-5-20:(oi)(ci)m");
247         ExecuteCommand("icacls", "\"" + dataDir + "\\etc\" /inheritance:r /grant:r *S-1-5-20:(oi)(ci)m *S-1-5-32-544:(oi)(ci)f");
248
249         ExecuteIcingaCommand("--scm-install daemon");
250
251         return 0;
252 }
253
254 static int UninstallIcinga(void)
255 {
256         ExecuteIcingaCommand("--scm-uninstall");
257
258         return 0;
259 }
260
261 /**
262 * Entry point for the installer application.
263 */
264 int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
265 {
266         CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
267
268         //AllocConsole();
269         int rc;
270
271         if (strcmp(lpCmdLine, "install") == 0) {
272                 rc = InstallIcinga();
273         } else if (strcmp(lpCmdLine, "uninstall") == 0) {
274                 rc = UninstallIcinga();
275         } else if (strcmp(lpCmdLine, "upgrade-nsis") == 0) {
276                 rc = UpgradeNSIS();
277         } else {
278                 MessageBox(nullptr, "This application should only be run by the MSI installer package.", "Icinga 2 Installer", MB_ICONWARNING);
279                 rc = 1;
280         }
281
282         //::Sleep(3000s);
283
284         return rc;
285 }