Add support for creating WSL symlinks
[cygwin-setup.git] / main.cc
blobb3854a8411bd7f01faac40f6793bfed2c27e1911
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 "msg.h"
40 #include "find.h"
41 #include "mount.h"
42 #include "LogFile.h"
43 #include "setup_version.h"
45 #include "proppage.h"
46 #include "propsheet.h"
48 // Page class headers
49 #include "splash.h"
50 #include "AntiVirus.h"
51 #include "source.h"
52 #include "root.h"
53 #include "localdir.h"
54 #include "net.h"
55 #include "site.h"
56 #include "choose.h"
57 #include "prereq.h"
58 #include "confirm.h"
59 #include "threebar.h"
60 #include "desktop.h"
61 #include "postinstallresults.h"
63 #include "getopt++/GetOption.h"
64 #include "getopt++/BoolOption.h"
65 #include "getopt++/StringOption.h"
67 #include "Exception.h"
68 #include <stdexcept>
70 #include "UserSettings.h"
71 #include "SourceSetting.h"
72 #include "ConnectionSetting.h"
73 #include "KeysSetting.h"
75 #include <wincon.h>
76 #include <fstream>
78 #ifdef __MINGW64_VERSION_MAJOR
79 extern char **_argv;
80 #endif
82 bool is_64bit;
83 bool is_new_install = false;
84 std::string SetupArch;
85 std::string SetupIniDir;
87 HINSTANCE hinstance;
89 static StringOption Arch ("", 'a', "arch", "Architecture to install (x86_64 or x86)", false);
90 static BoolOption UnattendedOption (false, 'q', "quiet-mode", "Unattended setup mode");
91 static BoolOption PackageManagerOption (false, 'M', "package-manager", "Semi-attended chooser-only mode");
92 static BoolOption NoAdminOption (false, 'B', "no-admin", "Do not check for and enforce running as Administrator");
93 static BoolOption WaitOption (false, 'W', "wait", "When elevating, wait for elevated child process");
94 static BoolOption HelpOption (false, 'h', "help", "Print help");
95 static BoolOption VersionOption (false, 'V', "version", "Show version");
96 static StringOption SetupBaseNameOpt ("setup", 'i', "ini-basename", "Use a different basename, e.g. \"foo\", instead of \"setup\"", false);
97 BoolOption UnsupportedOption (false, '\0', "allow-unsupported-windows", "Allow old, unsupported Windows versions");
98 std::string SetupBaseName;
100 static void inline
101 set_cout ()
103 HANDLE my_stdout = GetStdHandle (STD_OUTPUT_HANDLE);
104 if (my_stdout != INVALID_HANDLE_VALUE && GetFileType (my_stdout) != FILE_TYPE_UNKNOWN)
105 return;
107 if (AttachConsole ((DWORD) -1))
109 std::ofstream *conout = new std::ofstream ("conout$");
110 std::cout.rdbuf (conout->rdbuf ());
111 std::cout.flush ();
115 // Other threads talk to these pages, so we need to have it externable.
116 ThreeBarProgressPage Progress;
117 PostInstallResultsPage PostInstallResults;
119 static inline void
120 main_display ()
122 /* nondisplay classes */
123 LocalDirSetting localDir;
124 SourceSetting SourceSettings;
125 ConnectionSetting ConnectionSettings;
126 SiteSetting ChosenSites;
127 ExtraKeysSetting ExtraKeys;
129 SplashPage Splash;
130 AntiVirusPage AntiVirus;
131 SourcePage Source;
132 RootPage Root;
133 LocalDirPage LocalDir;
134 NetPage Net;
135 SitePage Site;
136 ChooserPage Chooser;
137 PrereqPage Prereq;
138 ConfirmPage Confirm;
139 DesktopSetupPage Desktop;
140 PropSheet MainWindow;
142 Log (LOG_TIMESTAMP) << "Current Directory: " << local_dir << endLog;
144 // Initialize common controls
145 INITCOMMONCONTROLSEX icce = { sizeof (INITCOMMONCONTROLSEX),
146 ICC_WIN95_CLASSES | ICC_LISTVIEW_CLASSES };
147 InitCommonControlsEx (&icce);
149 // Initialize COM and ShellLink instance here. For some reason
150 // Windows 7 fails to create the ShellLink instance if this is
151 // done later, in the thread which actually creates the shortcuts.
152 extern IShellLink *sl;
153 CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
154 HRESULT res = CoCreateInstance (CLSID_ShellLink, NULL,
155 CLSCTX_INPROC_SERVER, IID_IShellLink,
156 (LPVOID *) & sl);
157 if (res)
159 char buf[256];
160 sprintf (buf, "CoCreateInstance failed with error 0x%x.\n"
161 "Setup will not be able to create Cygwin Icons\n"
162 "in the Start Menu or on the Desktop.", (int) res);
163 mbox (NULL, buf, "Cygwin Setup", MB_OK);
166 // Init window class lib
167 Window::SetAppInstance (hinstance);
169 // Create pages
170 Splash.Create ();
171 AntiVirus.Create ();
172 Source.Create ();
173 Root.Create ();
174 LocalDir.Create ();
175 Net.Create ();
176 Site.Create ();
177 Chooser.Create ();
178 Prereq.Create ();
179 Confirm.Create ();
180 Progress.Create ();
181 PostInstallResults.Create ();
182 Desktop.Create ();
184 // Add pages to sheet
185 MainWindow.AddPage (&Splash);
186 MainWindow.AddPage (&AntiVirus);
187 MainWindow.AddPage (&Source);
188 MainWindow.AddPage (&Root);
189 MainWindow.AddPage (&LocalDir);
190 MainWindow.AddPage (&Net);
191 MainWindow.AddPage (&Site);
192 MainWindow.AddPage (&Chooser);
193 MainWindow.AddPage (&Prereq);
194 MainWindow.AddPage (&Confirm);
195 MainWindow.AddPage (&Progress);
196 MainWindow.AddPage (&PostInstallResults);
197 MainWindow.AddPage (&Desktop);
199 // Create the PropSheet main window
200 MainWindow.Create ();
202 // Uninitalize COM
203 if (sl)
204 sl->Release ();
205 CoUninitialize ();
208 int WINAPI
209 WinMain (HINSTANCE h,
210 HINSTANCE hPrevInstance, LPSTR command_line, int cmd_show)
213 hinstance = h;
215 // Make sure the C runtime functions use the same codepage as the GUI
216 char locale[12];
217 snprintf(locale, sizeof locale, ".%u", GetACP());
218 setlocale(LC_ALL, locale);
220 char **_argv;
221 int argc;
222 for (argc = 0, _argv = __argv; *_argv; _argv++)
223 ++argc;
224 _argv = __argv;
226 try {
227 bool help_option = false;
228 bool invalid_option = false;
229 char cwd[MAX_PATH];
230 GetCurrentDirectory (MAX_PATH, cwd);
231 local_dir = std::string (cwd);
233 if (!GetOption::GetInstance ().Process (argc,_argv, NULL))
234 help_option = invalid_option = true;
235 else if (HelpOption)
236 help_option = true;
238 if (!((std::string) Arch).size ())
240 #ifdef __x86_64__
241 is_64bit = true;
242 #else
243 is_64bit = false;
244 #endif
246 else if (((std::string) Arch).find ("64") != std::string::npos)
247 is_64bit = true;
248 else if (((std::string) Arch).find ("32") != std::string::npos
249 || ((std::string) Arch).find ("x86") != std::string::npos)
250 is_64bit = false;
251 else
253 char buff[80 + ((std::string) Arch).size ()];
254 sprintf (buff, "Invalid option for --arch: \"%s\"",
255 ((std::string) Arch).c_str ());
256 fprintf (stderr, "*** %s\n", buff);
257 mbox (NULL, buff, "Invalid option", MB_ICONEXCLAMATION | MB_OK);
258 exit (1);
261 unattended_mode = PackageManagerOption ? chooseronly
262 : (UnattendedOption ? unattended : attended);
264 bool output_only = help_option || VersionOption;
266 SetupBaseName = SetupBaseNameOpt;
267 SetupArch = is_64bit ? "x86_64" : "x86";
268 SetupIniDir = SetupArch+"/";
270 /* Initialize well known SIDs. We need the admin SID to test if we're
271 supposed to elevate. */
272 nt_sec.initialiseWellKnownSIDs ();
273 /* Check if we have to elevate. */
274 bool elevate = !output_only && OSMajorVersion () >= 6
275 && !NoAdminOption && !nt_sec.isRunAsAdmin ();
277 if (unattended_mode || output_only || !elevate)
278 set_cout ();
280 /* Start logging only if we don't elevate. Same for setting default
281 security settings. */
282 LogSingleton::SetInstance (*LogFile::createLogFile ());
283 const char *sep = isdirsep (local_dir[local_dir.size () - 1])
284 ? "" : "\\";
285 /* Don't create log files for help or version output only. */
286 if (!elevate && !output_only)
288 Logger ().setFile (LOG_BABBLE, local_dir + sep + "setup.log.full",
289 false);
290 Logger ().setFile (0, local_dir + sep + "setup.log", true);
291 Log (LOG_PLAIN) << "Starting cygwin install, version "
292 << setup_version << endLog;
295 if (help_option)
297 if (invalid_option)
298 Log (LOG_PLAIN) << "\nError during option processing.\n" << endLog;
299 Log (LOG_PLAIN) << "Cygwin setup " << setup_version << endLog;
300 Log (LOG_PLAIN) << "\nCommand Line Options:\n" << endLog;
301 GetOption::GetInstance ().ParameterUsage (Log (LOG_PLAIN));
302 Log (LOG_PLAIN) << endLog;
303 Log (LOG_PLAIN) << "The default is to both download and install packages, unless either --download or --local-install is specified." << endLog;
304 Logger ().exit (invalid_option ? 1 : 0, false);
305 goto finish_up;
308 if (VersionOption)
310 Log (LOG_PLAIN) << "Cygwin setup " << setup_version << endLog;
311 Logger ().exit (0, false);
312 goto finish_up;
315 /* Check if Cygwin works on this Windows version */
316 if (!UnsupportedOption && (OSMajorVersion () < 6))
318 mbox (NULL, "Cygwin is not supported on this Windows version",
319 "Cygwin Setup", MB_ICONEXCLAMATION | MB_OK);
320 Logger ().exit (1, false);
323 if (elevate)
325 char exe_path[MAX_PATH];
326 if (!GetModuleFileName(NULL, exe_path, ARRAYSIZE(exe_path)))
327 goto finish_up;
329 SHELLEXECUTEINFO sei = { sizeof(sei) };
330 sei.lpVerb = "runas";
331 sei.lpFile = exe_path;
332 sei.nShow = SW_NORMAL;
333 if (WaitOption)
334 sei.fMask |= SEE_MASK_NOCLOSEPROCESS;
336 // Avoid another isRunAsAdmin check in the child.
337 std::string command_line_cs (command_line);
338 command_line_cs += " -";
339 command_line_cs += NoAdminOption.shortOption();
340 sei.lpParameters = command_line_cs.c_str ();
342 if (ShellExecuteEx(&sei))
344 DWORD exitcode = 0;
345 /* Wait until child process is finished. */
346 if (WaitOption && sei.hProcess != NULL)
347 if (!WaitForSingleObject (sei.hProcess, INFINITE))
348 GetExitCodeProcess (sei.hProcess, &exitcode);
349 Logger ().setExitMsg (IDS_ELEVATED);
350 Logger ().exit (exitcode, false);
352 Log (LOG_PLAIN) << "Starting elevated child process failed" << endLog;
353 Logger ().exit (1, false);
355 else
357 /* Set default DACL and Group. */
358 nt_sec.setDefaultSecurity ((root_scope == IDC_ROOT_SYSTEM));
360 UserSettings Settings;
361 UserSettings::instance().load (local_dir);
362 main_display ();
363 Settings.save (); // Clean exit.. save user options.
364 if (rebootneeded)
365 Logger ().setExitMsg (IDS_REBOOT_REQUIRED);
366 Logger ().exit (rebootneeded ? IDS_REBOOT_REQUIRED : 0);
368 finish_up:
371 TOPLEVEL_CATCH(NULL, "main");
373 // Never reached
374 return 0;