]>
Commit | Line | Data |
---|---|---|
bc78a6d5 RC |
1 | /* |
2 | * Copyright (c) 2001, Jan Nieuwenhuizen. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * A copy of the GNU General Public License can be found at | |
10 | * http://www.gnu.org/ | |
11 | * | |
12 | * Written by DJ Delorie <dj@cygnus.com> | |
13 | * Jan Nieuwenhuizen <janneke@gnu.org> | |
14 | * | |
15 | */ | |
16 | ||
17 | /* The purpose of this file is to provide functions for the invocation | |
18 | of install scripts. */ | |
19 | ||
bc78a6d5 RC |
20 | #include "win32.h" |
21 | #include <stdlib.h> | |
22 | #include <unistd.h> | |
23 | #include <stdio.h> | |
1069407c | 24 | #include "LogSingleton.h" |
3c054baf | 25 | #include "filemanip.h" |
bc78a6d5 RC |
26 | #include "mount.h" |
27 | #include "io_stream.h" | |
ad646f43 | 28 | #include "script.h" |
1069407c | 29 | #include "mkdir.h" |
c3d07c13 AK |
30 | #include "state.h" |
31 | #include "resource.h" | |
5a85457b CF |
32 | #if HAVE_ALLOCA_H |
33 | #include <alloca.h> | |
34 | #else | |
35 | #ifndef alloca | |
36 | #define alloca __builtin_alloca | |
37 | #endif | |
38 | #endif | |
bc78a6d5 | 39 | |
390a9146 | 40 | static std::string sh, dash; |
b56d411d | 41 | static const char *cmd; |
5a85457b CF |
42 | |
43 | static void | |
44 | sanitize_PATH () | |
45 | { | |
46 | char dummy; | |
47 | DWORD len = GetEnvironmentVariable ("PATH", &dummy, 0); | |
48 | char *path = (char *) alloca (len + 1); | |
49 | GetEnvironmentVariable ("PATH", path, len); | |
50 | std::string newpath = backslash (cygpath ("/bin") + ";" | |
51 | + cygpath ("/usr/sbin") + ";" | |
52 | + cygpath ("/sbin")); | |
7cc4d95e | 53 | len = (UINT) GetSystemWindowsDirectory (&dummy, 0); |
5a85457b | 54 | char *system_root = (char *) alloca (len + 2); |
7cc4d95e | 55 | GetSystemWindowsDirectory (system_root, len--); |
5a85457b CF |
56 | if (system_root[len - 1] != '\\') |
57 | { | |
58 | system_root[len] = '\\'; | |
59 | system_root[++len] = '\0'; | |
60 | } | |
61 | for (char *p = strtok (path, ";"); p; p = strtok (NULL, ";")) | |
62 | { | |
63 | size_t plen = strlen (p); | |
64 | size_t cmplen = plen == (len - 1) ? plen : len; | |
65 | if (strncasecmp (system_root, p, cmplen) == 0) | |
66 | { | |
67 | newpath += ";"; | |
68 | newpath += p; | |
69 | } | |
70 | } | |
71 | SetEnvironmentVariable ("PATH", newpath.c_str()); | |
72 | } | |
73 | ||
bc78a6d5 RC |
74 | |
75 | void | |
76 | init_run_script () | |
77 | { | |
5a85457b CF |
78 | static bool initialized; |
79 | if (initialized) | |
80 | return; | |
81 | ||
82 | initialized = true; | |
83 | ||
84 | char *env = GetEnvironmentStrings (); | |
85 | if (env) | |
bc78a6d5 | 86 | { |
5a85457b CF |
87 | for (char *p = env; *p; p = strchr (p, '\0') + 1) |
88 | { | |
89 | char *eq = strchr (p, '='); | |
90 | *eq = '\0'; | |
91 | if (strcasecmp (p, "comspec") != 0 | |
92 | && strcasecmp (p, "path") != 0 | |
93 | && strncasecmp (p, "system", 7) != 0 | |
94 | && strncasecmp (p, "user", 4) != 0 | |
95 | && strcasecmp (p, "windir") != 0) | |
96 | SetEnvironmentVariable (p, NULL); | |
97 | p = eq + 1; | |
98 | } | |
99 | FreeEnvironmentStrings (env); | |
bc78a6d5 | 100 | } |
5a85457b | 101 | |
d2a3615c | 102 | SetEnvironmentVariable ("CYGWINROOT", get_root_dir ().c_str()); |
c3d07c13 AK |
103 | SetEnvironmentVariable ("CYGWINFORALL", |
104 | (root_scope == IDC_ROOT_SYSTEM) ? "-A" : NULL); | |
5a85457b CF |
105 | sanitize_PATH (); |
106 | SetEnvironmentVariable ("SHELL", "/bin/bash"); | |
107 | SetEnvironmentVariable ("TEMP", backslash (cygpath ("/tmp")).c_str ()); | |
108 | SetEnvironmentVariable ("TERM", "dumb"); | |
109 | SetEnvironmentVariable ("TMP", "/tmp"); | |
bc78a6d5 | 110 | |
390a9146 AG |
111 | sh = backslash (cygpath ("/bin/bash.exe")); |
112 | dash = backslash (cygpath ("/bin/dash.exe")); | |
113 | cmd = "cmd.exe"; | |
bc78a6d5 RC |
114 | } |
115 | ||
1069407c MB |
116 | class OutputLog |
117 | { | |
118 | public: | |
2bba98e8 | 119 | OutputLog (const std::string& filename); |
1069407c MB |
120 | ~OutputLog (); |
121 | HANDLE handle () { return _handle; } | |
122 | BOOL isValid () { return _handle != INVALID_HANDLE_VALUE; } | |
123 | BOOL isEmpty () { return GetFileSize (_handle, NULL) == 0; } | |
124 | friend std::ostream &operator<< (std::ostream &, OutputLog &); | |
125 | private: | |
126 | enum { BUFLEN = 1000 }; | |
127 | HANDLE _handle; | |
2bba98e8 | 128 | std::string _filename; |
1069407c MB |
129 | void out_to(std::ostream &); |
130 | }; | |
131 | ||
2bba98e8 | 132 | OutputLog::OutputLog (const std::string& filename) |
1069407c MB |
133 | : _handle(INVALID_HANDLE_VALUE), _filename(filename) |
134 | { | |
135 | if (!_filename.size()) | |
136 | return; | |
137 | ||
138 | SECURITY_ATTRIBUTES sa; | |
139 | memset (&sa, 0, sizeof (sa)); | |
140 | sa.nLength = sizeof (sa); | |
141 | sa.bInheritHandle = TRUE; | |
142 | sa.lpSecurityDescriptor = NULL; | |
143 | ||
b41c2908 | 144 | if (mkdir_p (0, backslash (cygpath (_filename)).c_str(), 0755)) |
1069407c MB |
145 | return; |
146 | ||
d2a3615c | 147 | _handle = CreateFile (backslash (cygpath (_filename)).c_str(), |
1069407c | 148 | GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, |
b41c2908 CV |
149 | &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, |
150 | NULL); | |
1069407c MB |
151 | |
152 | if (_handle == INVALID_HANDLE_VALUE) | |
153 | { | |
157dc2b8 | 154 | Log (LOG_PLAIN) << "error: Unable to redirect output to '" << _filename |
1069407c MB |
155 | << "'; using console" << endLog; |
156 | } | |
157 | } | |
158 | ||
159 | OutputLog::~OutputLog () | |
160 | { | |
161 | if (_handle != INVALID_HANDLE_VALUE) | |
162 | CloseHandle (_handle); | |
163 | if (_filename.size() && | |
d2a3615c | 164 | !DeleteFile(backslash (cygpath (_filename)).c_str())) |
1069407c | 165 | { |
157dc2b8 | 166 | Log (LOG_PLAIN) << "error: Unable to remove temporary file '" << _filename |
1069407c MB |
167 | << "'" << endLog; |
168 | } | |
169 | } | |
170 | ||
171 | std::ostream & | |
172 | operator<< (std::ostream &out, OutputLog &log) | |
173 | { | |
174 | log.out_to(out); | |
175 | return out; | |
176 | } | |
177 | ||
178 | void | |
179 | OutputLog::out_to(std::ostream &out) | |
180 | { | |
181 | char buf[BUFLEN]; | |
182 | DWORD num; | |
183 | FlushFileBuffers (_handle); | |
184 | SetFilePointer(_handle, 0, NULL, FILE_BEGIN); | |
54978a88 | 185 | |
1069407c MB |
186 | while (ReadFile(_handle, buf, BUFLEN-1, &num, NULL) && num != 0) |
187 | { | |
188 | buf[num] = '\0'; | |
189 | out << buf; | |
190 | } | |
191 | ||
192 | SetFilePointer(_handle, 0, NULL, FILE_END); | |
193 | } | |
194 | ||
f2952a6c JT |
195 | int |
196 | run (const char *cmdline) | |
bc78a6d5 | 197 | { |
f2952a6c | 198 | |
bc78a6d5 RC |
199 | STARTUPINFO si; |
200 | PROCESS_INFORMATION pi; | |
1069407c | 201 | DWORD flags = CREATE_NEW_CONSOLE; |
534c25cc | 202 | DWORD exitCode = 0; |
1069407c | 203 | BOOL inheritHandles = FALSE; |
534c25cc | 204 | BOOL exitCodeValid = FALSE; |
bc78a6d5 | 205 | |
157dc2b8 | 206 | Log (LOG_PLAIN) << "running: " << cmdline << endLog; |
f2952a6c JT |
207 | |
208 | char tmp_pat[] = "/var/log/setup.log.runXXXXXXX"; | |
209 | OutputLog file_out = std::string (mktemp (tmp_pat)); | |
210 | ||
bc78a6d5 RC |
211 | memset (&pi, 0, sizeof (pi)); |
212 | memset (&si, 0, sizeof (si)); | |
213 | si.cb = sizeof (si); | |
214 | si.lpTitle = (char *) "Cygwin Setup Post-Install Script"; | |
215 | si.dwFlags = STARTF_USEPOSITION; | |
216 | ||
1069407c MB |
217 | if (file_out.isValid ()) |
218 | { | |
219 | inheritHandles = TRUE; | |
220 | si.dwFlags |= STARTF_USESTDHANDLES; | |
5524909e | 221 | si.hStdInput = INVALID_HANDLE_VALUE; |
1069407c MB |
222 | si.hStdOutput = file_out.handle (); |
223 | si.hStdError = file_out.handle (); | |
224 | si.dwFlags |= STARTF_USESHOWWINDOW; | |
1069407c | 225 | si.wShowWindow = SW_HIDE; |
c6263b96 | 226 | flags = CREATE_NO_WINDOW; |
1069407c MB |
227 | } |
228 | ||
f2952a6c | 229 | BOOL createSucceeded = CreateProcess (0, (char *)cmdline, 0, 0, inheritHandles, |
d2a3615c | 230 | flags, 0, get_root_dir ().c_str(), |
1069407c | 231 | &si, &pi); |
bc78a6d5 | 232 | |
92f9402a | 233 | if (createSucceeded) |
534c25cc BD |
234 | { |
235 | WaitForSingleObject (pi.hProcess, INFINITE); | |
236 | exitCodeValid = GetExitCodeProcess(pi.hProcess, &exitCode); | |
237 | } | |
92f9402a RC |
238 | CloseHandle(pi.hProcess); |
239 | CloseHandle(pi.hThread); | |
f2952a6c JT |
240 | |
241 | if (!file_out.isEmpty ()) | |
157dc2b8 | 242 | Log (LOG_BABBLE) << file_out << endLog; |
f2952a6c | 243 | |
534c25cc BD |
244 | if (exitCodeValid) |
245 | return exitCode; | |
246 | return -GetLastError(); | |
bc78a6d5 RC |
247 | } |
248 | ||
534c25cc | 249 | int |
34ea5b6d | 250 | Script::run() const |
a0e56f67 | 251 | { |
54978a88 AG |
252 | if ("done" == scriptExtension) |
253 | return NO_ERROR; | |
254 | if (0 == scriptExtension.size()) | |
534c25cc | 255 | return -ERROR_INVALID_DATA; |
bc78a6d5 | 256 | |
a875b471 BD |
257 | /* Bail here if the script file does not exist. This can happen for |
258 | example in the case of tetex-* where two or more packages contain a | |
259 | postinstall script by the same name. When we are called the second | |
260 | time the file has already been renamed to .done, and if we don't | |
54978a88 | 261 | return here we end up erroneously deleting this .done file. */ |
a875b471 BD |
262 | std::string windowsName = backslash (cygpath (scriptName)); |
263 | if (_access (windowsName.c_str(), 0) == -1) | |
1069407c | 264 | { |
157dc2b8 | 265 | Log (LOG_PLAIN) << "can't run " << scriptName << ": No such file" |
a875b471 BD |
266 | << endLog; |
267 | return -ERROR_INVALID_DATA; | |
1069407c | 268 | } |
1069407c | 269 | |
a875b471 | 270 | int retval; |
7cc4d95e | 271 | char cmdline[CYG_PATH_MAX]; |
f2952a6c | 272 | |
54978a88 | 273 | if (sh.size() && ("sh" == scriptExtension)) |
bc78a6d5 | 274 | { |
f2952a6c JT |
275 | sprintf (cmdline, "%s %s \"%s\"", sh.c_str(), "--norc --noprofile", scriptName.c_str()); |
276 | retval = ::run (cmdline); | |
bc78a6d5 | 277 | } |
390a9146 AG |
278 | else if (dash.size() && ("dash" == scriptExtension)) |
279 | { | |
280 | sprintf (cmdline, "%s \"%s\"", dash.c_str(), scriptName.c_str()); | |
281 | retval = ::run (cmdline); | |
282 | } | |
283 | else if (cmd && (("bat" == scriptExtension) || | |
284 | ("cmd" == scriptExtension))) | |
bc78a6d5 | 285 | { |
f2952a6c JT |
286 | sprintf (cmdline, "%s %s \"%s\"", cmd, "/c", windowsName.c_str()); |
287 | retval = ::run (cmdline); | |
bc78a6d5 RC |
288 | } |
289 | else | |
534c25cc | 290 | return -ERROR_INVALID_DATA; |
bc78a6d5 | 291 | |
b2b35a6e | 292 | if (retval) |
157dc2b8 | 293 | Log (LOG_PLAIN) << "abnormal exit: exit code=" << retval << endLog; |
534c25cc | 294 | |
0707786e | 295 | /* if .done file exists then delete it otherwise just ignore no file error */ |
2bba98e8 | 296 | io_stream::remove ("cygfile://" + scriptName + ".done"); |
bc78a6d5 | 297 | |
54978a88 AG |
298 | /* don't rename the script as .done if it didn't run successfully or |
299 | if this script is marked to be always run */ | |
300 | if (!retval && ("p" != scriptType)) | |
0707786e JT |
301 | io_stream::move ("cygfile://" + scriptName, |
302 | "cygfile://" + scriptName + ".done"); | |
534c25cc BD |
303 | |
304 | return retval; | |
bc78a6d5 RC |
305 | } |
306 | ||
534c25cc | 307 | int |
2bba98e8 MB |
308 | try_run_script (const std::string& dir, |
309 | const std::string& fname, | |
310 | const std::string& ext) | |
bc78a6d5 | 311 | { |
2bba98e8 | 312 | if (io_stream::exists ("cygfile://" + dir + fname + ext)) |
534c25cc BD |
313 | return Script (dir + fname + ext).run (); |
314 | return NO_ERROR; | |
bc78a6d5 RC |
315 | } |
316 | ||
ad646f43 | 317 | bool |
2bba98e8 | 318 | Script::isAScript (const std::string& file) |
ad646f43 | 319 | { |
54978a88 AG |
320 | // is a directory path |
321 | if ('/' == file[file.size()-1]) | |
ad646f43 | 322 | return false; |
54978a88 AG |
323 | // file may start with /etc/postinstall/ or etc/postinstall/ |
324 | std::size_t found = file.find(ETCPostinstall+1); | |
325 | if (( found == 0) || | |
326 | ((found == 1) && (0 == file.find('/')))) | |
327 | return true; | |
328 | return false; | |
ad646f43 RC |
329 | } |
330 | ||
54978a88 AG |
331 | bool |
332 | Script::match (const std::string& stratum, const std::string& type) | |
ad646f43 | 333 | { |
54978a88 AG |
334 | // empty string for each parameter always matches |
335 | bool matchedStratum, matchedType; | |
336 | if ("done" == scriptExtension) | |
337 | return false; | |
338 | if (stratum.size()) | |
339 | matchedStratum = (std::string::npos != stratum.find(scriptStratum)); | |
340 | else | |
341 | matchedStratum = true; | |
342 | if (type.size()) | |
343 | matchedType = (std::string::npos != type.find(scriptType)); | |
344 | else | |
345 | matchedType = true; | |
346 | return matchedStratum && matchedType; | |
347 | } | |
348 | bool | |
349 | Script::is_p (const std::string& stratum) | |
350 | { | |
351 | return match( stratum, "p"); | |
352 | } | |
353 | bool | |
354 | Script::not_p (const std::string& stratum) | |
355 | { | |
356 | return match( stratum, allowedTypes+1); | |
357 | } | |
358 | ||
359 | Script::Script (const std::string& fileName) | |
360 | : scriptName (fileName), | |
361 | scriptBaseName (""), | |
362 | scriptExtension (""), | |
363 | scriptStratum ("_"), | |
364 | scriptType ("r") | |
365 | { | |
366 | std::size_t found; | |
367 | found = fileName.rfind('/'); | |
368 | if (found != std::string::npos) | |
369 | scriptBaseName = fileName.substr(found + 1); | |
370 | found = fileName.rfind('.'); | |
371 | if (found != std::string::npos) | |
372 | scriptExtension = fileName.substr(found + 1); | |
373 | if ( "_" == scriptBaseName.substr(2,1) && | |
374 | 0 == scriptBaseName.substr(1,1).find_first_of(allowedTypes) && | |
375 | "_" != scriptBaseName.substr(0,1) && // default stratum cannot be explicitly named | |
376 | 0 == scriptBaseName.substr(0,1).find_first_of(allowedStrata)) | |
377 | { | |
378 | // stratified script | |
379 | scriptStratum = scriptBaseName.substr(0,1); | |
380 | scriptType = scriptBaseName.substr(1,1); | |
381 | } | |
382 | // Let's hope people won't uninstall packages before installing bash | |
383 | init_run_script (); | |
ad646f43 RC |
384 | } |
385 | ||
470f4928 | 386 | std::string |
39ba3555 | 387 | Script::baseName() const |
ad646f43 | 388 | { |
54978a88 | 389 | return scriptBaseName; |
ad646f43 | 390 | } |
39ba3555 | 391 | |
2bba98e8 | 392 | std::string |
39ba3555 MB |
393 | Script::fullName() const |
394 | { | |
395 | return scriptName; | |
396 | } | |
54978a88 AG |
397 | |
398 | char const Script::ETCPostinstall[] = "/etc/postinstall/"; | |
399 | char const Script::allowedStrata[] = "0_z"; | |
400 | char const Script::allowedTypes[] = "pr"; |