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
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. */
24 #include "LogSingleton.h"
25 #include "filemanip.h"
27 #include "io_stream.h"
36 #define alloca __builtin_alloca
42 static std::string sh
, dash
;
43 static const char *cmd
;
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") + ";"
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)
73 SetEnvironmentVariable ("PATH", newpath
.c_str());
80 DWORD len
= GetEnvironmentVariable ("CYGWIN", &cygwin
[0], 0);
84 GetEnvironmentVariable ("CYGWIN", &cygwin
[0], len
);
85 cygwin
.resize(len
-1); // trim terminating null
91 case SymlinkTypeNative
:
92 cygwin
.append("winsymlinks:native");
96 cygwin
.append("winsymlinks:wsl");
99 case SymlinkTypeMagic
:
100 cygwin
.append("winsymlinks:sys");
103 case SymlinkTypeShortcut
: /* not yet implemented */
108 SetEnvironmentVariable ("CYGWIN", cygwin
.c_str());
114 static bool initialized
;
120 char *env
= GetEnvironmentStrings ();
123 for (char *p
= env
; *p
; p
= strchr (p
, '\0') + 1)
125 char *eq
= strchr (p
, '=');
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
);
137 FreeEnvironmentStrings (env
);
141 SetEnvironmentVariable ("CYGWINROOT", get_root_dir ().c_str());
142 SetEnvironmentVariable ("CYGWINFORALL",
143 (root_scope
== IDC_ROOT_SYSTEM
) ? "-A" : NULL
);
145 const char *sms
= startmenusuffix();
147 SetEnvironmentVariable ("CYGWIN_START_MENU_SUFFIX", sms
);
149 if (NoStartMenuOption
|| NoShortcutsOption
)
150 SetEnvironmentVariable ("CYGWIN_SETUP_OPTIONS", "no-startmenu");
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"));
166 OutputLog (const std::string
& filename
);
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
&);
173 enum { BUFLEN
= 1000 };
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())
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))
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
,
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
219 operator<< (std::ostream
&out
, OutputLog
&log
)
226 OutputLog::out_to(std::ostream
&out
)
230 FlushFileBuffers (_handle
);
231 SetFilePointer(_handle
, 0, NULL
, FILE_BEGIN
);
233 while (ReadFile(_handle
, buf
, BUFLEN
-1, &num
, NULL
) && num
!= 0)
239 SetFilePointer(_handle
, 0, NULL
, FILE_END
);
243 run (const char *cmdline
)
247 PROCESS_INFORMATION pi
;
248 DWORD flags
= CREATE_NEW_CONSOLE
;
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
));
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(),
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
;
293 return -GetLastError();
299 if ("done" == scriptExtension
)
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"
314 return -ERROR_INVALID_DATA
;
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
);
337 return -ERROR_INVALID_DATA
;
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");
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 ();
365 Script::isAScript (const std::string
& file
)
367 // is a directory path
368 if ('/' == file
[file
.size()-1])
370 // file may start with /etc/postinstall/ or etc/postinstall/
371 std::size_t found
= file
.find(ETCPostinstall
+1);
373 ((found
== 1) && (0 == file
.find('/'))))
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
)
386 matchedStratum
= (std::string::npos
!= stratum
.find(scriptStratum
));
388 matchedStratum
= true;
390 matchedType
= (std::string::npos
!= type
.find(scriptType
));
393 return matchedStratum
&& matchedType
;
396 Script::is_p (const std::string
& stratum
)
398 return match( stratum
, "p");
401 Script::not_p (const std::string
& stratum
)
403 return match( stratum
, allowedTypes
+1);
406 Script::Script (const std::string
& fileName
)
407 : scriptName (fileName
),
409 scriptExtension (""),
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
))
426 scriptStratum
= scriptBaseName
.substr(0,1);
427 scriptType
= scriptBaseName
.substr(1,1);
429 // Let's hope people won't uninstall packages before installing bash
434 Script::baseName() const
436 return scriptBaseName
;
440 Script::fullName() const
445 char const Script::ETCPostinstall
[] = "/etc/postinstall/";
446 char const Script::allowedStrata
[] = "0_z";
447 char const Script::allowedTypes
[] = "pr";