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
)
230 // Make sure Windows DLLs only delay-load further DLLs from System32
231 typedef BOOL (WINAPI
*PFNSETDEFAULTDLLDIRECTORIES
)(DWORD
);
232 PFNSETDEFAULTDLLDIRECTORIES pfnSetDefaultDllDirectories
= 0;
233 pfnSetDefaultDllDirectories
= (PFNSETDEFAULTDLLDIRECTORIES
)GetProcAddress(GetModuleHandle("kernel32.dll"), "SetDefaultDllDirectories");
234 if (pfnSetDefaultDllDirectories
)
235 pfnSetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32
);
237 // If we can't do that to remove the application directory from the search
238 // path, at least remove the current directory from the default DLL search
242 // Make sure the C runtime functions use the same codepage as the GUI
244 snprintf(locale
, sizeof locale
, ".%u", GetACP());
245 setlocale(LC_ALL
, locale
);
249 for (argc
= 0, _argv
= __argv
; *_argv
; _argv
++)
254 bool help_option
= false;
255 bool invalid_option
= false;
257 GetCurrentDirectory (MAX_PATH
, cwd
);
258 local_dir
= std::string (cwd
);
260 if (!GetOption::GetInstance ().Process (argc
,_argv
, NULL
))
261 help_option
= invalid_option
= true;
265 if (!((std::string
) Arch
).size ())
273 else if (((std::string
) Arch
).find ("64") != std::string::npos
)
275 else if (((std::string
) Arch
).find ("32") != std::string::npos
276 || ((std::string
) Arch
).find ("x86") != std::string::npos
)
280 char buff
[80 + ((std::string
) Arch
).size ()];
281 sprintf (buff
, "Invalid option for --arch: \"%s\"",
282 ((std::string
) Arch
).c_str ());
283 fprintf (stderr
, "*** %s\n", buff
);
287 if (GuiLangOption
.isPresent())
289 // If option's value isn't numeric, perhaps we should try to interpret
290 // it as a locale name?
291 int rc
= sscanf(((std::string
)GuiLangOption
).c_str(), "%hx", &langid
);
293 SetThreadUILanguage(langid
);
296 if (PackageManagerOption
)
297 unattended_mode
= chooseronly
;
299 if (UnattendedOption
< 0)
300 unattended_mode
= attended
;
302 unattended_mode
= unattended
;
304 bool output_only
= help_option
|| VersionOption
;
306 SetupBaseName
= SetupBaseNameOpt
;
307 SetupArch
= is_64bit
? "x86_64" : "x86";
308 SetupIniDir
= SetupArch
+"/";
310 /* Initialize well known SIDs. We need the admin SID to test if we're
311 supposed to elevate. */
312 nt_sec
.initialiseWellKnownSIDs ();
313 /* Check if we have to elevate. */
314 bool elevate
= !output_only
&& OSMajorVersion () >= 6
315 && !NoAdminOption
&& !nt_sec
.isRunAsAdmin ();
316 std::string elevate_extra_args
;
318 if (unattended_mode
|| output_only
|| !elevate
)
321 /* Start logging only if we don't elevate. Same for setting default
322 security settings. */
323 LogSingleton::SetInstance (*LogFile::createLogFile ());
324 const char *sep
= isdirsep (local_dir
[local_dir
.size () - 1])
326 /* Don't create log files for help or version output only. */
327 if (!elevate
&& !output_only
)
329 Logger ().setFile (LOG_BABBLE
, local_dir
+ sep
+ "setup.log.full",
331 Logger ().setFile (0, local_dir
+ sep
+ "setup.log", true);
332 Log (LOG_PLAIN
) << "Starting cygwin install, version "
333 << setup_version
<< endLog
;
336 /* Some confusion of interfaces here: Normally we try to write un-localized
337 strings to the log. However, if output_only is true, then we know that
338 Log() is only outputting to console, not a logfile, so using localized
343 Log (LOG_PLAIN
) << "\n" << LoadStringUtf8(IDS_HELPTEXT_ERROR
) << "\n" << endLog
;
344 Log (LOG_PLAIN
) << "Cygwin setup " << setup_version
<< endLog
;
345 Log (LOG_PLAIN
) << "\n" << LoadStringUtf8(IDS_HELPTEXT_HEADER
) << "\n" << endLog
;
346 GetOption::GetInstance ().ParameterUsage (Log (LOG_PLAIN
), LoadStringUtf8
);
347 Log (LOG_PLAIN
) << endLog
;
348 Log (LOG_PLAIN
) << LoadStringUtf8(IDS_HELPTEXT_FOOTER
) << endLog
;
349 Logger ().exit (invalid_option
? 1 : 0, false);
355 Log (LOG_PLAIN
) << "Cygwin setup " << setup_version
<< endLog
;
356 Logger ().exit (0, false);
360 /* Check if Cygwin works on this Windows architecture/version */
361 if (!UnsupportedOption
)
370 mbox (NULL
, IDS_UNSUPPORTED_WINDOWS_ARCH
,
371 MB_ICONEXCLAMATION
| MB_OK
);
372 Logger ().exit (1, false);
374 else if ((OSMajorVersion () < 6) ||
375 ((OSMajorVersion () == 6) && (OSMinorVersion() < 3)))
377 mbox (NULL
, IDS_UNSUPPORTED_WINDOWS_VERSION
,
378 MB_ICONEXCLAMATION
| MB_OK
);
379 Logger ().exit (1, false);
384 Warn if Windows version is deprecated for Cygwin
386 if (!DeprecatedOption
&& !elevate
)
388 if ((OSMajorVersion () == 6) && (OSMinorVersion() < 4))
389 mbox (NULL
, IDS_DEPRECATED_WINDOWS_VERSION
,
390 MB_ICONEXCLAMATION
| MB_OK
| MB_DSA_CHECKBOX
);
395 /* Set default DACL and Group. */
396 nt_sec
.setDefaultSecurity ();
399 If --symlink-type option isn't given, look for winsymlinks in CYGWIN
400 env var for a default
402 Since the current environment doesn't get passed to the process started
403 with with ShellExecuteEx, we need to convert the env var into an option
404 for that elevated instance.
406 if (!SymlinkTypeOption
.isPresent()) {
408 DWORD len
= GetEnvironmentVariable ("CYGWIN", &cygwin
[0], 0);
410 GetEnvironmentVariable ("CYGWIN", &cygwin
[0], len
);
412 if (cygwin
.find("winsymlinks:native") != std::string::npos
)
414 symlinkType
= SymlinkTypeNative
;
415 elevate_extra_args
.append("--symlink-type native");
417 else if (cygwin
.find("winsymlinks:wsl") != std::string::npos
)
419 symlinkType
= SymlinkTypeWsl
;
420 elevate_extra_args
.append("--symlink-type wsl");
422 else if (cygwin
.find("winsymlinks:sys") != std::string::npos
)
424 symlinkType
= SymlinkTypeMagic
;
425 elevate_extra_args
.append("--symlink-type sys");
427 else if (cygwin
.find("winsymlinks:lnk") != std::string::npos
)
429 // Ignore CYGWIN=winsymlinks:lnk, as '--symlink-type lnk' is not implemented
430 // symlinkType = SymlinkTypeShortcut;
431 // elevate_extra_args.append("--symlink-type lnk");
436 symlinkType
= (SymlinkTypeEnum
)(int)SymlinkTypeOption
;
439 if (symlinkType
== SymlinkTypeWsl
)
441 VersionInfo v
= GetVer();
442 if ((v
.major() < 10) ||
443 ((v
.major() == 10) && (v
.buildNumber() < 14393)))
445 fprintf (stderr
, "*** --symlink-type wsl requires Windows 10 1607 or later\n");
449 else if (symlinkType
== SymlinkTypeNative
)
451 VersionInfo v
= GetVer();
454 fprintf (stderr
, "*** --symlink-type native requires Windows 6.0 or later\n");
458 if (!(elevate
|| is_developer_mode() || nt_sec
.hasSymlinkCreationRights()))
460 fprintf (stderr
, "*** --symlink-type native requires SeCreateSymbolicLink privilege or 'Developer Mode'\n");
464 else if (symlinkType
== SymlinkTypeShortcut
)
466 fprintf (stderr
, "*** --symlink-type lnk is not implemented\n");
472 char exe_path
[MAX_PATH
];
473 if (!GetModuleFileName(NULL
, exe_path
, ARRAYSIZE(exe_path
)))
476 SHELLEXECUTEINFO sei
= { sizeof(sei
) };
477 sei
.lpVerb
= "runas";
478 sei
.lpFile
= exe_path
;
479 sei
.nShow
= SW_NORMAL
;
481 sei
.fMask
|= SEE_MASK_NOCLOSEPROCESS
;
483 // Avoid another isRunAsAdmin check in the child.
484 std::string
command_line_cs (command_line
);
485 command_line_cs
+= " -";
486 command_line_cs
+= NoAdminOption
.shortOption();
487 command_line_cs
+= " ";
488 command_line_cs
+= elevate_extra_args
;
489 sei
.lpParameters
= command_line_cs
.c_str ();
491 if (ShellExecuteEx(&sei
))
494 /* Wait until child process is finished. */
495 if (WaitOption
&& sei
.hProcess
!= NULL
)
496 if (!WaitForSingleObject (sei
.hProcess
, INFINITE
))
497 GetExitCodeProcess (sei
.hProcess
, &exitcode
);
498 Logger ().setExitMsg (IDS_ELEVATED
);
499 Logger ().exit (exitcode
, false);
501 Log (LOG_PLAIN
) << "Starting elevated child process failed" << endLog
;
502 Logger ().exit (1, false);
506 UserSettings Settings
;
507 UserSettings::instance().load (local_dir
);
509 Settings
.save (); // Clean exit.. save user options.
511 Logger ().setExitMsg (IDS_REBOOT_REQUIRED
);
512 Logger ().exit (rebootneeded
? IDS_REBOOT_REQUIRED
: 0);
517 TOPLEVEL_CATCH(NULL
, "main");