2 * Copyright (c) 2000, Red Hat, Inc.
3 * Copyright (c) 2003, Robert Collins <rbtcollins@hotmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * A copy of the GNU General Public License can be found at
13 * Written by DJ Delorie <dj@cygnus.com>
14 * Robert Collins <rbtcollins@hotmail.com>
19 /* OK, here's how this works. Each of the steps needed for install -
20 dialogs, downloads, installs - are in their own files and have some
21 "do_*" function (prototype in dialog.h) and a resource id (IDD_* or
22 IDD_S_* in resource.h) for that step. Each step is responsible for
23 selecting the next step! See the NEXT macro in dialog.h. Note
24 that the IDD_S_* ids are fake; those are for steps that don't
25 really have a controlling dialog (some have progress dialogs, but
26 those don't count, although they could). Replace the IDD_S_* with
27 IDD_* if you create a real dialog for those steps. */
43 #include "setup_version.h"
46 #include "propsheet.h"
50 #include "AntiVirus.h"
61 #include "postinstallresults.h"
63 #include "getopt++/GetOption.h"
64 #include "getopt++/BoolOption.h"
65 #include "getopt++/StringOption.h"
66 #include "getopt++/StringChoiceOption.h"
69 #include "Exception.h"
72 #include "UserSettings.h"
73 #include "SourceSetting.h"
74 #include "ConnectionSetting.h"
75 #include "KeysSetting.h"
80 #ifdef __MINGW64_VERSION_MAJOR
85 bool is_new_install
= false;
86 std::string SetupArch
;
87 std::string SetupIniDir
;
91 static StringChoiceOption::StringChoices
symlink_types({
92 {"native", SymlinkTypeNative
},
93 {"lnk", SymlinkTypeShortcut
},
94 {"sys", SymlinkTypeMagic
},
95 {"wsl", SymlinkTypeWsl
},
98 static StringOption
Arch ("", 'a', "arch", IDS_HELPTEXT_ARCH
, false);
99 static BoolOption
UnattendedOption (false, 'q', "quiet-mode", IDS_HELPTEXT_QUIET_MODE
);
100 static BoolOption
PackageManagerOption (false, 'M', "package-manager", IDS_HELPTEXT_PACKAGE_MANAGER
);
101 static BoolOption
NoAdminOption (false, 'B', "no-admin", IDS_HELPTEXT_NO_ADMIN
);
102 static BoolOption
WaitOption (false, 'W', "wait", IDS_HELPTEXT_WAIT
);
103 static BoolOption
HelpOption (false, 'h', "help", IDS_HELPTEXT_HELP
);
104 static BoolOption
VersionOption (false, 'V', "version", IDS_HELPTEXT_VERSION
);
105 static StringOption
SetupBaseNameOpt ("setup", 'i', "ini-basename", IDS_HELPTEXT_INI_BASENAME
, false);
106 BoolOption
UnsupportedOption (false, '\0', "allow-unsupported-windows", IDS_HELPTEXT_ALLOW_UNSUPPORTED_WINDOWS
);
107 static BoolOption
DeprecatedOption (false, 'w', "no-warn-deprecated-windows", IDS_HELPTEXT_NO_WARN_DEPRECATED_WINDOWS
);
108 static StringChoiceOption
SymlinkTypeOption(symlink_types
, '\0', "symlink-type", IDS_HELPTEXT_SYMLINK_TYPE
, false, SymlinkTypeMagic
);
109 static StringOption
GuiLangOption ("", '\0', "lang", IDS_HELPTEXT_LANG
);
111 std::string SetupBaseName
;
116 HANDLE my_stdout
= GetStdHandle (STD_OUTPUT_HANDLE
);
117 if (my_stdout
!= INVALID_HANDLE_VALUE
&& GetFileType (my_stdout
) != FILE_TYPE_UNKNOWN
)
120 if (AttachConsole ((DWORD
) -1))
122 std::ofstream
*conout
= new std::ofstream ("conout$");
123 std::cout
.rdbuf (conout
->rdbuf ());
128 // Other threads talk to these pages, so we need to have it externable.
129 ThreeBarProgressPage Progress
;
130 PostInstallResultsPage PostInstallResults
;
135 /* nondisplay classes */
136 LocalDirSetting localDir
;
137 SourceSetting SourceSettings
;
138 ConnectionSetting ConnectionSettings
;
139 SiteSetting ChosenSites
;
140 ExtraKeysSetting ExtraKeys
;
143 AntiVirusPage AntiVirus
;
146 LocalDirPage LocalDir
;
152 DesktopSetupPage Desktop
;
153 PropSheet MainWindow
;
155 Log (LOG_TIMESTAMP
) << "Current Directory: " << local_dir
<< endLog
;
157 // Initialize common controls
158 INITCOMMONCONTROLSEX icce
= { sizeof (INITCOMMONCONTROLSEX
),
159 ICC_WIN95_CLASSES
| ICC_LISTVIEW_CLASSES
};
160 InitCommonControlsEx (&icce
);
162 // Initialize COM and ShellLink instance here. For some reason
163 // Windows 7 fails to create the ShellLink instance if this is
164 // done later, in the thread which actually creates the shortcuts.
165 extern IShellLink
*sl
;
166 CoInitializeEx (NULL
, COINIT_APARTMENTTHREADED
);
167 HRESULT res
= CoCreateInstance (CLSID_ShellLink
, NULL
,
168 CLSCTX_INPROC_SERVER
, IID_IShellLink
,
172 mbox (NULL
, IDS_SHELLLINK_FAILED
, MB_OK
, res
);
175 // Init window class lib
176 Window::SetAppInstance (hinstance
);
190 PostInstallResults
.Create ();
193 // Add pages to sheet
194 MainWindow
.AddPage (&Splash
);
195 MainWindow
.AddPage (&AntiVirus
);
196 MainWindow
.AddPage (&Source
);
197 MainWindow
.AddPage (&Root
);
198 MainWindow
.AddPage (&LocalDir
);
199 MainWindow
.AddPage (&Net
);
200 MainWindow
.AddPage (&Site
);
201 MainWindow
.AddPage (&Chooser
);
202 MainWindow
.AddPage (&Prereq
);
203 MainWindow
.AddPage (&Confirm
);
204 MainWindow
.AddPage (&Progress
);
205 MainWindow
.AddPage (&PostInstallResults
);
206 MainWindow
.AddPage (&Desktop
);
208 // Create the PropSheet main window
209 MainWindow
.Create ();
218 WinMain (HINSTANCE h
,
219 HINSTANCE hPrevInstance
, LPSTR command_line
, int cmd_show
)
224 // Make sure the C runtime functions use the same codepage as the GUI
226 snprintf(locale
, sizeof locale
, ".%u", GetACP());
227 setlocale(LC_ALL
, locale
);
231 for (argc
= 0, _argv
= __argv
; *_argv
; _argv
++)
236 bool help_option
= false;
237 bool invalid_option
= false;
239 GetCurrentDirectory (MAX_PATH
, cwd
);
240 local_dir
= std::string (cwd
);
242 if (!GetOption::GetInstance ().Process (argc
,_argv
, NULL
))
243 help_option
= invalid_option
= true;
247 if (!((std::string
) Arch
).size ())
255 else if (((std::string
) Arch
).find ("64") != std::string::npos
)
257 else if (((std::string
) Arch
).find ("32") != std::string::npos
258 || ((std::string
) Arch
).find ("x86") != std::string::npos
)
262 char buff
[80 + ((std::string
) Arch
).size ()];
263 sprintf (buff
, "Invalid option for --arch: \"%s\"",
264 ((std::string
) Arch
).c_str ());
265 fprintf (stderr
, "*** %s\n", buff
);
269 if (GuiLangOption
.isPresent())
271 // If option's value isn't numeric, perhaps we should try to interpret
272 // it as a locale name?
273 int rc
= sscanf(((std::string
)GuiLangOption
).c_str(), "%hx", &langid
);
275 SetThreadUILanguage(langid
);
278 unattended_mode
= PackageManagerOption
? chooseronly
279 : (UnattendedOption
? unattended
: attended
);
281 bool output_only
= help_option
|| VersionOption
;
283 SetupBaseName
= SetupBaseNameOpt
;
284 SetupArch
= is_64bit
? "x86_64" : "x86";
285 SetupIniDir
= SetupArch
+"/";
287 /* Initialize well known SIDs. We need the admin SID to test if we're
288 supposed to elevate. */
289 nt_sec
.initialiseWellKnownSIDs ();
290 /* Check if we have to elevate. */
291 bool elevate
= !output_only
&& OSMajorVersion () >= 6
292 && !NoAdminOption
&& !nt_sec
.isRunAsAdmin ();
293 std::string elevate_extra_args
;
295 if (unattended_mode
|| output_only
|| !elevate
)
298 /* Start logging only if we don't elevate. Same for setting default
299 security settings. */
300 LogSingleton::SetInstance (*LogFile::createLogFile ());
301 const char *sep
= isdirsep (local_dir
[local_dir
.size () - 1])
303 /* Don't create log files for help or version output only. */
304 if (!elevate
&& !output_only
)
306 Logger ().setFile (LOG_BABBLE
, local_dir
+ sep
+ "setup.log.full",
308 Logger ().setFile (0, local_dir
+ sep
+ "setup.log", true);
309 Log (LOG_PLAIN
) << "Starting cygwin install, version "
310 << setup_version
<< endLog
;
313 /* Some confusion of interfaces here: Normally we try to write un-localized
314 strings to the log. However, if output_only is true, then we know that
315 Log() is only outputting to console, not a logfile, so using localized
320 Log (LOG_PLAIN
) << "\n" << LoadStringUtf8(IDS_HELPTEXT_ERROR
) << "\n" << endLog
;
321 Log (LOG_PLAIN
) << "Cygwin setup " << setup_version
<< endLog
;
322 Log (LOG_PLAIN
) << "\n" << LoadStringUtf8(IDS_HELPTEXT_HEADER
) << "\n" << endLog
;
323 GetOption::GetInstance ().ParameterUsage (Log (LOG_PLAIN
), LoadStringUtf8
);
324 Log (LOG_PLAIN
) << endLog
;
325 Log (LOG_PLAIN
) << LoadStringUtf8(IDS_HELPTEXT_FOOTER
) << endLog
;
326 Logger ().exit (invalid_option
? 1 : 0, false);
332 Log (LOG_PLAIN
) << "Cygwin setup " << setup_version
<< endLog
;
333 Logger ().exit (0, false);
337 /* Check if Cygwin works on this Windows version */
338 if (!UnsupportedOption
&& (OSMajorVersion () < 6))
340 mbox (NULL
, IDS_UNSUPPORTED_WINDOWS_VERSION
,
341 MB_ICONEXCLAMATION
| MB_OK
);
342 Logger ().exit (1, false);
345 /* Warn if Windows version is deprecated for Cygwin */
346 if (!DeprecatedOption
&& !elevate
)
354 mbox (NULL
, IDS_DEPRECATED_WINDOWS_ARCH
,
355 MB_ICONEXCLAMATION
| MB_OK
| MB_DSA_CHECKBOX
);
356 else if ((OSMajorVersion () == 6) && (OSMinorVersion() < 1))
357 mbox (NULL
, IDS_DEPRECATED_WINDOWS_VERSION
,
358 MB_ICONEXCLAMATION
| MB_OK
| MB_DSA_CHECKBOX
);
361 /* Set default DACL and Group. */
362 nt_sec
.setDefaultSecurity ();
365 If --symlink-type option isn't given, look for winsymlinks in CYGWIN
366 env var for a default
368 Since the current environment doesn't get passed to the process started
369 with with ShellExecuteEx, we need to convert the env var into an option
370 for that elevated instance.
372 if (!SymlinkTypeOption
.isPresent()) {
374 DWORD len
= GetEnvironmentVariable ("CYGWIN", &cygwin
[0], 0);
376 GetEnvironmentVariable ("CYGWIN", &cygwin
[0], len
);
378 if (cygwin
.find("winsymlinks:native") != std::string::npos
)
380 symlinkType
= SymlinkTypeNative
;
381 elevate_extra_args
.append("--symlink-type native");
383 else if (cygwin
.find("winsymlinks:wsl") != std::string::npos
)
385 symlinkType
= SymlinkTypeWsl
;
386 elevate_extra_args
.append("--symlink-type wsl");
388 else if (cygwin
.find("winsymlinks:sys") != std::string::npos
)
390 symlinkType
= SymlinkTypeMagic
;
391 elevate_extra_args
.append("--symlink-type sys");
393 else if (cygwin
.find("winsymlinks:lnk") != std::string::npos
)
395 // Ignore CYGWIN=winsymlinks:lnk, as '--symlink-type lnk' is not implemented
396 // symlinkType = SymlinkTypeShortcut;
397 // elevate_extra_args.append("--symlink-type lnk");
402 symlinkType
= (SymlinkTypeEnum
)(int)SymlinkTypeOption
;
405 if (symlinkType
== SymlinkTypeWsl
)
407 VersionInfo v
= GetVer();
408 if ((v
.major() < 10) ||
409 ((v
.major() == 10) && (v
.buildNumber() < 14393)))
411 fprintf (stderr
, "*** --symlink-type wsl requires Windows 10 1607 or later\n");
415 else if (symlinkType
== SymlinkTypeNative
)
417 VersionInfo v
= GetVer();
420 fprintf (stderr
, "*** --symlink-type native requires Windows 6.0 or later\n");
424 if (!(elevate
|| is_developer_mode() || nt_sec
.hasSymlinkCreationRights()))
426 fprintf (stderr
, "*** --symlink-type native requires SeCreateSymbolicLink privilege or 'Developer Mode'\n");
430 else if (symlinkType
== SymlinkTypeShortcut
)
432 fprintf (stderr
, "*** --symlink-type lnk is not implemented\n");
438 char exe_path
[MAX_PATH
];
439 if (!GetModuleFileName(NULL
, exe_path
, ARRAYSIZE(exe_path
)))
442 SHELLEXECUTEINFO sei
= { sizeof(sei
) };
443 sei
.lpVerb
= "runas";
444 sei
.lpFile
= exe_path
;
445 sei
.nShow
= SW_NORMAL
;
447 sei
.fMask
|= SEE_MASK_NOCLOSEPROCESS
;
449 // Avoid another isRunAsAdmin check in the child.
450 std::string
command_line_cs (command_line
);
451 command_line_cs
+= " -";
452 command_line_cs
+= NoAdminOption
.shortOption();
453 command_line_cs
+= " ";
454 command_line_cs
+= elevate_extra_args
;
455 sei
.lpParameters
= command_line_cs
.c_str ();
457 if (ShellExecuteEx(&sei
))
460 /* Wait until child process is finished. */
461 if (WaitOption
&& sei
.hProcess
!= NULL
)
462 if (!WaitForSingleObject (sei
.hProcess
, INFINITE
))
463 GetExitCodeProcess (sei
.hProcess
, &exitcode
);
464 Logger ().setExitMsg (IDS_ELEVATED
);
465 Logger ().exit (exitcode
, false);
467 Log (LOG_PLAIN
) << "Starting elevated child process failed" << endLog
;
468 Logger ().exit (1, false);
472 UserSettings Settings
;
473 UserSettings::instance().load (local_dir
);
475 Settings
.save (); // Clean exit.. save user options.
477 Logger ().setExitMsg (IDS_REBOOT_REQUIRED
);
478 Logger ().exit (rebootneeded
? IDS_REBOOT_REQUIRED
: 0);
483 TOPLEVEL_CATCH(NULL
, "main");