]> cygwin.com Git - cygwin-apps/setup.git/blame - script.cc
Use solver to check for problems and produce a list of package transactions
[cygwin-apps/setup.git] / script.cc
CommitLineData
bc78a6d5
RC
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
bc78a6d5
RC
20#include "win32.h"
21#include <stdlib.h>
22#include <unistd.h>
23#include <stdio.h>
1069407c 24#include "LogSingleton.h"
3c054baf 25#include "filemanip.h"
bc78a6d5
RC
26#include "mount.h"
27#include "io_stream.h"
ad646f43 28#include "script.h"
1069407c 29#include "mkdir.h"
c3d07c13
AK
30#include "state.h"
31#include "resource.h"
5a85457b
CF
32#if HAVE_ALLOCA_H
33#include <alloca.h>
34#else
35#ifndef alloca
36#define alloca __builtin_alloca
37#endif
38#endif
bc78a6d5 39
390a9146 40static std::string sh, dash;
b56d411d 41static const char *cmd;
5a85457b
CF
42
43static void
44sanitize_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"));
7cc4d95e 53 len = (UINT) GetSystemWindowsDirectory (&dummy, 0);
5a85457b 54 char *system_root = (char *) alloca (len + 2);
7cc4d95e 55 GetSystemWindowsDirectory (system_root, len--);
5a85457b
CF
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
bc78a6d5
RC
74
75void
76init_run_script ()
77{
5a85457b
CF
78 static bool initialized;
79 if (initialized)
80 return;
81
82 initialized = true;
83
84 char *env = GetEnvironmentStrings ();
85 if (env)
bc78a6d5 86 {
5a85457b
CF
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);
bc78a6d5 100 }
5a85457b 101
d2a3615c 102 SetEnvironmentVariable ("CYGWINROOT", get_root_dir ().c_str());
c3d07c13
AK
103 SetEnvironmentVariable ("CYGWINFORALL",
104 (root_scope == IDC_ROOT_SYSTEM) ? "-A" : NULL);
5a85457b
CF
105 sanitize_PATH ();
106 SetEnvironmentVariable ("SHELL", "/bin/bash");
107 SetEnvironmentVariable ("TEMP", backslash (cygpath ("/tmp")).c_str ());
108 SetEnvironmentVariable ("TERM", "dumb");
109 SetEnvironmentVariable ("TMP", "/tmp");
bc78a6d5 110
390a9146
AG
111 sh = backslash (cygpath ("/bin/bash.exe"));
112 dash = backslash (cygpath ("/bin/dash.exe"));
113 cmd = "cmd.exe";
bc78a6d5
RC
114}
115
1069407c
MB
116class OutputLog
117{
118public:
2bba98e8 119 OutputLog (const std::string& filename);
1069407c
MB
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 &);
125private:
126 enum { BUFLEN = 1000 };
127 HANDLE _handle;
2bba98e8 128 std::string _filename;
1069407c
MB
129 void out_to(std::ostream &);
130};
131
2bba98e8 132OutputLog::OutputLog (const std::string& filename)
1069407c
MB
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
b41c2908 144 if (mkdir_p (0, backslash (cygpath (_filename)).c_str(), 0755))
1069407c
MB
145 return;
146
d2a3615c 147 _handle = CreateFile (backslash (cygpath (_filename)).c_str(),
1069407c 148 GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
b41c2908
CV
149 &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
150 NULL);
1069407c
MB
151
152 if (_handle == INVALID_HANDLE_VALUE)
153 {
157dc2b8 154 Log (LOG_PLAIN) << "error: Unable to redirect output to '" << _filename
1069407c
MB
155 << "'; using console" << endLog;
156 }
157}
158
159OutputLog::~OutputLog ()
160{
161 if (_handle != INVALID_HANDLE_VALUE)
162 CloseHandle (_handle);
163 if (_filename.size() &&
d2a3615c 164 !DeleteFile(backslash (cygpath (_filename)).c_str()))
1069407c 165 {
157dc2b8 166 Log (LOG_PLAIN) << "error: Unable to remove temporary file '" << _filename
1069407c
MB
167 << "'" << endLog;
168 }
169}
170
171std::ostream &
172operator<< (std::ostream &out, OutputLog &log)
173{
174 log.out_to(out);
175 return out;
176}
177
178void
179OutputLog::out_to(std::ostream &out)
180{
181 char buf[BUFLEN];
182 DWORD num;
183 FlushFileBuffers (_handle);
184 SetFilePointer(_handle, 0, NULL, FILE_BEGIN);
54978a88 185
1069407c
MB
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
f2952a6c
JT
195int
196run (const char *cmdline)
bc78a6d5 197{
f2952a6c 198
bc78a6d5
RC
199 STARTUPINFO si;
200 PROCESS_INFORMATION pi;
1069407c 201 DWORD flags = CREATE_NEW_CONSOLE;
534c25cc 202 DWORD exitCode = 0;
1069407c 203 BOOL inheritHandles = FALSE;
534c25cc 204 BOOL exitCodeValid = FALSE;
bc78a6d5 205
157dc2b8 206 Log (LOG_PLAIN) << "running: " << cmdline << endLog;
f2952a6c
JT
207
208 char tmp_pat[] = "/var/log/setup.log.runXXXXXXX";
209 OutputLog file_out = std::string (mktemp (tmp_pat));
210
bc78a6d5
RC
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
1069407c
MB
217 if (file_out.isValid ())
218 {
219 inheritHandles = TRUE;
220 si.dwFlags |= STARTF_USESTDHANDLES;
5524909e 221 si.hStdInput = INVALID_HANDLE_VALUE;
1069407c
MB
222 si.hStdOutput = file_out.handle ();
223 si.hStdError = file_out.handle ();
224 si.dwFlags |= STARTF_USESHOWWINDOW;
1069407c 225 si.wShowWindow = SW_HIDE;
c6263b96 226 flags = CREATE_NO_WINDOW;
1069407c
MB
227 }
228
f2952a6c 229 BOOL createSucceeded = CreateProcess (0, (char *)cmdline, 0, 0, inheritHandles,
d2a3615c 230 flags, 0, get_root_dir ().c_str(),
1069407c 231 &si, &pi);
bc78a6d5 232
92f9402a 233 if (createSucceeded)
534c25cc
BD
234 {
235 WaitForSingleObject (pi.hProcess, INFINITE);
236 exitCodeValid = GetExitCodeProcess(pi.hProcess, &exitCode);
237 }
92f9402a
RC
238 CloseHandle(pi.hProcess);
239 CloseHandle(pi.hThread);
f2952a6c
JT
240
241 if (!file_out.isEmpty ())
157dc2b8 242 Log (LOG_BABBLE) << file_out << endLog;
f2952a6c 243
534c25cc
BD
244 if (exitCodeValid)
245 return exitCode;
246 return -GetLastError();
bc78a6d5
RC
247}
248
534c25cc 249int
34ea5b6d 250Script::run() const
a0e56f67 251{
54978a88
AG
252 if ("done" == scriptExtension)
253 return NO_ERROR;
254 if (0 == scriptExtension.size())
534c25cc 255 return -ERROR_INVALID_DATA;
bc78a6d5 256
a875b471
BD
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
54978a88 261 return here we end up erroneously deleting this .done file. */
a875b471
BD
262 std::string windowsName = backslash (cygpath (scriptName));
263 if (_access (windowsName.c_str(), 0) == -1)
1069407c 264 {
157dc2b8 265 Log (LOG_PLAIN) << "can't run " << scriptName << ": No such file"
a875b471
BD
266 << endLog;
267 return -ERROR_INVALID_DATA;
1069407c 268 }
1069407c 269
a875b471 270 int retval;
7cc4d95e 271 char cmdline[CYG_PATH_MAX];
f2952a6c 272
54978a88 273 if (sh.size() && ("sh" == scriptExtension))
bc78a6d5 274 {
f2952a6c
JT
275 sprintf (cmdline, "%s %s \"%s\"", sh.c_str(), "--norc --noprofile", scriptName.c_str());
276 retval = ::run (cmdline);
bc78a6d5 277 }
390a9146
AG
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)))
bc78a6d5 285 {
f2952a6c
JT
286 sprintf (cmdline, "%s %s \"%s\"", cmd, "/c", windowsName.c_str());
287 retval = ::run (cmdline);
bc78a6d5
RC
288 }
289 else
534c25cc 290 return -ERROR_INVALID_DATA;
bc78a6d5 291
b2b35a6e 292 if (retval)
157dc2b8 293 Log (LOG_PLAIN) << "abnormal exit: exit code=" << retval << endLog;
534c25cc 294
0707786e 295 /* if .done file exists then delete it otherwise just ignore no file error */
2bba98e8 296 io_stream::remove ("cygfile://" + scriptName + ".done");
bc78a6d5 297
54978a88
AG
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))
0707786e
JT
301 io_stream::move ("cygfile://" + scriptName,
302 "cygfile://" + scriptName + ".done");
534c25cc
BD
303
304 return retval;
bc78a6d5
RC
305}
306
534c25cc 307int
2bba98e8
MB
308try_run_script (const std::string& dir,
309 const std::string& fname,
310 const std::string& ext)
bc78a6d5 311{
2bba98e8 312 if (io_stream::exists ("cygfile://" + dir + fname + ext))
534c25cc
BD
313 return Script (dir + fname + ext).run ();
314 return NO_ERROR;
bc78a6d5
RC
315}
316
ad646f43 317bool
2bba98e8 318Script::isAScript (const std::string& file)
ad646f43 319{
54978a88
AG
320 // is a directory path
321 if ('/' == file[file.size()-1])
ad646f43 322 return false;
54978a88
AG
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;
ad646f43
RC
329}
330
54978a88
AG
331bool
332Script::match (const std::string& stratum, const std::string& type)
ad646f43 333{
54978a88
AG
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}
348bool
349Script::is_p (const std::string& stratum)
350{
351 return match( stratum, "p");
352}
353bool
354Script::not_p (const std::string& stratum)
355{
356 return match( stratum, allowedTypes+1);
357}
358
359Script::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 ();
ad646f43
RC
384}
385
470f4928 386std::string
39ba3555 387Script::baseName() const
ad646f43 388{
54978a88 389 return scriptBaseName;
ad646f43 390}
39ba3555 391
2bba98e8 392std::string
39ba3555
MB
393Script::fullName() const
394{
395 return scriptName;
396}
54978a88
AG
397
398char const Script::ETCPostinstall[] = "/etc/postinstall/";
399char const Script::allowedStrata[] = "0_z";
400char const Script::allowedTypes[] = "pr";
This page took 0.157367 seconds and 5 git commands to generate.