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. */
21 static const char *cvsid
=
29 #include "LogSingleton.h"
30 #include "filemanip.h"
32 #include "io_stream.h"
39 #define alloca __builtin_alloca
43 static std::string sh
;
44 static const char *cmd
;
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") + ";"
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] != '\\')
61 system_root
[len
] = '\\';
62 system_root
[++len
] = '\0';
64 for (char *p
= strtok (path
, ";"); p
; p
= strtok (NULL
, ";"))
66 size_t plen
= strlen (p
);
67 size_t cmplen
= plen
== (len
- 1) ? plen
: len
;
68 if (strncasecmp (system_root
, p
, cmplen
) == 0)
74 SetEnvironmentVariable ("PATH", newpath
.c_str());
81 static bool initialized
;
87 char *env
= GetEnvironmentStrings ();
90 for (char *p
= env
; *p
; p
= strchr (p
, '\0') + 1)
92 char *eq
= strchr (p
, '=');
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
);
102 FreeEnvironmentStrings (env
);
105 SetEnvironmentVariable ("CYGWINROOT", get_root_dir ().c_str());
107 SetEnvironmentVariable ("SHELL", "/bin/bash");
108 SetEnvironmentVariable ("TEMP", backslash (cygpath ("/tmp")).c_str ());
109 SetEnvironmentVariable ("TERM", "dumb");
110 SetEnvironmentVariable ("TMP", "/tmp");
112 sh
= backslash (cygpath ("/bin/bash.exe"));
113 cmd
= IsWindowsNT () ? "cmd.exe" : "command.com";
119 OutputLog (const std::string
& filename
);
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
&);
126 enum { BUFLEN
= 1000 };
128 std::string _filename
;
129 void out_to(std::ostream
&);
132 OutputLog::OutputLog (const std::string
& filename
)
133 : _handle(INVALID_HANDLE_VALUE
), _filename(filename
)
135 if (!_filename
.size())
138 SECURITY_ATTRIBUTES sa
;
139 memset (&sa
, 0, sizeof (sa
));
140 sa
.nLength
= sizeof (sa
);
141 sa
.bInheritHandle
= TRUE
;
142 sa
.lpSecurityDescriptor
= NULL
;
144 if (mkdir_p (0, backslash (cygpath (_filename
)).c_str(), 0755))
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
,
152 if (_handle
== INVALID_HANDLE_VALUE
)
154 log(LOG_PLAIN
) << "error: Unable to redirect output to '" << _filename
155 << "'; using console" << endLog
;
159 OutputLog::~OutputLog ()
161 if (_handle
!= INVALID_HANDLE_VALUE
)
162 CloseHandle (_handle
);
163 if (_filename
.size() &&
164 !DeleteFile(backslash (cygpath (_filename
)).c_str()))
166 log(LOG_PLAIN
) << "error: Unable to remove temporary file '" << _filename
172 operator<< (std::ostream
&out
, OutputLog
&log
)
179 OutputLog::out_to(std::ostream
&out
)
183 FlushFileBuffers (_handle
);
184 SetFilePointer(_handle
, 0, NULL
, FILE_BEGIN
);
186 while (ReadFile(_handle
, buf
, BUFLEN
-1, &num
, NULL
) && num
!= 0)
192 SetFilePointer(_handle
, 0, NULL
, FILE_END
);
196 run (const char *sh
, const char *args
, const char *file
, OutputLog
&file_out
)
198 char cmdline
[MAX_PATH
];
200 PROCESS_INFORMATION pi
;
201 DWORD flags
= CREATE_NEW_CONSOLE
;
203 BOOL inheritHandles
= FALSE
;
204 BOOL exitCodeValid
= FALSE
;
206 sprintf (cmdline
, "%s %s %s", sh
, args
, file
);
207 memset (&pi
, 0, sizeof (pi
));
208 memset (&si
, 0, sizeof (si
));
210 si
.lpTitle
= (char *) "Cygwin Setup Post-Install Script";
211 si
.dwFlags
= STARTF_USEPOSITION
;
213 if (file_out
.isValid ())
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
225 BOOL createSucceeded
= CreateProcess (0, cmdline
, 0, 0, inheritHandles
,
226 flags
, 0, get_root_dir ().c_str(),
231 WaitForSingleObject (pi
.hProcess
, INFINITE
);
232 exitCodeValid
= GetExitCodeProcess(pi
.hProcess
, &exitCode
);
234 CloseHandle(pi
.hProcess
);
235 CloseHandle(pi
.hThread
);
238 return -GetLastError();
242 Script::extension() const
244 return strrchr (scriptName
.c_str(), '.');
251 return -ERROR_INVALID_DATA
;
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)
261 log(LOG_PLAIN
) << "can't run " << scriptName
<< ": No such file"
263 return -ERROR_INVALID_DATA
;
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)
271 log(LOG_PLAIN
) << "running: " << sh
<< " --norc --noprofile " << scriptName
<< endLog
;
272 retval
= ::run (sh
.c_str(), "--norc --noprofile", scriptName
.c_str(), file_out
);
274 else if (cmd
&& stricmp (extension(), ".bat") == 0)
276 log(LOG_PLAIN
) << "running: " << cmd
<< " /c " << windowsName
<< endLog
;
277 retval
= ::run (cmd
, "/c", windowsName
.c_str(), file_out
);
280 return -ERROR_INVALID_DATA
;
282 if (!file_out
.isEmpty ())
283 log(LOG_BABBLE
) << file_out
<< endLog
;
286 log(LOG_PLAIN
) << "abnormal exit: exit code=" << retval
<< endLog
;
288 /* if .done file exists then delete it otherwise just ignore no file error */
289 io_stream::remove ("cygfile://" + scriptName
+ ".done");
291 /* don't rename the script as .done if it didn't run successfully */
293 io_stream::move ("cygfile://" + scriptName
,
294 "cygfile://" + scriptName
+ ".done");
300 try_run_script (const std::string
& dir
,
301 const std::string
& fname
,
302 const std::string
& ext
)
304 if (io_stream::exists ("cygfile://" + dir
+ fname
+ ext
))
305 return Script (dir
+ fname
+ ext
).run ();
309 char const Script::ETCPostinstall
[] = "/etc/postinstall/";
312 Script::isAScript (const std::string
& file
)
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))
318 if (file
.c_str()[file
.size() - 1] == '/')
323 Script::Script (const std::string
& fileName
) : scriptName (fileName
)
329 Script::baseName() const
331 std::string result
= scriptName
;
332 result
= result
.substr(result
.rfind('/') + 1);
337 Script::fullName() const