Translated using Weblate (Chinese (Simplified))
[cygwin-setup.git] / script.cc
blobf578ea6046b777f494edba5039d31af77aca8160
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"
40 #include "desktop.h"
42 static std::string sh, dash;
43 static const char *cmd;
45 static void
46 sanitize_PATH ()
48 char dummy;
49 DWORD len = GetEnvironmentVariable ("PATH", &dummy, 0);
50 char *path = (char *) alloca (len + 1);
51 GetEnvironmentVariable ("PATH", path, len);
52 std::string newpath = backslash (cygpath ("/bin") + ";"
53 + cygpath ("/usr/sbin") + ";"
54 + cygpath ("/sbin"));
55 len = (UINT) GetSystemWindowsDirectory (&dummy, 0);
56 char *system_root = (char *) alloca (len + 2);
57 GetSystemWindowsDirectory (system_root, len--);
58 if (system_root[len - 1] != '\\')
60 system_root[len] = '\\';
61 system_root[++len] = '\0';
63 for (char *p = strtok (path, ";"); p; p = strtok (NULL, ";"))
65 size_t plen = strlen (p);
66 size_t cmplen = plen == (len - 1) ? plen : len;
67 if (strncasecmp (system_root, p, cmplen) == 0)
69 newpath += ";";
70 newpath += p;
73 SetEnvironmentVariable ("PATH", newpath.c_str());
76 static void
77 modify_CYGWIN ()
79 std::string cygwin;
80 DWORD len = GetEnvironmentVariable ("CYGWIN", &cygwin[0], 0);
81 if (len > 0)
83 cygwin.resize(len);
84 GetEnvironmentVariable ("CYGWIN", &cygwin[0], len);
85 cygwin.resize(len-1); // trim terminating null
86 cygwin.append(" ");
89 switch (symlinkType)
91 case SymlinkTypeNative:
92 cygwin.append("winsymlinks:native");
93 break;
95 case SymlinkTypeWsl:
96 cygwin.append("winsymlinks:wsl");
97 break;
99 case SymlinkTypeMagic:
100 cygwin.append("winsymlinks:sys");
101 break;
103 case SymlinkTypeShortcut: /* not yet implemented */
104 default:
105 break;
108 SetEnvironmentVariable ("CYGWIN", cygwin.c_str());
111 void
112 init_run_script ()
114 static bool initialized;
115 if (initialized)
116 return;
118 initialized = true;
120 char *env = GetEnvironmentStrings ();
121 if (env)
123 for (char *p = env; *p; p = strchr (p, '\0') + 1)
125 char *eq = strchr (p, '=');
126 *eq = '\0';
127 if (strcasecmp (p, "comspec") != 0
128 && strcasecmp (p, "cygwin") != 0
129 && strcasecmp (p, "path") != 0
130 && strcasecmp (p, "programdata") != 0
131 && strncasecmp (p, "system", 7) != 0
132 && strncasecmp (p, "user", 4) != 0
133 && strcasecmp (p, "windir") != 0)
134 SetEnvironmentVariable (p, NULL);
135 p = eq + 1;
137 FreeEnvironmentStrings (env);
140 modify_CYGWIN();
141 SetEnvironmentVariable ("CYGWINROOT", get_root_dir ().c_str());
142 SetEnvironmentVariable ("CYGWINFORALL",
143 (root_scope == IDC_ROOT_SYSTEM) ? "-A" : NULL);
145 const char *sms = startmenusuffix();
146 if (strlen(sms) > 0)
147 SetEnvironmentVariable ("CYGWIN_START_MENU_SUFFIX", sms);
149 if (NoStartMenuOption || NoShortcutsOption)
150 SetEnvironmentVariable ("CYGWIN_SETUP_OPTIONS", "no-startmenu");
152 sanitize_PATH ();
153 SetEnvironmentVariable ("SHELL", "/bin/bash");
154 SetEnvironmentVariable ("TEMP", backslash (cygpath ("/tmp")).c_str ());
155 SetEnvironmentVariable ("TERM", "dumb");
156 SetEnvironmentVariable ("TMP", "/tmp");
158 sh = backslash (cygpath ("/bin/bash.exe"));
159 dash = backslash (cygpath ("/bin/dash.exe"));
160 cmd = "cmd.exe";
163 class OutputLog
165 public:
166 OutputLog (const std::string& filename);
167 ~OutputLog ();
168 HANDLE handle () { return _handle; }
169 BOOL isValid () { return _handle != INVALID_HANDLE_VALUE; }
170 BOOL isEmpty () { return GetFileSize (_handle, NULL) == 0; }
171 friend std::ostream &operator<< (std::ostream &, OutputLog &);
172 private:
173 enum { BUFLEN = 1000 };
174 HANDLE _handle;
175 std::string _filename;
176 void out_to(std::ostream &);
179 OutputLog::OutputLog (const std::string& filename)
180 : _handle(INVALID_HANDLE_VALUE), _filename(filename)
182 if (!_filename.size())
183 return;
185 SECURITY_ATTRIBUTES sa;
186 memset (&sa, 0, sizeof (sa));
187 sa.nLength = sizeof (sa);
188 sa.bInheritHandle = TRUE;
189 sa.lpSecurityDescriptor = NULL;
191 if (mkdir_p (0, backslash (cygpath (_filename)).c_str(), 0755))
192 return;
194 _handle = CreateFile (backslash (cygpath (_filename)).c_str(),
195 GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
196 &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
197 NULL);
199 if (_handle == INVALID_HANDLE_VALUE)
201 Log (LOG_PLAIN) << "error: Unable to redirect output to '" << _filename
202 << "'; using console" << endLog;
206 OutputLog::~OutputLog ()
208 if (_handle != INVALID_HANDLE_VALUE)
209 CloseHandle (_handle);
210 if (_filename.size() &&
211 !DeleteFile(backslash (cygpath (_filename)).c_str()))
213 Log (LOG_PLAIN) << "error: Unable to remove temporary file '" << _filename
214 << "'" << endLog;
218 std::ostream &
219 operator<< (std::ostream &out, OutputLog &log)
221 log.out_to(out);
222 return out;
225 void
226 OutputLog::out_to(std::ostream &out)
228 char buf[BUFLEN];
229 DWORD num;
230 FlushFileBuffers (_handle);
231 SetFilePointer(_handle, 0, NULL, FILE_BEGIN);
233 while (ReadFile(_handle, buf, BUFLEN-1, &num, NULL) && num != 0)
235 buf[num] = '\0';
236 out << buf;
239 SetFilePointer(_handle, 0, NULL, FILE_END);
243 run (const char *cmdline)
246 STARTUPINFO si;
247 PROCESS_INFORMATION pi;
248 DWORD flags = CREATE_NEW_CONSOLE;
249 DWORD exitCode = 0;
250 BOOL inheritHandles = FALSE;
251 BOOL exitCodeValid = FALSE;
253 Log (LOG_PLAIN) << "running: " << cmdline << endLog;
255 char tmp_pat[] = "/var/log/setup.log.runXXXXXXX";
256 OutputLog file_out = std::string (mktemp (tmp_pat));
258 memset (&pi, 0, sizeof (pi));
259 memset (&si, 0, sizeof (si));
260 si.cb = sizeof (si);
261 si.lpTitle = (char *) "Cygwin Setup Post-Install Script";
262 si.dwFlags = STARTF_USEPOSITION;
264 if (file_out.isValid ())
266 inheritHandles = TRUE;
267 si.dwFlags |= STARTF_USESTDHANDLES;
268 si.hStdInput = INVALID_HANDLE_VALUE;
269 si.hStdOutput = file_out.handle ();
270 si.hStdError = file_out.handle ();
271 si.dwFlags |= STARTF_USESHOWWINDOW;
272 si.wShowWindow = SW_HIDE;
273 flags = CREATE_NO_WINDOW;
276 BOOL createSucceeded = CreateProcess (0, (char *)cmdline, 0, 0, inheritHandles,
277 flags, 0, get_root_dir ().c_str(),
278 &si, &pi);
280 if (createSucceeded)
282 WaitForSingleObject (pi.hProcess, INFINITE);
283 exitCodeValid = GetExitCodeProcess(pi.hProcess, &exitCode);
285 CloseHandle(pi.hProcess);
286 CloseHandle(pi.hThread);
288 if (!file_out.isEmpty ())
289 Log (LOG_BABBLE) << file_out << endLog;
291 if (exitCodeValid)
292 return exitCode;
293 return -GetLastError();
297 Script::run() const
299 if ("done" == scriptExtension)
300 return NO_ERROR;
301 if (0 == scriptExtension.size())
302 return -ERROR_INVALID_DATA;
304 /* Bail here if the script file does not exist. This can happen for
305 example in the case of tetex-* where two or more packages contain a
306 postinstall script by the same name. When we are called the second
307 time the file has already been renamed to .done, and if we don't
308 return here we end up erroneously deleting this .done file. */
309 std::string windowsName = backslash (cygpath (scriptName));
310 if (_access (windowsName.c_str(), 0) == -1)
312 Log (LOG_PLAIN) << "can't run " << scriptName << ": No such file"
313 << endLog;
314 return -ERROR_INVALID_DATA;
317 int retval;
318 char cmdline[CYG_PATH_MAX];
320 if (sh.size() && ("sh" == scriptExtension))
322 sprintf (cmdline, "%s %s \"%s\"", sh.c_str(), "--norc --noprofile", scriptName.c_str());
323 retval = ::run (cmdline);
325 else if (dash.size() && ("dash" == scriptExtension))
327 sprintf (cmdline, "%s \"%s\"", dash.c_str(), scriptName.c_str());
328 retval = ::run (cmdline);
330 else if (cmd && (("bat" == scriptExtension) ||
331 ("cmd" == scriptExtension)))
333 sprintf (cmdline, "%s %s \"%s\"", cmd, "/c", windowsName.c_str());
334 retval = ::run (cmdline);
336 else
337 return -ERROR_INVALID_DATA;
339 if (retval)
340 Log (LOG_PLAIN) << "abnormal exit: exit code=" << retval << endLog;
342 /* if .done file exists then delete it otherwise just ignore no file error */
343 io_stream::remove ("cygfile://" + scriptName + ".done");
345 /* don't rename the script as .done if it didn't run successfully or
346 if this script is marked to be always run */
347 if (!retval && ("p" != scriptType))
348 io_stream::move ("cygfile://" + scriptName,
349 "cygfile://" + scriptName + ".done");
351 return retval;
355 try_run_script (const std::string& dir,
356 const std::string& fname,
357 const std::string& ext)
359 if (io_stream::exists ("cygfile://" + dir + fname + ext))
360 return Script (dir + fname + ext).run ();
361 return NO_ERROR;
364 bool
365 Script::isAScript (const std::string& file)
367 // is a directory path
368 if ('/' == file[file.size()-1])
369 return false;
370 // file may start with /etc/postinstall/ or etc/postinstall/
371 std::size_t found = file.find(ETCPostinstall+1);
372 if (( found == 0) ||
373 ((found == 1) && (0 == file.find('/'))))
374 return true;
375 return false;
378 bool
379 Script::match (const std::string& stratum, const std::string& type)
381 // empty string for each parameter always matches
382 bool matchedStratum, matchedType;
383 if ("done" == scriptExtension)
384 return false;
385 if (stratum.size())
386 matchedStratum = (std::string::npos != stratum.find(scriptStratum));
387 else
388 matchedStratum = true;
389 if (type.size())
390 matchedType = (std::string::npos != type.find(scriptType));
391 else
392 matchedType = true;
393 return matchedStratum && matchedType;
395 bool
396 Script::is_p (const std::string& stratum)
398 return match( stratum, "p");
400 bool
401 Script::not_p (const std::string& stratum)
403 return match( stratum, allowedTypes+1);
406 Script::Script (const std::string& fileName)
407 : scriptName (fileName),
408 scriptBaseName (""),
409 scriptExtension (""),
410 scriptStratum ("_"),
411 scriptType ("r")
413 std::size_t found;
414 found = fileName.rfind('/');
415 if (found != std::string::npos)
416 scriptBaseName = fileName.substr(found + 1);
417 found = fileName.rfind('.');
418 if (found != std::string::npos)
419 scriptExtension = fileName.substr(found + 1);
420 if ( "_" == scriptBaseName.substr(2,1) &&
421 0 == scriptBaseName.substr(1,1).find_first_of(allowedTypes) &&
422 "_" != scriptBaseName.substr(0,1) && // default stratum cannot be explicitly named
423 0 == scriptBaseName.substr(0,1).find_first_of(allowedStrata))
425 // stratified script
426 scriptStratum = scriptBaseName.substr(0,1);
427 scriptType = scriptBaseName.substr(1,1);
429 // Let's hope people won't uninstall packages before installing bash
430 init_run_script ();
433 std::string
434 Script::baseName() const
436 return scriptBaseName;
439 std::string
440 Script::fullName() const
442 return scriptName;
445 char const Script::ETCPostinstall[] = "/etc/postinstall/";
446 char const Script::allowedStrata[] = "0_z";
447 char const Script::allowedTypes[] = "pr";