]> cygwin.com Git - cygwin-apps/setup.git/blame - script.cc
implement minimal stratified postinstall
[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
20#if 0
21static const char *cvsid =
22 "\n%%% $Id$\n";
23#endif
24
25#include "win32.h"
26#include <stdlib.h>
27#include <unistd.h>
28#include <stdio.h>
1069407c 29#include "LogSingleton.h"
3c054baf 30#include "filemanip.h"
bc78a6d5
RC
31#include "mount.h"
32#include "io_stream.h"
ad646f43 33#include "script.h"
1069407c 34#include "mkdir.h"
c3d07c13
AK
35#include "state.h"
36#include "resource.h"
5a85457b
CF
37#if HAVE_ALLOCA_H
38#include <alloca.h>
39#else
40#ifndef alloca
41#define alloca __builtin_alloca
42#endif
43#endif
bc78a6d5 44
2bba98e8 45static std::string sh;
b56d411d 46static const char *cmd;
5a85457b
CF
47
48static void
49sanitize_PATH ()
50{
51 char dummy;
52 DWORD len = GetEnvironmentVariable ("PATH", &dummy, 0);
53 char *path = (char *) alloca (len + 1);
54 GetEnvironmentVariable ("PATH", path, len);
55 std::string newpath = backslash (cygpath ("/bin") + ";"
56 + cygpath ("/usr/sbin") + ";"
57 + cygpath ("/sbin"));
7cc4d95e 58 len = (UINT) GetSystemWindowsDirectory (&dummy, 0);
5a85457b 59 char *system_root = (char *) alloca (len + 2);
7cc4d95e 60 GetSystemWindowsDirectory (system_root, len--);
5a85457b
CF
61 if (system_root[len - 1] != '\\')
62 {
63 system_root[len] = '\\';
64 system_root[++len] = '\0';
65 }
66 for (char *p = strtok (path, ";"); p; p = strtok (NULL, ";"))
67 {
68 size_t plen = strlen (p);
69 size_t cmplen = plen == (len - 1) ? plen : len;
70 if (strncasecmp (system_root, p, cmplen) == 0)
71 {
72 newpath += ";";
73 newpath += p;
74 }
75 }
76 SetEnvironmentVariable ("PATH", newpath.c_str());
77}
78
bc78a6d5
RC
79
80void
81init_run_script ()
82{
5a85457b
CF
83 static bool initialized;
84 if (initialized)
85 return;
86
87 initialized = true;
88
89 char *env = GetEnvironmentStrings ();
90 if (env)
bc78a6d5 91 {
5a85457b
CF
92 for (char *p = env; *p; p = strchr (p, '\0') + 1)
93 {
94 char *eq = strchr (p, '=');
95 *eq = '\0';
96 if (strcasecmp (p, "comspec") != 0
97 && strcasecmp (p, "path") != 0
98 && strncasecmp (p, "system", 7) != 0
99 && strncasecmp (p, "user", 4) != 0
100 && strcasecmp (p, "windir") != 0)
101 SetEnvironmentVariable (p, NULL);
102 p = eq + 1;
103 }
104 FreeEnvironmentStrings (env);
bc78a6d5 105 }
5a85457b 106
d2a3615c 107 SetEnvironmentVariable ("CYGWINROOT", get_root_dir ().c_str());
c3d07c13
AK
108 SetEnvironmentVariable ("CYGWINFORALL",
109 (root_scope == IDC_ROOT_SYSTEM) ? "-A" : NULL);
5a85457b
CF
110 sanitize_PATH ();
111 SetEnvironmentVariable ("SHELL", "/bin/bash");
112 SetEnvironmentVariable ("TEMP", backslash (cygpath ("/tmp")).c_str ());
113 SetEnvironmentVariable ("TERM", "dumb");
114 SetEnvironmentVariable ("TMP", "/tmp");
bc78a6d5 115
46cf12cd 116 sh = backslash (cygpath ("/bin/bash.exe"));
1e029da2 117 cmd = "cmd.exe";
bc78a6d5
RC
118}
119
1069407c
MB
120class OutputLog
121{
122public:
2bba98e8 123 OutputLog (const std::string& filename);
1069407c
MB
124 ~OutputLog ();
125 HANDLE handle () { return _handle; }
126 BOOL isValid () { return _handle != INVALID_HANDLE_VALUE; }
127 BOOL isEmpty () { return GetFileSize (_handle, NULL) == 0; }
128 friend std::ostream &operator<< (std::ostream &, OutputLog &);
129private:
130 enum { BUFLEN = 1000 };
131 HANDLE _handle;
2bba98e8 132 std::string _filename;
1069407c
MB
133 void out_to(std::ostream &);
134};
135
2bba98e8 136OutputLog::OutputLog (const std::string& filename)
1069407c
MB
137 : _handle(INVALID_HANDLE_VALUE), _filename(filename)
138{
139 if (!_filename.size())
140 return;
141
142 SECURITY_ATTRIBUTES sa;
143 memset (&sa, 0, sizeof (sa));
144 sa.nLength = sizeof (sa);
145 sa.bInheritHandle = TRUE;
146 sa.lpSecurityDescriptor = NULL;
147
b41c2908 148 if (mkdir_p (0, backslash (cygpath (_filename)).c_str(), 0755))
1069407c
MB
149 return;
150
d2a3615c 151 _handle = CreateFile (backslash (cygpath (_filename)).c_str(),
1069407c 152 GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
b41c2908
CV
153 &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
154 NULL);
1069407c
MB
155
156 if (_handle == INVALID_HANDLE_VALUE)
157 {
157dc2b8 158 Log (LOG_PLAIN) << "error: Unable to redirect output to '" << _filename
1069407c
MB
159 << "'; using console" << endLog;
160 }
161}
162
163OutputLog::~OutputLog ()
164{
165 if (_handle != INVALID_HANDLE_VALUE)
166 CloseHandle (_handle);
167 if (_filename.size() &&
d2a3615c 168 !DeleteFile(backslash (cygpath (_filename)).c_str()))
1069407c 169 {
157dc2b8 170 Log (LOG_PLAIN) << "error: Unable to remove temporary file '" << _filename
1069407c
MB
171 << "'" << endLog;
172 }
173}
174
175std::ostream &
176operator<< (std::ostream &out, OutputLog &log)
177{
178 log.out_to(out);
179 return out;
180}
181
182void
183OutputLog::out_to(std::ostream &out)
184{
185 char buf[BUFLEN];
186 DWORD num;
187 FlushFileBuffers (_handle);
188 SetFilePointer(_handle, 0, NULL, FILE_BEGIN);
54978a88 189
1069407c
MB
190 while (ReadFile(_handle, buf, BUFLEN-1, &num, NULL) && num != 0)
191 {
192 buf[num] = '\0';
193 out << buf;
194 }
195
196 SetFilePointer(_handle, 0, NULL, FILE_END);
197}
198
f2952a6c
JT
199int
200run (const char *cmdline)
bc78a6d5 201{
f2952a6c 202
bc78a6d5
RC
203 STARTUPINFO si;
204 PROCESS_INFORMATION pi;
1069407c 205 DWORD flags = CREATE_NEW_CONSOLE;
534c25cc 206 DWORD exitCode = 0;
1069407c 207 BOOL inheritHandles = FALSE;
534c25cc 208 BOOL exitCodeValid = FALSE;
bc78a6d5 209
157dc2b8 210 Log (LOG_PLAIN) << "running: " << cmdline << endLog;
f2952a6c
JT
211
212 char tmp_pat[] = "/var/log/setup.log.runXXXXXXX";
213 OutputLog file_out = std::string (mktemp (tmp_pat));
214
bc78a6d5
RC
215 memset (&pi, 0, sizeof (pi));
216 memset (&si, 0, sizeof (si));
217 si.cb = sizeof (si);
218 si.lpTitle = (char *) "Cygwin Setup Post-Install Script";
219 si.dwFlags = STARTF_USEPOSITION;
220
1069407c
MB
221 if (file_out.isValid ())
222 {
223 inheritHandles = TRUE;
224 si.dwFlags |= STARTF_USESTDHANDLES;
5524909e 225 si.hStdInput = INVALID_HANDLE_VALUE;
1069407c
MB
226 si.hStdOutput = file_out.handle ();
227 si.hStdError = file_out.handle ();
228 si.dwFlags |= STARTF_USESHOWWINDOW;
1069407c 229 si.wShowWindow = SW_HIDE;
c6263b96 230 flags = CREATE_NO_WINDOW;
1069407c
MB
231 }
232
f2952a6c 233 BOOL createSucceeded = CreateProcess (0, (char *)cmdline, 0, 0, inheritHandles,
d2a3615c 234 flags, 0, get_root_dir ().c_str(),
1069407c 235 &si, &pi);
bc78a6d5 236
92f9402a 237 if (createSucceeded)
534c25cc
BD
238 {
239 WaitForSingleObject (pi.hProcess, INFINITE);
240 exitCodeValid = GetExitCodeProcess(pi.hProcess, &exitCode);
241 }
92f9402a
RC
242 CloseHandle(pi.hProcess);
243 CloseHandle(pi.hThread);
f2952a6c
JT
244
245 if (!file_out.isEmpty ())
157dc2b8 246 Log (LOG_BABBLE) << file_out << endLog;
f2952a6c 247
534c25cc
BD
248 if (exitCodeValid)
249 return exitCode;
250 return -GetLastError();
bc78a6d5
RC
251}
252
534c25cc 253int
34ea5b6d 254Script::run() const
a0e56f67 255{
54978a88
AG
256 if ("done" == scriptExtension)
257 return NO_ERROR;
258 if (0 == scriptExtension.size())
534c25cc 259 return -ERROR_INVALID_DATA;
bc78a6d5 260
a875b471
BD
261 /* Bail here if the script file does not exist. This can happen for
262 example in the case of tetex-* where two or more packages contain a
263 postinstall script by the same name. When we are called the second
264 time the file has already been renamed to .done, and if we don't
54978a88 265 return here we end up erroneously deleting this .done file. */
a875b471
BD
266 std::string windowsName = backslash (cygpath (scriptName));
267 if (_access (windowsName.c_str(), 0) == -1)
1069407c 268 {
157dc2b8 269 Log (LOG_PLAIN) << "can't run " << scriptName << ": No such file"
a875b471
BD
270 << endLog;
271 return -ERROR_INVALID_DATA;
1069407c 272 }
1069407c 273
a875b471 274 int retval;
7cc4d95e 275 char cmdline[CYG_PATH_MAX];
f2952a6c 276
54978a88 277 if (sh.size() && ("sh" == scriptExtension))
bc78a6d5 278 {
f2952a6c
JT
279 sprintf (cmdline, "%s %s \"%s\"", sh.c_str(), "--norc --noprofile", scriptName.c_str());
280 retval = ::run (cmdline);
bc78a6d5 281 }
54978a88 282 else if (cmd && ("bat" == scriptExtension))
bc78a6d5 283 {
f2952a6c
JT
284 sprintf (cmdline, "%s %s \"%s\"", cmd, "/c", windowsName.c_str());
285 retval = ::run (cmdline);
bc78a6d5
RC
286 }
287 else
534c25cc 288 return -ERROR_INVALID_DATA;
bc78a6d5 289
b2b35a6e 290 if (retval)
157dc2b8 291 Log (LOG_PLAIN) << "abnormal exit: exit code=" << retval << endLog;
534c25cc 292
0707786e 293 /* if .done file exists then delete it otherwise just ignore no file error */
2bba98e8 294 io_stream::remove ("cygfile://" + scriptName + ".done");
bc78a6d5 295
54978a88
AG
296 /* don't rename the script as .done if it didn't run successfully or
297 if this script is marked to be always run */
298 if (!retval && ("p" != scriptType))
0707786e
JT
299 io_stream::move ("cygfile://" + scriptName,
300 "cygfile://" + scriptName + ".done");
534c25cc
BD
301
302 return retval;
bc78a6d5
RC
303}
304
534c25cc 305int
2bba98e8
MB
306try_run_script (const std::string& dir,
307 const std::string& fname,
308 const std::string& ext)
bc78a6d5 309{
2bba98e8 310 if (io_stream::exists ("cygfile://" + dir + fname + ext))
534c25cc
BD
311 return Script (dir + fname + ext).run ();
312 return NO_ERROR;
bc78a6d5
RC
313}
314
ad646f43 315bool
2bba98e8 316Script::isAScript (const std::string& file)
ad646f43 317{
54978a88
AG
318 // is a directory path
319 if ('/' == file[file.size()-1])
ad646f43 320 return false;
54978a88
AG
321 // file may start with /etc/postinstall/ or etc/postinstall/
322 std::size_t found = file.find(ETCPostinstall+1);
323 if (( found == 0) ||
324 ((found == 1) && (0 == file.find('/'))))
325 return true;
326 return false;
ad646f43
RC
327}
328
54978a88
AG
329bool
330Script::match (const std::string& stratum, const std::string& type)
ad646f43 331{
54978a88
AG
332 // empty string for each parameter always matches
333 bool matchedStratum, matchedType;
334 if ("done" == scriptExtension)
335 return false;
336 if (stratum.size())
337 matchedStratum = (std::string::npos != stratum.find(scriptStratum));
338 else
339 matchedStratum = true;
340 if (type.size())
341 matchedType = (std::string::npos != type.find(scriptType));
342 else
343 matchedType = true;
344 return matchedStratum && matchedType;
345}
346bool
347Script::is_p (const std::string& stratum)
348{
349 return match( stratum, "p");
350}
351bool
352Script::not_p (const std::string& stratum)
353{
354 return match( stratum, allowedTypes+1);
355}
356
357Script::Script (const std::string& fileName)
358 : scriptName (fileName),
359 scriptBaseName (""),
360 scriptExtension (""),
361 scriptStratum ("_"),
362 scriptType ("r")
363{
364 std::size_t found;
365 found = fileName.rfind('/');
366 if (found != std::string::npos)
367 scriptBaseName = fileName.substr(found + 1);
368 found = fileName.rfind('.');
369 if (found != std::string::npos)
370 scriptExtension = fileName.substr(found + 1);
371 if ( "_" == scriptBaseName.substr(2,1) &&
372 0 == scriptBaseName.substr(1,1).find_first_of(allowedTypes) &&
373 "_" != scriptBaseName.substr(0,1) && // default stratum cannot be explicitly named
374 0 == scriptBaseName.substr(0,1).find_first_of(allowedStrata))
375 {
376 // stratified script
377 scriptStratum = scriptBaseName.substr(0,1);
378 scriptType = scriptBaseName.substr(1,1);
379 }
380 // Let's hope people won't uninstall packages before installing bash
381 init_run_script ();
ad646f43
RC
382}
383
470f4928 384std::string
39ba3555 385Script::baseName() const
ad646f43 386{
54978a88 387 return scriptBaseName;
ad646f43 388}
39ba3555 389
2bba98e8 390std::string
39ba3555
MB
391Script::fullName() const
392{
393 return scriptName;
394}
54978a88
AG
395
396char const Script::ETCPostinstall[] = "/etc/postinstall/";
397char const Script::allowedStrata[] = "0_z";
398char const Script::allowedTypes[] = "pr";
This page took 0.093063 seconds and 5 git commands to generate.