]> cygwin.com Git - cygwin-apps/setup.git/blob - script.cc
* prereq.cc (PrereqChecker::getUnmetString): Improve dependency list
[cygwin-apps/setup.git] / script.cc
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
20 #if 0
21 static const char *cvsid =
22 "\n%%% $Id$\n";
23 #endif
24
25 #include "win32.h"
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <stdio.h>
29 #include "LogSingleton.h"
30 #include "filemanip.h"
31 #include "mount.h"
32 #include "io_stream.h"
33 #include "script.h"
34 #include "mkdir.h"
35 #if HAVE_ALLOCA_H
36 #include <alloca.h>
37 #else
38 #ifndef alloca
39 #define alloca __builtin_alloca
40 #endif
41 #endif
42
43 static std::string sh;
44 static const char *cmd;
45
46 static void
47 sanitize_PATH ()
48 {
49 char dummy;
50 DWORD len = GetEnvironmentVariable ("PATH", &dummy, 0);
51 char *path = (char *) alloca (len + 1);
52 GetEnvironmentVariable ("PATH", path, len);
53 std::string newpath = backslash (cygpath ("/bin") + ";"
54 + cygpath ("/usr/sbin") + ";"
55 + cygpath ("/sbin"));
56 len = (UINT) GetWindowsDirectory (&dummy, 0);
57 char *system_root = (char *) alloca (len + 2);
58 GetWindowsDirectory (system_root, len--);
59 if (system_root[len - 1] != '\\')
60 {
61 system_root[len] = '\\';
62 system_root[++len] = '\0';
63 }
64 for (char *p = strtok (path, ";"); p; p = strtok (NULL, ";"))
65 {
66 size_t plen = strlen (p);
67 size_t cmplen = plen == (len - 1) ? plen : len;
68 if (strncasecmp (system_root, p, cmplen) == 0)
69 {
70 newpath += ";";
71 newpath += p;
72 }
73 }
74 SetEnvironmentVariable ("PATH", newpath.c_str());
75 }
76
77
78 void
79 init_run_script ()
80 {
81 static bool initialized;
82 if (initialized)
83 return;
84
85 initialized = true;
86
87 char *env = GetEnvironmentStrings ();
88 if (env)
89 {
90 for (char *p = env; *p; p = strchr (p, '\0') + 1)
91 {
92 char *eq = strchr (p, '=');
93 *eq = '\0';
94 if (strcasecmp (p, "comspec") != 0
95 && strcasecmp (p, "path") != 0
96 && strncasecmp (p, "system", 7) != 0
97 && strncasecmp (p, "user", 4) != 0
98 && strcasecmp (p, "windir") != 0)
99 SetEnvironmentVariable (p, NULL);
100 p = eq + 1;
101 }
102 FreeEnvironmentStrings (env);
103 }
104
105 SetEnvironmentVariable ("CYGWINROOT", get_root_dir ().c_str());
106 sanitize_PATH ();
107 SetEnvironmentVariable ("SHELL", "/bin/bash");
108 SetEnvironmentVariable ("TEMP", backslash (cygpath ("/tmp")).c_str ());
109 SetEnvironmentVariable ("TERM", "dumb");
110 SetEnvironmentVariable ("TMP", "/tmp");
111
112 sh = backslash (cygpath ("/bin/bash.exe"));
113 cmd = IsWindowsNT () ? "cmd.exe" : "command.com";
114 }
115
116 class OutputLog
117 {
118 public:
119 OutputLog (const std::string& filename);
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;
128 std::string _filename;
129 void out_to(std::ostream &);
130 };
131
132 OutputLog::OutputLog (const std::string& filename)
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
144 if (mkdir_p (0, backslash (cygpath (_filename)).c_str(), 0755))
145 return;
146
147 _handle = CreateFile (backslash (cygpath (_filename)).c_str(),
148 GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
149 &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
150 NULL);
151
152 if (_handle == INVALID_HANDLE_VALUE)
153 {
154 log(LOG_PLAIN) << "error: Unable to redirect output to '" << _filename
155 << "'; using console" << endLog;
156 }
157 }
158
159 OutputLog::~OutputLog ()
160 {
161 if (_handle != INVALID_HANDLE_VALUE)
162 CloseHandle (_handle);
163 if (_filename.size() &&
164 !DeleteFile(backslash (cygpath (_filename)).c_str()))
165 {
166 log(LOG_PLAIN) << "error: Unable to remove temporary file '" << _filename
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);
185
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
195 static int
196 run (const char *sh, const char *args, const char *file, OutputLog &file_out)
197 {
198 char cmdline[MAX_PATH];
199 STARTUPINFO si;
200 PROCESS_INFORMATION pi;
201 DWORD flags = CREATE_NEW_CONSOLE;
202 DWORD exitCode = 0;
203 BOOL inheritHandles = FALSE;
204 BOOL exitCodeValid = FALSE;
205
206 sprintf (cmdline, "%s %s %s", sh, args, file);
207 memset (&pi, 0, sizeof (pi));
208 memset (&si, 0, sizeof (si));
209 si.cb = sizeof (si);
210 si.lpTitle = (char *) "Cygwin Setup Post-Install Script";
211 si.dwFlags = STARTF_USEPOSITION;
212
213 if (file_out.isValid ())
214 {
215 inheritHandles = TRUE;
216 si.dwFlags |= STARTF_USESTDHANDLES;
217 si.hStdInput = INVALID_HANDLE_VALUE;
218 si.hStdOutput = file_out.handle ();
219 si.hStdError = file_out.handle ();
220 si.dwFlags |= STARTF_USESHOWWINDOW;
221 si.wShowWindow = SW_HIDE;
222 flags = CREATE_NO_WINDOW; // Note: this is ignored on Win9x
223 }
224
225 BOOL createSucceeded = CreateProcess (0, cmdline, 0, 0, inheritHandles,
226 flags, 0, get_root_dir ().c_str(),
227 &si, &pi);
228
229 if (createSucceeded)
230 {
231 WaitForSingleObject (pi.hProcess, INFINITE);
232 exitCodeValid = GetExitCodeProcess(pi.hProcess, &exitCode);
233 }
234 CloseHandle(pi.hProcess);
235 CloseHandle(pi.hThread);
236 if (exitCodeValid)
237 return exitCode;
238 return -GetLastError();
239 }
240
241 char const *
242 Script::extension() const
243 {
244 return strrchr (scriptName.c_str(), '.');
245 }
246
247 int
248 Script::run() const
249 {
250 if (!extension())
251 return -ERROR_INVALID_DATA;
252
253 /* Bail here if the script file does not exist. This can happen for
254 example in the case of tetex-* where two or more packages contain a
255 postinstall script by the same name. When we are called the second
256 time the file has already been renamed to .done, and if we don't
257 return here we end up erroniously deleting this .done file. */
258 std::string windowsName = backslash (cygpath (scriptName));
259 if (_access (windowsName.c_str(), 0) == -1)
260 {
261 log(LOG_PLAIN) << "can't run " << scriptName << ": No such file"
262 << endLog;
263 return -ERROR_INVALID_DATA;
264 }
265
266 int retval;
267 char tmp_pat[] = "/var/log/setup.log.postinstallXXXXXXX";
268 OutputLog file_out = std::string (mktemp (tmp_pat));
269 if (sh.size() && stricmp (extension(), ".sh") == 0)
270 {
271 log(LOG_PLAIN) << "running: " << sh << " --norc --noprofile " << scriptName << endLog;
272 retval = ::run (sh.c_str(), "--norc --noprofile", scriptName.c_str(), file_out);
273 }
274 else if (cmd && stricmp (extension(), ".bat") == 0)
275 {
276 log(LOG_PLAIN) << "running: " << cmd << " /c " << windowsName << endLog;
277 retval = ::run (cmd, "/c", windowsName.c_str(), file_out);
278 }
279 else
280 return -ERROR_INVALID_DATA;
281
282 if (!file_out.isEmpty ())
283 log(LOG_BABBLE) << file_out << endLog;
284
285 if (retval)
286 log(LOG_PLAIN) << "abnormal exit: exit code=" << retval << endLog;
287
288 /* if .done file exists then delete it otherwise just ignore no file error */
289 io_stream::remove ("cygfile://" + scriptName + ".done");
290
291 /* don't rename the script as .done if it didn't run successfully */
292 if (!retval)
293 io_stream::move ("cygfile://" + scriptName,
294 "cygfile://" + scriptName + ".done");
295
296 return retval;
297 }
298
299 int
300 try_run_script (const std::string& dir,
301 const std::string& fname,
302 const std::string& ext)
303 {
304 if (io_stream::exists ("cygfile://" + dir + fname + ext))
305 return Script (dir + fname + ext).run ();
306 return NO_ERROR;
307 }
308
309 char const Script::ETCPostinstall[] = "/etc/postinstall/";
310
311 bool
312 Script::isAScript (const std::string& file)
313 {
314 /* file may be /etc/postinstall or etc/postinstall */
315 if (casecompare(file, ETCPostinstall, sizeof(ETCPostinstall)-1) &&
316 casecompare(file, ETCPostinstall+1, sizeof(ETCPostinstall)-2))
317 return false;
318 if (file.c_str()[file.size() - 1] == '/')
319 return false;
320 return true;
321 }
322
323 Script::Script (const std::string& fileName) : scriptName (fileName)
324 {
325
326 }
327
328 std::string
329 Script::baseName() const
330 {
331 std::string result = scriptName;
332 result = result.substr(result.rfind('/') + 1);
333 return result;
334 }
335
336 std::string
337 Script::fullName() const
338 {
339 return scriptName;
340 }
This page took 0.052453 seconds and 5 git commands to generate.