]> cygwin.com Git - cygwin-apps/setup.git/blame_incremental - script.cc
Added dpiAwareness element to manifest
[cygwin-apps/setup.git] / script.cc
... / ...
CommitLineData
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#include "win32.h"
21#include <stdlib.h>
22#include <unistd.h>
23#include <stdio.h>
24#include "LogSingleton.h"
25#include "filemanip.h"
26#include "mount.h"
27#include "io_stream.h"
28#include "script.h"
29#include "mkdir.h"
30#include "state.h"
31#include "resource.h"
32#if HAVE_ALLOCA_H
33#include <alloca.h>
34#else
35#ifndef alloca
36#define alloca __builtin_alloca
37#endif
38#endif
39#include "mklink2.h"
40#include "desktop.h"
41
42static std::string sh, dash;
43static const char *cmd;
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"));
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] != '\\')
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
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}
110
111void
112init_run_script ()
113{
114 static bool initialized;
115 if (initialized)
116 return;
117
118 initialized = true;
119
120 char *env = GetEnvironmentStrings ();
121 if (env)
122 {
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
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);
135 p = eq + 1;
136 }
137 FreeEnvironmentStrings (env);
138 }
139
140 modify_CYGWIN();
141 SetEnvironmentVariable ("CYGWINROOT", get_root_dir ().c_str());
142 SetEnvironmentVariable ("CYGWINFORALL",
143 (root_scope == IDC_ROOT_SYSTEM) ? "-A" : NULL);
144
145 const char *sms = startmenusuffix();
146 if (strlen(sms) > 0)
147 SetEnvironmentVariable ("CYGWIN_START_MENU_SUFFIX", sms);
148
149 if (NoStartMenuOption || NoShortcutsOption)
150 SetEnvironmentVariable ("CYGWIN_SETUP_OPTIONS", "no-startmenu");
151
152 sanitize_PATH ();
153 SetEnvironmentVariable ("SHELL", "/bin/bash");
154 SetEnvironmentVariable ("TEMP", backslash (cygpath ("/tmp")).c_str ());
155 SetEnvironmentVariable ("TERM", "dumb");
156 SetEnvironmentVariable ("TMP", "/tmp");
157
158 sh = backslash (cygpath ("/bin/bash.exe"));
159 dash = backslash (cygpath ("/bin/dash.exe"));
160 cmd = "cmd.exe";
161}
162
163class OutputLog
164{
165public:
166 OutputLog (const std::string& filename);
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;
175 std::string _filename;
176 void out_to(std::ostream &);
177};
178
179OutputLog::OutputLog (const std::string& filename)
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
191 if (mkdir_p (0, backslash (cygpath (_filename)).c_str(), 0755))
192 return;
193
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,
197 NULL);
198
199 if (_handle == INVALID_HANDLE_VALUE)
200 {
201 Log (LOG_PLAIN) << "error: Unable to redirect output to '" << _filename
202 << "'; using console" << endLog;
203 }
204}
205
206OutputLog::~OutputLog ()
207{
208 if (_handle != INVALID_HANDLE_VALUE)
209 CloseHandle (_handle);
210 if (_filename.size() &&
211 !DeleteFile(backslash (cygpath (_filename)).c_str()))
212 {
213 Log (LOG_PLAIN) << "error: Unable to remove temporary file '" << _filename
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);
232
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
242int
243run (const char *cmdline)
244{
245
246 STARTUPINFO si;
247 PROCESS_INFORMATION pi;
248 DWORD flags = CREATE_NEW_CONSOLE;
249 DWORD exitCode = 0;
250 BOOL inheritHandles = FALSE;
251 BOOL exitCodeValid = FALSE;
252
253 Log (LOG_PLAIN) << "running: " << cmdline << endLog;
254
255 char tmp_pat[] = "/var/log/setup.log.runXXXXXXX";
256 OutputLog file_out = std::string (mktemp (tmp_pat));
257
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
264 if (file_out.isValid ())
265 {
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;
274 }
275
276 BOOL createSucceeded = CreateProcess (0, (char *)cmdline, 0, 0, inheritHandles,
277 flags, 0, get_root_dir ().c_str(),
278 &si, &pi);
279
280 if (createSucceeded)
281 {
282 WaitForSingleObject (pi.hProcess, INFINITE);
283 exitCodeValid = GetExitCodeProcess(pi.hProcess, &exitCode);
284 }
285 CloseHandle(pi.hProcess);
286 CloseHandle(pi.hThread);
287
288 if (!file_out.isEmpty ())
289 Log (LOG_BABBLE) << file_out << endLog;
290
291 if (exitCodeValid)
292 return exitCode;
293 return -GetLastError();
294}
295
296int
297Script::run() const
298{
299 if ("done" == scriptExtension)
300 return NO_ERROR;
301 if (0 == scriptExtension.size())
302 return -ERROR_INVALID_DATA;
303
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)
311 {
312 Log (LOG_PLAIN) << "can't run " << scriptName << ": No such file"
313 << endLog;
314 return -ERROR_INVALID_DATA;
315 }
316
317 int retval;
318 char cmdline[CYG_PATH_MAX];
319
320 if (sh.size() && ("sh" == scriptExtension))
321 {
322 sprintf (cmdline, "%s %s \"%s\"", sh.c_str(), "--norc --noprofile", scriptName.c_str());
323 retval = ::run (cmdline);
324 }
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)))
332 {
333 sprintf (cmdline, "%s %s \"%s\"", cmd, "/c", windowsName.c_str());
334 retval = ::run (cmdline);
335 }
336 else
337 return -ERROR_INVALID_DATA;
338
339 if (retval)
340 Log (LOG_PLAIN) << "abnormal exit: exit code=" << retval << endLog;
341
342 /* if .done file exists then delete it otherwise just ignore no file error */
343 io_stream::remove ("cygfile://" + scriptName + ".done");
344
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");
350
351 return retval;
352}
353
354int
355try_run_script (const std::string& dir,
356 const std::string& fname,
357 const std::string& ext)
358{
359 if (io_stream::exists ("cygfile://" + dir + fname + ext))
360 return Script (dir + fname + ext).run ();
361 return NO_ERROR;
362}
363
364bool
365Script::isAScript (const std::string& file)
366{
367 // is a directory path
368 if ('/' == file[file.size()-1])
369 return false;
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;
376}
377
378bool
379Script::match (const std::string& stratum, const std::string& type)
380{
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 ();
431}
432
433std::string
434Script::baseName() const
435{
436 return scriptBaseName;
437}
438
439std::string
440Script::fullName() const
441{
442 return scriptName;
443}
444
445char const Script::ETCPostinstall[] = "/etc/postinstall/";
446char const Script::allowedStrata[] = "0_z";
447char const Script::allowedTypes[] = "pr";
This page took 0.029477 seconds and 6 git commands to generate.