]> cygwin.com Git - cygwin-apps/setup.git/blob - script.cc
Make PrereqChecker::setTrust() a static method
[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 #include "win32.h"
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <stdio.h>
24 #include "LogSingleton.h"
25 #include "filemanip.h"
26 #include "mount.h"
27 #include "io_stream.h"
28 #include "script.h"
29 #include "mkdir.h"
30 #include "state.h"
31 #include "resource.h"
32 #if HAVE_ALLOCA_H
33 #include <alloca.h>
34 #else
35 #ifndef alloca
36 #define alloca __builtin_alloca
37 #endif
38 #endif
39
40 static std::string sh, dash;
41 static const char *cmd;
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"));
53 len = (UINT) GetSystemWindowsDirectory (&dummy, 0);
54 char *system_root = (char *) alloca (len + 2);
55 GetSystemWindowsDirectory (system_root, len--);
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
74
75 void
76 init_run_script ()
77 {
78 static bool initialized;
79 if (initialized)
80 return;
81
82 initialized = true;
83
84 char *env = GetEnvironmentStrings ();
85 if (env)
86 {
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);
100 }
101
102 SetEnvironmentVariable ("CYGWINROOT", get_root_dir ().c_str());
103 SetEnvironmentVariable ("CYGWINFORALL",
104 (root_scope == IDC_ROOT_SYSTEM) ? "-A" : NULL);
105 sanitize_PATH ();
106 SetEnvironmentVariable ("SHELL", "/bin/bash");
107 SetEnvironmentVariable ("TEMP", backslash (cygpath ("/tmp")).c_str ());
108 SetEnvironmentVariable ("TERM", "dumb");
109 SetEnvironmentVariable ("TMP", "/tmp");
110
111 sh = backslash (cygpath ("/bin/bash.exe"));
112 dash = backslash (cygpath ("/bin/dash.exe"));
113 cmd = "cmd.exe";
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 int
196 run (const char *cmdline)
197 {
198
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 Log (LOG_PLAIN) << "running: " << cmdline << endLog;
207
208 char tmp_pat[] = "/var/log/setup.log.runXXXXXXX";
209 OutputLog file_out = std::string (mktemp (tmp_pat));
210
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
217 if (file_out.isValid ())
218 {
219 inheritHandles = TRUE;
220 si.dwFlags |= STARTF_USESTDHANDLES;
221 si.hStdInput = INVALID_HANDLE_VALUE;
222 si.hStdOutput = file_out.handle ();
223 si.hStdError = file_out.handle ();
224 si.dwFlags |= STARTF_USESHOWWINDOW;
225 si.wShowWindow = SW_HIDE;
226 flags = CREATE_NO_WINDOW;
227 }
228
229 BOOL createSucceeded = CreateProcess (0, (char *)cmdline, 0, 0, inheritHandles,
230 flags, 0, get_root_dir ().c_str(),
231 &si, &pi);
232
233 if (createSucceeded)
234 {
235 WaitForSingleObject (pi.hProcess, INFINITE);
236 exitCodeValid = GetExitCodeProcess(pi.hProcess, &exitCode);
237 }
238 CloseHandle(pi.hProcess);
239 CloseHandle(pi.hThread);
240
241 if (!file_out.isEmpty ())
242 Log (LOG_BABBLE) << file_out << endLog;
243
244 if (exitCodeValid)
245 return exitCode;
246 return -GetLastError();
247 }
248
249 int
250 Script::run() const
251 {
252 if ("done" == scriptExtension)
253 return NO_ERROR;
254 if (0 == scriptExtension.size())
255 return -ERROR_INVALID_DATA;
256
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
261 return here we end up erroneously deleting this .done file. */
262 std::string windowsName = backslash (cygpath (scriptName));
263 if (_access (windowsName.c_str(), 0) == -1)
264 {
265 Log (LOG_PLAIN) << "can't run " << scriptName << ": No such file"
266 << endLog;
267 return -ERROR_INVALID_DATA;
268 }
269
270 int retval;
271 char cmdline[CYG_PATH_MAX];
272
273 if (sh.size() && ("sh" == scriptExtension))
274 {
275 sprintf (cmdline, "%s %s \"%s\"", sh.c_str(), "--norc --noprofile", scriptName.c_str());
276 retval = ::run (cmdline);
277 }
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)))
285 {
286 sprintf (cmdline, "%s %s \"%s\"", cmd, "/c", windowsName.c_str());
287 retval = ::run (cmdline);
288 }
289 else
290 return -ERROR_INVALID_DATA;
291
292 if (retval)
293 Log (LOG_PLAIN) << "abnormal exit: exit code=" << retval << endLog;
294
295 /* if .done file exists then delete it otherwise just ignore no file error */
296 io_stream::remove ("cygfile://" + scriptName + ".done");
297
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))
301 io_stream::move ("cygfile://" + scriptName,
302 "cygfile://" + scriptName + ".done");
303
304 return retval;
305 }
306
307 int
308 try_run_script (const std::string& dir,
309 const std::string& fname,
310 const std::string& ext)
311 {
312 if (io_stream::exists ("cygfile://" + dir + fname + ext))
313 return Script (dir + fname + ext).run ();
314 return NO_ERROR;
315 }
316
317 bool
318 Script::isAScript (const std::string& file)
319 {
320 // is a directory path
321 if ('/' == file[file.size()-1])
322 return false;
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;
329 }
330
331 bool
332 Script::match (const std::string& stratum, const std::string& type)
333 {
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 ();
384 }
385
386 std::string
387 Script::baseName() const
388 {
389 return scriptBaseName;
390 }
391
392 std::string
393 Script::fullName() const
394 {
395 return scriptName;
396 }
397
398 char const Script::ETCPostinstall[] = "/etc/postinstall/";
399 char const Script::allowedStrata[] = "0_z";
400 char const Script::allowedTypes[] = "pr";
This page took 0.052907 seconds and 5 git commands to generate.