]> cygwin.com Git - cygwin-apps/setup.git/blame - script.cc
Added dpiAwareness element to manifest
[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
34b58a2e 39#include "mklink2.h"
c8ea8d6c 40#include "desktop.h"
bc78a6d5 41
390a9146 42static std::string sh, dash;
b56d411d 43static const char *cmd;
5a85457b
CF
44
45static void
46sanitize_PATH ()
47{
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"));
7cc4d95e 55 len = (UINT) GetSystemWindowsDirectory (&dummy, 0);
5a85457b 56 char *system_root = (char *) alloca (len + 2);
7cc4d95e 57 GetSystemWindowsDirectory (system_root, len--);
5a85457b
CF
58 if (system_root[len - 1] != '\\')
59 {
60 system_root[len] = '\\';
61 system_root[++len] = '\0';
62 }
63 for (char *p = strtok (path, ";"); p; p = strtok (NULL, ";"))
64 {
65 size_t plen = strlen (p);
66 size_t cmplen = plen == (len - 1) ? plen : len;
67 if (strncasecmp (system_root, p, cmplen) == 0)
68 {
69 newpath += ";";
70 newpath += p;
71 }
72 }
73 SetEnvironmentVariable ("PATH", newpath.c_str());
74}
75
34b58a2e
JT
76static void
77modify_CYGWIN ()
78{
79 std::string cygwin;
80 DWORD len = GetEnvironmentVariable ("CYGWIN", &cygwin[0], 0);
81 if (len > 0)
82 {
83 cygwin.resize(len);
84 GetEnvironmentVariable ("CYGWIN", &cygwin[0], len);
85 cygwin.resize(len-1); // trim terminating null
86 cygwin.append(" ");
87 }
88
89 switch (symlinkType)
90 {
91 case SymlinkTypeNative:
92 cygwin.append("winsymlinks:native");
93 break;
94
95 case SymlinkTypeWsl:
96 cygwin.append("winsymlinks:wsl");
97 break;
98
99 case SymlinkTypeMagic:
100 cygwin.append("winsymlinks:sys");
101 break;
102
103 case SymlinkTypeShortcut: /* not yet implemented */
104 default:
105 break;
106 }
107
108 SetEnvironmentVariable ("CYGWIN", cygwin.c_str());
109}
bc78a6d5
RC
110
111void
112init_run_script ()
113{
5a85457b
CF
114 static bool initialized;
115 if (initialized)
116 return;
117
118 initialized = true;
119
120 char *env = GetEnvironmentStrings ();
121 if (env)
bc78a6d5 122 {
5a85457b
CF
123 for (char *p = env; *p; p = strchr (p, '\0') + 1)
124 {
125 char *eq = strchr (p, '=');
126 *eq = '\0';
127 if (strcasecmp (p, "comspec") != 0
1473bfd3 128 && strcasecmp (p, "cygwin") != 0
5a85457b 129 && strcasecmp (p, "path") != 0
4d1bc244 130 && strcasecmp (p, "programdata") != 0
5a85457b
CF
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;
136 }
137 FreeEnvironmentStrings (env);
bc78a6d5 138 }
5a85457b 139
34b58a2e 140 modify_CYGWIN();
d2a3615c 141 SetEnvironmentVariable ("CYGWINROOT", get_root_dir ().c_str());
c3d07c13
AK
142 SetEnvironmentVariable ("CYGWINFORALL",
143 (root_scope == IDC_ROOT_SYSTEM) ? "-A" : NULL);
c8ea8d6c
JT
144
145 const char *sms = startmenusuffix();
146 if (strlen(sms) > 0)
147 SetEnvironmentVariable ("CYGWIN_START_MENU_SUFFIX", sms);
148
7b640934
JT
149 if (NoStartMenuOption || NoShortcutsOption)
150 SetEnvironmentVariable ("CYGWIN_SETUP_OPTIONS", "no-startmenu");
151
5a85457b
CF
152 sanitize_PATH ();
153 SetEnvironmentVariable ("SHELL", "/bin/bash");
154 SetEnvironmentVariable ("TEMP", backslash (cygpath ("/tmp")).c_str ());
155 SetEnvironmentVariable ("TERM", "dumb");
156 SetEnvironmentVariable ("TMP", "/tmp");
bc78a6d5 157
390a9146
AG
158 sh = backslash (cygpath ("/bin/bash.exe"));
159 dash = backslash (cygpath ("/bin/dash.exe"));
160 cmd = "cmd.exe";
bc78a6d5
RC
161}
162
1069407c
MB
163class OutputLog
164{
165public:
2bba98e8 166 OutputLog (const std::string& filename);
1069407c
MB
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 &);
172private:
173 enum { BUFLEN = 1000 };
174 HANDLE _handle;
2bba98e8 175 std::string _filename;
1069407c
MB
176 void out_to(std::ostream &);
177};
178
2bba98e8 179OutputLog::OutputLog (const std::string& filename)
1069407c
MB
180 : _handle(INVALID_HANDLE_VALUE), _filename(filename)
181{
182 if (!_filename.size())
183 return;
184
185 SECURITY_ATTRIBUTES sa;
186 memset (&sa, 0, sizeof (sa));
187 sa.nLength = sizeof (sa);
188 sa.bInheritHandle = TRUE;
189 sa.lpSecurityDescriptor = NULL;
190
b41c2908 191 if (mkdir_p (0, backslash (cygpath (_filename)).c_str(), 0755))
1069407c
MB
192 return;
193
d2a3615c 194 _handle = CreateFile (backslash (cygpath (_filename)).c_str(),
1069407c 195 GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
b41c2908
CV
196 &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
197 NULL);
1069407c
MB
198
199 if (_handle == INVALID_HANDLE_VALUE)
200 {
157dc2b8 201 Log (LOG_PLAIN) << "error: Unable to redirect output to '" << _filename
1069407c
MB
202 << "'; using console" << endLog;
203 }
204}
205
206OutputLog::~OutputLog ()
207{
208 if (_handle != INVALID_HANDLE_VALUE)
209 CloseHandle (_handle);
210 if (_filename.size() &&
d2a3615c 211 !DeleteFile(backslash (cygpath (_filename)).c_str()))
1069407c 212 {
157dc2b8 213 Log (LOG_PLAIN) << "error: Unable to remove temporary file '" << _filename
1069407c
MB
214 << "'" << endLog;
215 }
216}
217
218std::ostream &
219operator<< (std::ostream &out, OutputLog &log)
220{
221 log.out_to(out);
222 return out;
223}
224
225void
226OutputLog::out_to(std::ostream &out)
227{
228 char buf[BUFLEN];
229 DWORD num;
230 FlushFileBuffers (_handle);
231 SetFilePointer(_handle, 0, NULL, FILE_BEGIN);
54978a88 232
1069407c
MB
233 while (ReadFile(_handle, buf, BUFLEN-1, &num, NULL) && num != 0)
234 {
235 buf[num] = '\0';
236 out << buf;
237 }
238
239 SetFilePointer(_handle, 0, NULL, FILE_END);
240}
241
f2952a6c
JT
242int
243run (const char *cmdline)
bc78a6d5 244{
f2952a6c 245
bc78a6d5
RC
246 STARTUPINFO si;
247 PROCESS_INFORMATION pi;
1069407c 248 DWORD flags = CREATE_NEW_CONSOLE;
534c25cc 249 DWORD exitCode = 0;
1069407c 250 BOOL inheritHandles = FALSE;
534c25cc 251 BOOL exitCodeValid = FALSE;
bc78a6d5 252
157dc2b8 253 Log (LOG_PLAIN) << "running: " << cmdline << endLog;
f2952a6c
JT
254
255 char tmp_pat[] = "/var/log/setup.log.runXXXXXXX";
256 OutputLog file_out = std::string (mktemp (tmp_pat));
257
bc78a6d5
RC
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;
263
1069407c
MB
264 if (file_out.isValid ())
265 {
266 inheritHandles = TRUE;
267 si.dwFlags |= STARTF_USESTDHANDLES;
5524909e 268 si.hStdInput = INVALID_HANDLE_VALUE;
1069407c
MB
269 si.hStdOutput = file_out.handle ();
270 si.hStdError = file_out.handle ();
271 si.dwFlags |= STARTF_USESHOWWINDOW;
1069407c 272 si.wShowWindow = SW_HIDE;
c6263b96 273 flags = CREATE_NO_WINDOW;
1069407c
MB
274 }
275
f2952a6c 276 BOOL createSucceeded = CreateProcess (0, (char *)cmdline, 0, 0, inheritHandles,
d2a3615c 277 flags, 0, get_root_dir ().c_str(),
1069407c 278 &si, &pi);
bc78a6d5 279
92f9402a 280 if (createSucceeded)
534c25cc
BD
281 {
282 WaitForSingleObject (pi.hProcess, INFINITE);
283 exitCodeValid = GetExitCodeProcess(pi.hProcess, &exitCode);
284 }
92f9402a
RC
285 CloseHandle(pi.hProcess);
286 CloseHandle(pi.hThread);
f2952a6c
JT
287
288 if (!file_out.isEmpty ())
157dc2b8 289 Log (LOG_BABBLE) << file_out << endLog;
f2952a6c 290
534c25cc
BD
291 if (exitCodeValid)
292 return exitCode;
293 return -GetLastError();
bc78a6d5
RC
294}
295
534c25cc 296int
34ea5b6d 297Script::run() const
a0e56f67 298{
54978a88
AG
299 if ("done" == scriptExtension)
300 return NO_ERROR;
301 if (0 == scriptExtension.size())
534c25cc 302 return -ERROR_INVALID_DATA;
bc78a6d5 303
a875b471
BD
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
54978a88 308 return here we end up erroneously deleting this .done file. */
a875b471
BD
309 std::string windowsName = backslash (cygpath (scriptName));
310 if (_access (windowsName.c_str(), 0) == -1)
1069407c 311 {
157dc2b8 312 Log (LOG_PLAIN) << "can't run " << scriptName << ": No such file"
a875b471
BD
313 << endLog;
314 return -ERROR_INVALID_DATA;
1069407c 315 }
1069407c 316
a875b471 317 int retval;
7cc4d95e 318 char cmdline[CYG_PATH_MAX];
f2952a6c 319
54978a88 320 if (sh.size() && ("sh" == scriptExtension))
bc78a6d5 321 {
f2952a6c
JT
322 sprintf (cmdline, "%s %s \"%s\"", sh.c_str(), "--norc --noprofile", scriptName.c_str());
323 retval = ::run (cmdline);
bc78a6d5 324 }
390a9146
AG
325 else if (dash.size() && ("dash" == scriptExtension))
326 {
327 sprintf (cmdline, "%s \"%s\"", dash.c_str(), scriptName.c_str());
328 retval = ::run (cmdline);
329 }
330 else if (cmd && (("bat" == scriptExtension) ||
331 ("cmd" == scriptExtension)))
bc78a6d5 332 {
f2952a6c
JT
333 sprintf (cmdline, "%s %s \"%s\"", cmd, "/c", windowsName.c_str());
334 retval = ::run (cmdline);
bc78a6d5
RC
335 }
336 else
534c25cc 337 return -ERROR_INVALID_DATA;
bc78a6d5 338
b2b35a6e 339 if (retval)
157dc2b8 340 Log (LOG_PLAIN) << "abnormal exit: exit code=" << retval << endLog;
534c25cc 341
0707786e 342 /* if .done file exists then delete it otherwise just ignore no file error */
2bba98e8 343 io_stream::remove ("cygfile://" + scriptName + ".done");
bc78a6d5 344
54978a88
AG
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))
0707786e
JT
348 io_stream::move ("cygfile://" + scriptName,
349 "cygfile://" + scriptName + ".done");
534c25cc
BD
350
351 return retval;
bc78a6d5
RC
352}
353
534c25cc 354int
2bba98e8
MB
355try_run_script (const std::string& dir,
356 const std::string& fname,
357 const std::string& ext)
bc78a6d5 358{
2bba98e8 359 if (io_stream::exists ("cygfile://" + dir + fname + ext))
534c25cc
BD
360 return Script (dir + fname + ext).run ();
361 return NO_ERROR;
bc78a6d5
RC
362}
363
ad646f43 364bool
2bba98e8 365Script::isAScript (const std::string& file)
ad646f43 366{
54978a88
AG
367 // is a directory path
368 if ('/' == file[file.size()-1])
ad646f43 369 return false;
54978a88
AG
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;
ad646f43
RC
376}
377
54978a88
AG
378bool
379Script::match (const std::string& stratum, const std::string& type)
ad646f43 380{
54978a88
AG
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;
394}
395bool
396Script::is_p (const std::string& stratum)
397{
398 return match( stratum, "p");
399}
400bool
401Script::not_p (const std::string& stratum)
402{
403 return match( stratum, allowedTypes+1);
404}
405
406Script::Script (const std::string& fileName)
407 : scriptName (fileName),
408 scriptBaseName (""),
409 scriptExtension (""),
410 scriptStratum ("_"),
411 scriptType ("r")
412{
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))
424 {
425 // stratified script
426 scriptStratum = scriptBaseName.substr(0,1);
427 scriptType = scriptBaseName.substr(1,1);
428 }
429 // Let's hope people won't uninstall packages before installing bash
430 init_run_script ();
ad646f43
RC
431}
432
470f4928 433std::string
39ba3555 434Script::baseName() const
ad646f43 435{
54978a88 436 return scriptBaseName;
ad646f43 437}
39ba3555 438
2bba98e8 439std::string
39ba3555
MB
440Script::fullName() const
441{
442 return scriptName;
443}
54978a88
AG
444
445char const Script::ETCPostinstall[] = "/etc/postinstall/";
446char const Script::allowedStrata[] = "0_z";
447char const Script::allowedTypes[] = "pr";
This page took 0.193909 seconds and 6 git commands to generate.