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. */
44 #include "setup_version.h"
47 #include "propsheet.h"
51 #include "AntiVirus.h"
62 #include "postinstallresults.h"
64 #include "getopt++/GetOption.h"
65 #include "getopt++/BoolOption.h"
66 #include "getopt++/StringOption.h"
67 #include "getopt++/StringChoiceOption.h"
70 #include "Exception.h"
73 #include "UserSettings.h"
74 #include "SourceSetting.h"
75 #include "ConnectionSetting.h"
76 #include "KeysSetting.h"
81 #ifdef __MINGW64_VERSION_MAJOR
86 bool is_new_install
= false;
87 std::string SetupArch
;
88 std::string SetupIniDir
;
92 static StringChoiceOption::StringChoices
symlink_types({
93 {"native", SymlinkTypeNative
},
94 {"lnk", SymlinkTypeShortcut
},
95 {"sys", SymlinkTypeMagic
},
96 {"wsl", SymlinkTypeWsl
},
99 static StringChoiceOption::StringChoices
quiet_types({
100 {"unattended", QuietUnattended
},
101 {"noinput", QuietNoInput
},
102 {"hidden", QuietHidden
},
105 static StringOption
Arch ("", 'a', "arch", IDS_HELPTEXT_ARCH
, false);
106 StringChoiceOption
UnattendedOption (quiet_types
, 'q', "quiet-mode", IDS_HELPTEXT_QUIET_MODE
, true, -1, QuietUnattended
);
107 static BoolOption
PackageManagerOption (false, 'M', "package-manager", IDS_HELPTEXT_PACKAGE_MANAGER
);
108 static BoolOption
NoAdminOption (false, 'B', "no-admin", IDS_HELPTEXT_NO_ADMIN
);
109 static BoolOption
WaitOption (false, 'W', "wait", IDS_HELPTEXT_WAIT
);
110 static BoolOption
HelpOption (false, 'h', "help", IDS_HELPTEXT_HELP
);
111 static BoolOption
VersionOption (false, 'V', "version", IDS_HELPTEXT_VERSION
);
112 static StringOption
SetupBaseNameOpt ("setup", 'i', "ini-basename", IDS_HELPTEXT_INI_BASENAME
, false);
113 BoolOption
UnsupportedOption (false, '\0', "allow-unsupported-windows", IDS_HELPTEXT_ALLOW_UNSUPPORTED_WINDOWS
);
114 static BoolOption
DeprecatedOption (false, 'w', "no-warn-deprecated-windows", IDS_HELPTEXT_NO_WARN_DEPRECATED_WINDOWS
);
115 static StringChoiceOption
SymlinkTypeOption(symlink_types
, '\0', "symlink-type", IDS_HELPTEXT_SYMLINK_TYPE
, false, SymlinkTypeMagic
);
116 static StringOption
GuiLangOption ("", '\0', "lang", IDS_HELPTEXT_LANG
);
118 std::string SetupBaseName
;
123 HANDLE my_stdout
= GetStdHandle (STD_OUTPUT_HANDLE
);
124 if (my_stdout
!= INVALID_HANDLE_VALUE
&& GetFileType (my_stdout
) != FILE_TYPE_UNKNOWN
)
127 if (AttachConsole ((DWORD
) -1))
129 std::ofstream
*conout
= new std::ofstream ("conout$");
130 std::cout
.rdbuf (conout
->rdbuf ());
135 // Other threads talk to these pages, so we need to have it externable.
136 ThreeBarProgressPage Progress
;
137 PostInstallResultsPage PostInstallResults
;
142 /* nondisplay classes */
143 LocalDirSetting localDir
;
144 SourceSetting SourceSettings
;
145 ConnectionSetting ConnectionSettings
;
146 SiteSetting ChosenSites
;
147 ExtraKeysSetting ExtraKeys
;
150 AntiVirusPage AntiVirus
;
153 LocalDirPage LocalDir
;
159 DesktopSetupPage Desktop
;
160 PropSheet MainWindow
;
162 Log (LOG_TIMESTAMP
) << "Current Directory: " << local_dir
<< endLog
;
164 // Initialize common controls
165 INITCOMMONCONTROLSEX icce
= { sizeof (INITCOMMONCONTROLSEX
),
166 ICC_WIN95_CLASSES
| ICC_LISTVIEW_CLASSES
};
167 InitCommonControlsEx (&icce
);
169 // Initialize COM and ShellLink instance here. For some reason
170 // Windows 7 fails to create the ShellLink instance if this is
171 // done later, in the thread which actually creates the shortcuts.
172 extern IShellLink
*sl
;
173 CoInitializeEx (NULL
, COINIT_APARTMENTTHREADED
);
174 HRESULT res
= CoCreateInstance (CLSID_ShellLink
, NULL
,
175 CLSCTX_INPROC_SERVER
, IID_IShellLink
,
179 mbox (NULL
, IDS_SHELLLINK_FAILED
, MB_OK
, res
);
182 // Init window class lib
183 Window::SetAppInstance (hinstance
);
197 PostInstallResults
.Create ();
200 // Add pages to sheet
201 MainWindow
.AddPage (&Splash
);
202 MainWindow
.AddPage (&AntiVirus
);
203 MainWindow
.AddPage (&Source
);
204 MainWindow
.AddPage (&Root
);
205 MainWindow
.AddPage (&LocalDir
);
206 MainWindow
.AddPage (&Net
);
207 MainWindow
.AddPage (&Site
);
208 MainWindow
.AddPage (&Chooser
);
209 MainWindow
.AddPage (&Prereq
);
210 MainWindow
.AddPage (&Confirm
);
211 MainWindow
.AddPage (&Progress
);
212 MainWindow
.AddPage (&PostInstallResults
);
213 MainWindow
.AddPage (&Desktop
);
215 // Create the PropSheet main window
216 MainWindow
.Create ();
225 WinMain (HINSTANCE h
,
226 HINSTANCE hPrevInstance
, LPSTR command_line
, int cmd_show
)
231 // Make sure the C runtime functions use the same codepage as the GUI
233 snprintf(locale
, sizeof locale
, ".%u", GetACP());
234 setlocale(LC_ALL
, locale
);
238 for (argc
= 0, _argv
= __argv
; *_argv
; _argv
++)
243 bool help_option
= false;
244 bool invalid_option
= false;
246 GetCurrentDirectory (MAX_PATH
, cwd
);
247 local_dir
= std::string (cwd
);
249 if (!GetOption::GetInstance ().Process (argc
,_argv
, NULL
))
250 help_option
= invalid_option
= true;
254 if (!((std::string
) Arch
).size ())
262 else if (((std::string
) Arch
).find ("64") != std::string::npos
)
264 else if (((std::string
) Arch
).find ("32") != std::string::npos
265 || ((std::string
) Arch
).find ("x86") != std::string::npos
)
269 char buff
[80 + ((std::string
) Arch
).size ()];
270 sprintf (buff
, "Invalid option for --arch: \"%s\"",
271 ((std::string
) Arch
).c_str ());
272 fprintf (stderr
, "*** %s\n", buff
);
276 if (GuiLangOption
.isPresent())
278 // If option's value isn't numeric, perhaps we should try to interpret
279 // it as a locale name?
280 int rc
= sscanf(((std::string
)GuiLangOption
).c_str(), "%hx", &langid
);
282 SetThreadUILanguage(langid
);
285 if (PackageManagerOption
)
286 unattended_mode
= chooseronly
;
288 if (UnattendedOption
< 0)
289 unattended_mode
= attended
;
291 unattended_mode
= unattended
;
293 bool output_only
= help_option
|| VersionOption
;
295 SetupBaseName
= SetupBaseNameOpt
;
296 SetupArch
= is_64bit
? "x86_64" : "x86";
297 SetupIniDir
= SetupArch
+"/";
299 /* Initialize well known SIDs. We need the admin SID to test if we're
300 supposed to elevate. */
301 nt_sec
.initialiseWellKnownSIDs ();
302 /* Check if we have to elevate. */
303 bool elevate
= !output_only
&& OSMajorVersion () >= 6
304 && !NoAdminOption
&& !nt_sec
.isRunAsAdmin ();
305 std::string elevate_extra_args
;
307 if (unattended_mode
|| output_only
|| !elevate
)
310 /* Start logging only if we don't elevate. Same for setting default
311 security settings. */
312 LogSingleton::SetInstance (*LogFile::createLogFile ());
313 const char *sep
= isdirsep (local_dir
[local_dir
.size () - 1])
315 /* Don't create log files for help or version output only. */
316 if (!elevate
&& !output_only
)
318 Logger ().setFile (LOG_BABBLE
, local_dir
+ sep
+ "setup.log.full",
320 Logger ().setFile (0, local_dir
+ sep
+ "setup.log", true);
321 Log (LOG_PLAIN
) << "Starting cygwin install, version "
322 << setup_version
<< endLog
;
325 /* Some confusion of interfaces here: Normally we try to write un-localized
326 strings to the log. However, if output_only is true, then we know that
327 Log() is only outputting to console, not a logfile, so using localized
332 Log (LOG_PLAIN
) << "\n" << LoadStringUtf8(IDS_HELPTEXT_ERROR
) << "\n" << endLog
;
333 Log (LOG_PLAIN
) << "Cygwin setup " << setup_version
<< endLog
;
334 Log (LOG_PLAIN
) << "\n" << LoadStringUtf8(IDS_HELPTEXT_HEADER
) << "\n" << endLog
;
335 GetOption::GetInstance ().ParameterUsage (Log (LOG_PLAIN
), LoadStringUtf8
);
336 Log (LOG_PLAIN
) << endLog
;
337 Log (LOG_PLAIN
) << LoadStringUtf8(IDS_HELPTEXT_FOOTER
) << endLog
;
338 Logger ().exit (invalid_option
? 1 : 0, false);
344 Log (LOG_PLAIN
) << "Cygwin setup " << setup_version
<< endLog
;
345 Logger ().exit (0, false);
349 /* Check if Cygwin works on this Windows architecture/version */
350 if (!UnsupportedOption
)
359 mbox (NULL
, IDS_UNSUPPORTED_WINDOWS_ARCH
,
360 MB_ICONEXCLAMATION
| MB_OK
);
361 Logger ().exit (1, false);
363 else if ((OSMajorVersion () < 6) ||
364 ((OSMajorVersion () == 6) && (OSMinorVersion() < 1)))
366 mbox (NULL
, IDS_UNSUPPORTED_WINDOWS_VERSION
,
367 MB_ICONEXCLAMATION
| MB_OK
);
368 Logger ().exit (1, false);
372 Warn if Windows version is deprecated for Cygwin: Current plans are to
373 deprecate Windows 7 & 8 sometime during the Cygwin DLL 3.4 lifetime
375 if (!DeprecatedOption
&& !elevate
)
377 if ((OSMajorVersion () == 6) && (OSMinorVersion() < 3))
378 mbox (NULL
, IDS_DEPRECATED_WINDOWS_VERSION
,
379 MB_ICONEXCLAMATION
| MB_OK
| MB_DSA_CHECKBOX
);
383 /* Set default DACL and Group. */
384 nt_sec
.setDefaultSecurity ();
387 If --symlink-type option isn't given, look for winsymlinks in CYGWIN
388 env var for a default
390 Since the current environment doesn't get passed to the process started
391 with with ShellExecuteEx, we need to convert the env var into an option
392 for that elevated instance.
394 if (!SymlinkTypeOption
.isPresent()) {
396 DWORD len
= GetEnvironmentVariable ("CYGWIN", &cygwin
[0], 0);
398 GetEnvironmentVariable ("CYGWIN", &cygwin
[0], len
);
400 if (cygwin
.find("winsymlinks:native") != std::string::npos
)
402 symlinkType
= SymlinkTypeNative
;
403 elevate_extra_args
.append("--symlink-type native");
405 else if (cygwin
.find("winsymlinks:wsl") != std::string::npos
)
407 symlinkType
= SymlinkTypeWsl
;
408 elevate_extra_args
.append("--symlink-type wsl");
410 else if (cygwin
.find("winsymlinks:sys") != std::string::npos
)
412 symlinkType
= SymlinkTypeMagic
;
413 elevate_extra_args
.append("--symlink-type sys");
415 else if (cygwin
.find("winsymlinks:lnk") != std::string::npos
)
417 // Ignore CYGWIN=winsymlinks:lnk, as '--symlink-type lnk' is not implemented
418 // symlinkType = SymlinkTypeShortcut;
419 // elevate_extra_args.append("--symlink-type lnk");
424 symlinkType
= (SymlinkTypeEnum
)(int)SymlinkTypeOption
;
427 if (symlinkType
== SymlinkTypeWsl
)
429 VersionInfo v
= GetVer();
430 if ((v
.major() < 10) ||
431 ((v
.major() == 10) && (v
.buildNumber() < 14393)))
433 fprintf (stderr
, "*** --symlink-type wsl requires Windows 10 1607 or later\n");
437 else if (symlinkType
== SymlinkTypeNative
)
439 VersionInfo v
= GetVer();
442 fprintf (stderr
, "*** --symlink-type native requires Windows 6.0 or later\n");
446 if (!(elevate
|| is_developer_mode() || nt_sec
.hasSymlinkCreationRights()))
448 fprintf (stderr
, "*** --symlink-type native requires SeCreateSymbolicLink privilege or 'Developer Mode'\n");
452 else if (symlinkType
== SymlinkTypeShortcut
)
454 fprintf (stderr
, "*** --symlink-type lnk is not implemented\n");
460 char exe_path
[MAX_PATH
];
461 if (!GetModuleFileName(NULL
, exe_path
, ARRAYSIZE(exe_path
)))
464 SHELLEXECUTEINFO sei
= { sizeof(sei
) };
465 sei
.lpVerb
= "runas";
466 sei
.lpFile
= exe_path
;
467 sei
.nShow
= SW_NORMAL
;
469 sei
.fMask
|= SEE_MASK_NOCLOSEPROCESS
;
471 // Avoid another isRunAsAdmin check in the child.
472 std::string
command_line_cs (command_line
);
473 command_line_cs
+= " -";
474 command_line_cs
+= NoAdminOption
.shortOption();
475 command_line_cs
+= " ";
476 command_line_cs
+= elevate_extra_args
;
477 sei
.lpParameters
= command_line_cs
.c_str ();
479 if (ShellExecuteEx(&sei
))
482 /* Wait until child process is finished. */
483 if (WaitOption
&& sei
.hProcess
!= NULL
)
484 if (!WaitForSingleObject (sei
.hProcess
, INFINITE
))
485 GetExitCodeProcess (sei
.hProcess
, &exitcode
);
486 Logger ().setExitMsg (IDS_ELEVATED
);
487 Logger ().exit (exitcode
, false);
489 Log (LOG_PLAIN
) << "Starting elevated child process failed" << endLog
;
490 Logger ().exit (1, false);
494 UserSettings Settings
;
495 UserSettings::instance().load (local_dir
);
497 Settings
.save (); // Clean exit.. save user options.
499 Logger ().setExitMsg (IDS_REBOOT_REQUIRED
);
500 Logger ().exit (rebootneeded
? IDS_REBOOT_REQUIRED
: 0);
505 TOPLEVEL_CATCH(NULL
, "main");