Translated using Weblate (Chinese (Simplified))
[cygwin-setup.git] / main.cc
blob01c90b95bffb0cd961d12fc48c2d3a919156f66f
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)
229 hinstance = h;
231 // Make sure the C runtime functions use the same codepage as the GUI
232 char locale[12];
233 snprintf(locale, sizeof locale, ".%u", GetACP());
234 setlocale(LC_ALL, locale);
236 char **_argv;
237 int argc;
238 for (argc = 0, _argv = __argv; *_argv; _argv++)
239 ++argc;
240 _argv = __argv;
242 try {
243 bool help_option = false;
244 bool invalid_option = false;
245 char cwd[MAX_PATH];
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;
251 else if (HelpOption)
252 help_option = true;
254 if (!((std::string) Arch).size ())
256 #ifdef __x86_64__
257 is_64bit = true;
258 #else
259 is_64bit = false;
260 #endif
262 else if (((std::string) Arch).find ("64") != std::string::npos)
263 is_64bit = true;
264 else if (((std::string) Arch).find ("32") != std::string::npos
265 || ((std::string) Arch).find ("x86") != std::string::npos)
266 is_64bit = false;
267 else
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);
273 exit (1);
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);
281 if (rc > 0)
282 SetThreadUILanguage(langid);
285 if (PackageManagerOption)
286 unattended_mode = chooseronly;
287 else
288 if (UnattendedOption < 0)
289 unattended_mode = attended;
290 else
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)
308 set_cout ();
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])
314 ? "" : "\\";
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",
319 false);
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
328 text is ok. */
329 if (help_option)
331 if (invalid_option)
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);
339 goto finish_up;
342 if (VersionOption)
344 Log (LOG_PLAIN) << "Cygwin setup " << setup_version << endLog;
345 Logger ().exit (0, false);
346 goto finish_up;
349 /* Check if Cygwin works on this Windows architecture/version */
350 if (!UnsupportedOption)
353 #ifdef _X86_
354 (TRUE)
355 #else
356 (!is_64bit)
357 #endif
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()) {
395 std::string cygwin;
396 DWORD len = GetEnvironmentVariable ("CYGWIN", &cygwin[0], 0);
397 cygwin.resize(len);
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");
422 else
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");
434 exit(1);
437 else if (symlinkType == SymlinkTypeNative)
439 VersionInfo v = GetVer();
440 if (v.major() < 6)
442 fprintf (stderr, "*** --symlink-type native requires Windows 6.0 or later\n");
443 exit(1);
446 if (!(elevate || is_developer_mode() || nt_sec.hasSymlinkCreationRights()))
448 fprintf (stderr, "*** --symlink-type native requires SeCreateSymbolicLink privilege or 'Developer Mode'\n");
449 exit(1);
452 else if (symlinkType == SymlinkTypeShortcut)
454 fprintf (stderr, "*** --symlink-type lnk is not implemented\n");
455 exit(1);
458 if (elevate)
460 char exe_path[MAX_PATH];
461 if (!GetModuleFileName(NULL, exe_path, ARRAYSIZE(exe_path)))
462 goto finish_up;
464 SHELLEXECUTEINFO sei = { sizeof(sei) };
465 sei.lpVerb = "runas";
466 sei.lpFile = exe_path;
467 sei.nShow = SW_NORMAL;
468 if (WaitOption)
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))
481 DWORD exitcode = 0;
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);
492 else
494 UserSettings Settings;
495 UserSettings::instance().load (local_dir);
496 main_display ();
497 Settings.save (); // Clean exit.. save user options.
498 if (rebootneeded)
499 Logger ().setExitMsg (IDS_REBOOT_REQUIRED);
500 Logger ().exit (rebootneeded ? IDS_REBOOT_REQUIRED : 0);
502 finish_up:
505 TOPLEVEL_CATCH(NULL, "main");
507 // Never reached
508 return 0;