CI: Pass host triplet to bootstrap script
[cygwin-setup.git] / script.cc
blob6818f4b255c0696ed9beff4baa201377a5bb718a
1 /*
2 * Copyright (c) 2001, Jan Nieuwenhuizen.
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.
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
12 * Written by DJ Delorie <dj@cygnus.com>
13 * Jan Nieuwenhuizen <janneke@gnu.org>
17 /* The purpose of this file is to provide functions for the invocation
18 of install scripts. */
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 #include "mklink2.h"
41 static std::string sh, dash;
42 static const char *cmd;
44 static void
45 sanitize_PATH ()
47 char dummy;
48 DWORD len = GetEnvironmentVariable ("PATH", &dummy, 0);
49 char *path = (char *) alloca (len + 1);
50 GetEnvironmentVariable ("PATH", path, len);
51 std::string newpath = backslash (cygpath ("/bin") + ";"
52 + cygpath ("/usr/sbin") + ";"
53 + cygpath ("/sbin"));
54 len = (UINT) GetSystemWindowsDirectory (&dummy, 0);
55 char *system_root = (char *) alloca (len + 2);
56 GetSystemWindowsDirectory (system_root, len--);
57 if (system_root[len - 1] != '\\')
59 system_root[len] = '\\';
60 system_root[++len] = '\0';
62 for (char *p = strtok (path, ";"); p; p = strtok (NULL, ";"))
64 size_t plen = strlen (p);
65 size_t cmplen = plen == (len - 1) ? plen : len;
66 if (strncasecmp (system_root, p, cmplen) == 0)
68 newpath += ";";
69 newpath += p;
72 SetEnvironmentVariable ("PATH", newpath.c_str());
75 static void
76 modify_CYGWIN ()
78 std::string cygwin;
79 DWORD len = GetEnvironmentVariable ("CYGWIN", &cygwin[0], 0);
80 if (len > 0)
82 cygwin.resize(len);
83 GetEnvironmentVariable ("CYGWIN", &cygwin[0], len);
84 cygwin.resize(len-1); // trim terminating null
85 cygwin.append(" ");
88 switch (symlinkType)
90 case SymlinkTypeNative:
91 cygwin.append("winsymlinks:native");
92 break;
94 case SymlinkTypeWsl:
95 cygwin.append("winsymlinks:wsl");
96 break;
98 case SymlinkTypeMagic:
99 cygwin.append("winsymlinks:sys");
100 break;
102 case SymlinkTypeShortcut: /* not yet implemented */
103 default:
104 break;
107 SetEnvironmentVariable ("CYGWIN", cygwin.c_str());
110 void
111 init_run_script ()
113 static bool initialized;
114 if (initialized)
115 return;
117 initialized = true;
119 char *env = GetEnvironmentStrings ();
120 if (env)
122 for (char *p = env; *p; p = strchr (p, '\0') + 1)
124 char *eq = strchr (p, '=');
125 *eq = '\0';
126 if (strcasecmp (p, "comspec") != 0
127 && strcasecmp (p, "cygwin") != 0
128 && strcasecmp (p, "path") != 0
129 && strncasecmp (p, "system", 7) != 0
130 && strncasecmp (p, "user", 4) != 0
131 && strcasecmp (p, "windir") != 0)
132 SetEnvironmentVariable (p, NULL);
133 p = eq + 1;
135 FreeEnvironmentStrings (env);
138 modify_CYGWIN();
139 SetEnvironmentVariable ("CYGWINROOT", get_root_dir ().c_str());
140 SetEnvironmentVariable ("CYGWINFORALL",
141 (root_scope == IDC_ROOT_SYSTEM) ? "-A" : NULL);
142 sanitize_PATH ();
143 SetEnvironmentVariable ("SHELL", "/bin/bash");
144 SetEnvironmentVariable ("TEMP", backslash (cygpath ("/tmp")).c_str ());
145 SetEnvironmentVariable ("TERM", "dumb");
146 SetEnvironmentVariable ("TMP", "/tmp");
148 sh = backslash (cygpath ("/bin/bash.exe"));
149 dash = backslash (cygpath ("/bin/dash.exe"));
150 cmd = "cmd.exe";
153 class OutputLog
155 public:
156 OutputLog (const std::string& filename);
157 ~OutputLog ();
158 HANDLE handle () { return _handle; }
159 BOOL isValid () { return _handle != INVALID_HANDLE_VALUE; }
160 BOOL isEmpty () { return GetFileSize (_handle, NULL) == 0; }
161 friend std::ostream &operator<< (std::ostream &, OutputLog &);
162 private:
163 enum { BUFLEN = 1000 };
164 HANDLE _handle;
165 std::string _filename;
166 void out_to(std::ostream &);
169 OutputLog::OutputLog (const std::string& filename)
170 : _handle(INVALID_HANDLE_VALUE), _filename(filename)
172 if (!_filename.size())
173 return;
175 SECURITY_ATTRIBUTES sa;
176 memset (&sa, 0, sizeof (sa));
177 sa.nLength = sizeof (sa);
178 sa.bInheritHandle = TRUE;
179 sa.lpSecurityDescriptor = NULL;
181 if (mkdir_p (0, backslash (cygpath (_filename)).c_str(), 0755))
182 return;
184 _handle = CreateFile (backslash (cygpath (_filename)).c_str(),
185 GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
186 &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
187 NULL);
189 if (_handle == INVALID_HANDLE_VALUE)
191 Log (LOG_PLAIN) << "error: Unable to redirect output to '" << _filename
192 << "'; using console" << endLog;
196 OutputLog::~OutputLog ()
198 if (_handle != INVALID_HANDLE_VALUE)
199 CloseHandle (_handle);
200 if (_filename.size() &&
201 !DeleteFile(backslash (cygpath (_filename)).c_str()))
203 Log (LOG_PLAIN) << "error: Unable to remove temporary file '" << _filename
204 << "'" << endLog;
208 std::ostream &
209 operator<< (std::ostream &out, OutputLog &log)
211 log.out_to(out);
212 return out;
215 void
216 OutputLog::out_to(std::ostream &out)
218 char buf[BUFLEN];
219 DWORD num;
220 FlushFileBuffers (_handle);
221 SetFilePointer(_handle, 0, NULL, FILE_BEGIN);
223 while (ReadFile(_handle, buf, BUFLEN-1, &num, NULL) && num != 0)
225 buf[num] = '\0';
226 out << buf;
229 SetFilePointer(_handle, 0, NULL, FILE_END);
233 run (const char *cmdline)
236 STARTUPINFO si;
237 PROCESS_INFORMATION pi;
238 DWORD flags = CREATE_NEW_CONSOLE;
239 DWORD exitCode = 0;
240 BOOL inheritHandles = FALSE;
241 BOOL exitCodeValid = FALSE;
243 Log (LOG_PLAIN) << "running: " << cmdline << endLog;
245 char tmp_pat[] = "/var/log/setup.log.runXXXXXXX";
246 OutputLog file_out = std::string (mktemp (tmp_pat));
248 memset (&pi, 0, sizeof (pi));
249 memset (&si, 0, sizeof (si));
250 si.cb = sizeof (si);
251 si.lpTitle = (char *) "Cygwin Setup Post-Install Script";
252 si.dwFlags = STARTF_USEPOSITION;
254 if (file_out.isValid ())
256 inheritHandles = TRUE;
257 si.dwFlags |= STARTF_USESTDHANDLES;
258 si.hStdInput = INVALID_HANDLE_VALUE;
259 si.hStdOutput = file_out.handle ();
260 si.hStdError = file_out.handle ();
261 si.dwFlags |= STARTF_USESHOWWINDOW;
262 si.wShowWindow = SW_HIDE;
263 flags = CREATE_NO_WINDOW;
266 BOOL createSucceeded = CreateProcess (0, (char *)cmdline, 0, 0, inheritHandles,
267 flags, 0, get_root_dir ().c_str(),
268 &si, &pi);
270 if (createSucceeded)
272 WaitForSingleObject (pi.hProcess, INFINITE);
273 exitCodeValid = GetExitCodeProcess(pi.hProcess, &exitCode);
275 CloseHandle(pi.hProcess);
276 CloseHandle(pi.hThread);
278 if (!file_out.isEmpty ())
279 Log (LOG_BABBLE) << file_out << endLog;
281 if (exitCodeValid)
282 return exitCode;
283 return -GetLastError();
287 Script::run() const
289 if ("done" == scriptExtension)
290 return NO_ERROR;
291 if (0 == scriptExtension.size())
292 return -ERROR_INVALID_DATA;
294 /* Bail here if the script file does not exist. This can happen for
295 example in the case of tetex-* where two or more packages contain a
296 postinstall script by the same name. When we are called the second
297 time the file has already been renamed to .done, and if we don't
298 return here we end up erroneously deleting this .done file. */
299 std::string windowsName = backslash (cygpath (scriptName));
300 if (_access (windowsName.c_str(), 0) == -1)
302 Log (LOG_PLAIN) << "can't run " << scriptName << ": No such file"
303 << endLog;
304 return -ERROR_INVALID_DATA;
307 int retval;
308 char cmdline[CYG_PATH_MAX];
310 if (sh.size() && ("sh" == scriptExtension))
312 sprintf (cmdline, "%s %s \"%s\"", sh.c_str(), "--norc --noprofile", scriptName.c_str());
313 retval = ::run (cmdline);
315 else if (dash.size() && ("dash" == scriptExtension))
317 sprintf (cmdline, "%s \"%s\"", dash.c_str(), scriptName.c_str());
318 retval = ::run (cmdline);
320 else if (cmd && (("bat" == scriptExtension) ||
321 ("cmd" == scriptExtension)))
323 sprintf (cmdline, "%s %s \"%s\"", cmd, "/c", windowsName.c_str());
324 retval = ::run (cmdline);
326 else
327 return -ERROR_INVALID_DATA;
329 if (retval)
330 Log (LOG_PLAIN) << "abnormal exit: exit code=" << retval << endLog;
332 /* if .done file exists then delete it otherwise just ignore no file error */
333 io_stream::remove ("cygfile://" + scriptName + ".done");
335 /* don't rename the script as .done if it didn't run successfully or
336 if this script is marked to be always run */
337 if (!retval && ("p" != scriptType))
338 io_stream::move ("cygfile://" + scriptName,
339 "cygfile://" + scriptName + ".done");
341 return retval;
345 try_run_script (const std::string& dir,
346 const std::string& fname,
347 const std::string& ext)
349 if (io_stream::exists ("cygfile://" + dir + fname + ext))
350 return Script (dir + fname + ext).run ();
351 return NO_ERROR;
354 bool
355 Script::isAScript (const std::string& file)
357 // is a directory path
358 if ('/' == file[file.size()-1])
359 return false;
360 // file may start with /etc/postinstall/ or etc/postinstall/
361 std::size_t found = file.find(ETCPostinstall+1);
362 if (( found == 0) ||
363 ((found == 1) && (0 == file.find('/'))))
364 return true;
365 return false;
368 bool
369 Script::match (const std::string& stratum, const std::string& type)
371 // empty string for each parameter always matches
372 bool matchedStratum, matchedType;
373 if ("done" == scriptExtension)
374 return false;
375 if (stratum.size())
376 matchedStratum = (std::string::npos != stratum.find(scriptStratum));
377 else
378 matchedStratum = true;
379 if (type.size())
380 matchedType = (std::string::npos != type.find(scriptType));
381 else
382 matchedType = true;
383 return matchedStratum && matchedType;
385 bool
386 Script::is_p (const std::string& stratum)
388 return match( stratum, "p");
390 bool
391 Script::not_p (const std::string& stratum)
393 return match( stratum, allowedTypes+1);
396 Script::Script (const std::string& fileName)
397 : scriptName (fileName),
398 scriptBaseName (""),
399 scriptExtension (""),
400 scriptStratum ("_"),
401 scriptType ("r")
403 std::size_t found;
404 found = fileName.rfind('/');
405 if (found != std::string::npos)
406 scriptBaseName = fileName.substr(found + 1);
407 found = fileName.rfind('.');
408 if (found != std::string::npos)
409 scriptExtension = fileName.substr(found + 1);
410 if ( "_" == scriptBaseName.substr(2,1) &&
411 0 == scriptBaseName.substr(1,1).find_first_of(allowedTypes) &&
412 "_" != scriptBaseName.substr(0,1) && // default stratum cannot be explicitly named
413 0 == scriptBaseName.substr(0,1).find_first_of(allowedStrata))
415 // stratified script
416 scriptStratum = scriptBaseName.substr(0,1);
417 scriptType = scriptBaseName.substr(1,1);
419 // Let's hope people won't uninstall packages before installing bash
420 init_run_script ();
423 std::string
424 Script::baseName() const
426 return scriptBaseName;
429 std::string
430 Script::fullName() const
432 return scriptName;
435 char const Script::ETCPostinstall[] = "/etc/postinstall/";
436 char const Script::allowedStrata[] = "0_z";
437 char const Script::allowedTypes[] = "pr";