Suggest URLs for updated setup based on build architecture
[cygwin-setup.git] / main.cc
blobcf9e32348573efbd0c9c7bc7f5d191c8e47d577c
1 /*
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
11 * http://www.gnu.org/
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. */
29 #include "win32.h"
30 #include <commctrl.h>
31 #include <shellapi.h>
32 #include "shlobj.h"
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include "resource.h"
37 #include "dialog.h"
38 #include "state.h"
39 #include "quiet.h"
40 #include "msg.h"
41 #include "find.h"
42 #include "mount.h"
43 #include "LogFile.h"
44 #include "setup_version.h"
46 #include "proppage.h"
47 #include "propsheet.h"
49 // Page class headers
50 #include "splash.h"
51 #include "AntiVirus.h"
52 #include "source.h"
53 #include "root.h"
54 #include "localdir.h"
55 #include "net.h"
56 #include "site.h"
57 #include "choose.h"
58 #include "prereq.h"
59 #include "confirm.h"
60 #include "threebar.h"
61 #include "desktop.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"
68 #include "mklink2.h"
70 #include "Exception.h"
71 #include <stdexcept>
73 #include "UserSettings.h"
74 #include "SourceSetting.h"
75 #include "ConnectionSetting.h"
76 #include "KeysSetting.h"
78 #include <wincon.h>
79 #include <fstream>
81 #ifdef __MINGW64_VERSION_MAJOR
82 extern char **_argv;
83 #endif
85 bool is_64bit;
86 bool is_new_install = false;
87 std::string SetupArch;
88 std::string SetupIniDir;
90 HINSTANCE hinstance;
92 static StringChoiceOption::StringChoices symlink_types({
93 {"native", SymlinkTypeNative},
94 {"lnk", SymlinkTypeShortcut},
95 {"sys", SymlinkTypeMagic},
96 {"wsl", SymlinkTypeWsl},
97 });
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;
120 static void inline
121 set_cout ()
123 HANDLE my_stdout = GetStdHandle (STD_OUTPUT_HANDLE);
124 if (my_stdout != INVALID_HANDLE_VALUE && GetFileType (my_stdout) != FILE_TYPE_UNKNOWN)
125 return;
127 if (AttachConsole ((DWORD) -1))
129 std::ofstream *conout = new std::ofstream ("conout$");
130 std::cout.rdbuf (conout->rdbuf ());
131 std::cout.flush ();
135 // Other threads talk to these pages, so we need to have it externable.
136 ThreeBarProgressPage Progress;
137 PostInstallResultsPage PostInstallResults;
139 static inline void
140 main_display ()
142 /* nondisplay classes */
143 LocalDirSetting localDir;
144 SourceSetting SourceSettings;
145 ConnectionSetting ConnectionSettings;
146 SiteSetting ChosenSites;
147 ExtraKeysSetting ExtraKeys;
149 SplashPage Splash;
150 AntiVirusPage AntiVirus;
151 SourcePage Source;
152 RootPage Root;
153 LocalDirPage LocalDir;
154 NetPage Net;
155 SitePage Site;
156 ChooserPage Chooser;
157 PrereqPage Prereq;
158 ConfirmPage Confirm;
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,
176 (LPVOID *) & sl);
177 if (res)
179 mbox (NULL, IDS_SHELLLINK_FAILED, MB_OK, res);
182 // Init window class lib
183 Window::SetAppInstance (hinstance);
185 // Create pages
186 Splash.Create ();
187 AntiVirus.Create ();
188 Source.Create ();
189 Root.Create ();
190 LocalDir.Create ();
191 Net.Create ();
192 Site.Create ();
193 Chooser.Create ();
194 Prereq.Create ();
195 Confirm.Create ();
196 Progress.Create ();
197 PostInstallResults.Create ();
198 Desktop.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 ();
218 // Uninitalize COM
219 if (sl)
220 sl->Release ();
221 CoUninitialize ();
224 int WINAPI
225 WinMain (HINSTANCE h,
226 HINSTANCE hPrevInstance, LPSTR command_line, int cmd_show)
228 hinstance = h;
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
239 // order.
240 SetDllDirectory("");
242 // Make sure the C runtime functions use the same codepage as the GUI
243 char locale[12];
244 snprintf(locale, sizeof locale, ".%u", GetACP());
245 setlocale(LC_ALL, locale);
247 char **_argv;
248 int argc;
249 for (argc = 0, _argv = __argv; *_argv; _argv++)
250 ++argc;
251 _argv = __argv;
253 try {
254 bool help_option = false;
255 bool invalid_option = false;
256 char cwd[MAX_PATH];
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;
262 else if (HelpOption)
263 help_option = true;
265 if (!((std::string) Arch).size ())
267 #ifdef __x86_64__
268 is_64bit = true;
269 #else
270 is_64bit = false;
271 #endif
273 else if (((std::string) Arch).find ("64") != std::string::npos)
274 is_64bit = true;
275 else if (((std::string) Arch).find ("32") != std::string::npos
276 || ((std::string) Arch).find ("x86") != std::string::npos)
277 is_64bit = false;
278 else
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);
284 exit (1);
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);
292 if (rc > 0)
293 SetThreadUILanguage(langid);
296 if (PackageManagerOption)
297 unattended_mode = chooseronly;
298 else
299 if (UnattendedOption < 0)
300 unattended_mode = attended;
301 else
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)
319 set_cout ();
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])
325 ? "" : "\\";
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",
330 false);
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
339 text is ok. */
340 if (help_option)
342 if (invalid_option)
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);
350 goto finish_up;
353 if (VersionOption)
355 Log (LOG_PLAIN) << "Cygwin setup " << setup_version << endLog;
356 Logger ().exit (0, false);
357 goto finish_up;
360 /* Check if Cygwin works on this Windows architecture/version */
361 if (!UnsupportedOption)
364 #ifdef _X86_
365 (TRUE)
366 #else
367 (!is_64bit)
368 #endif
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);
382 #if 0
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);
392 #endif
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()) {
407 std::string cygwin;
408 DWORD len = GetEnvironmentVariable ("CYGWIN", &cygwin[0], 0);
409 cygwin.resize(len);
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");
434 else
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");
446 exit(1);
449 else if (symlinkType == SymlinkTypeNative)
451 VersionInfo v = GetVer();
452 if (v.major() < 6)
454 fprintf (stderr, "*** --symlink-type native requires Windows 6.0 or later\n");
455 exit(1);
458 if (!(elevate || is_developer_mode() || nt_sec.hasSymlinkCreationRights()))
460 fprintf (stderr, "*** --symlink-type native requires SeCreateSymbolicLink privilege or 'Developer Mode'\n");
461 exit(1);
464 else if (symlinkType == SymlinkTypeShortcut)
466 fprintf (stderr, "*** --symlink-type lnk is not implemented\n");
467 exit(1);
470 if (elevate)
472 char exe_path[MAX_PATH];
473 if (!GetModuleFileName(NULL, exe_path, ARRAYSIZE(exe_path)))
474 goto finish_up;
476 SHELLEXECUTEINFO sei = { sizeof(sei) };
477 sei.lpVerb = "runas";
478 sei.lpFile = exe_path;
479 sei.nShow = SW_NORMAL;
480 if (WaitOption)
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))
493 DWORD exitcode = 0;
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);
504 else
506 UserSettings Settings;
507 UserSettings::instance().load (local_dir);
508 main_display ();
509 Settings.save (); // Clean exit.. save user options.
510 if (rebootneeded)
511 Logger ().setExitMsg (IDS_REBOOT_REQUIRED);
512 Logger ().exit (rebootneeded ? IDS_REBOOT_REQUIRED : 0);
514 finish_up:
517 TOPLEVEL_CATCH(NULL, "main");
519 // Never reached
520 return 0;