]> cygwin.com Git - cygwin-apps/setup.git/blob - main.cc
2005-05-21 Brian Dessent <brian@dessent.net>
[cygwin-apps/setup.git] / main.cc
1 /*
2 * Copyright (c) 2000, Red Hat, Inc.
3 * Copyright (c) 2003, Robert Collins <rbtcollins@hotmail.com>
4 *
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.
9 *
10 * A copy of the GNU General Public License can be found at
11 * http://www.gnu.org/
12 *
13 * Written by DJ Delorie <dj@cygnus.com>
14 * Robert Collins <rbtcollins@hotmail.com>
15 *
16 *
17 */
18
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. */
28
29 #if 0
30 static const char *cvsid =
31 "\n%%% $Id$\n";
32 #endif
33
34 #include "win32.h"
35 #include <commctrl.h>
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include "resource.h"
40 #include "dialog.h"
41 #include "state.h"
42 #include "msg.h"
43 #include "find.h"
44 #include "mount.h"
45 #include "LogFile.h"
46 #include "setup_version.h"
47
48 #include "proppage.h"
49 #include "propsheet.h"
50
51 // Page class headers
52 #include "splash.h"
53 #include "AntiVirus.h"
54 #include "source.h"
55 #include "root.h"
56 #include "localdir.h"
57 #include "net.h"
58 #include "site.h"
59 #include "choose.h"
60 #include "prereq.h"
61 #include "threebar.h"
62 #include "desktop.h"
63
64 #include "getopt++/GetOption.h"
65 #include "getopt++/BoolOption.h"
66
67 #include "UserSettings.h"
68 #include "Exception.h"
69 #include <stdexcept>
70
71 using namespace std;
72
73 HINSTANCE hinstance;
74
75 static BoolOption UnattendedOption (false, 'q', "quiet-mode", "Unattended setup mode");
76 static BoolOption HelpOption (false, 'h', "help", "print help");
77
78 /* Maximum size of a SID on NT/W2K. */
79 #define MAX_SID_LEN 40
80
81 /* Computes the size of an ACL in relation to the number of ACEs it
82 should contain. */
83 #define TOKEN_ACL_SIZE(cnt) (sizeof(ACL) + \
84 (cnt) * (sizeof(ACCESS_ALLOWED_ACE) + MAX_SID_LEN))
85
86 namespace Setup {
87 class SIDWrapper {
88 public:
89 SIDWrapper();
90 /* Prevent synthetics. If assignment is needed, this should be refcounting */
91 SIDWrapper(SIDWrapper const &);
92 SIDWrapper&operator=(SIDWrapper const&);
93 ~SIDWrapper();
94 /* We could look at doing weird typcast overloads here,
95 but manual access is easier for now
96 */
97 PSID &theSID();
98 PSID const &theSID() const;
99 private:
100 PSID value;
101 };
102
103 SIDWrapper::SIDWrapper() : value(NULL) {}
104 SIDWrapper::~SIDWrapper()
105 {
106 if (value)
107 FreeSid (value);
108 }
109
110 PSID &
111 SIDWrapper::theSID()
112 {
113 return value;
114 }
115
116 PSID const &
117 SIDWrapper::theSID() const
118 {
119 return value;
120 }
121
122 class HANDLEWrapper {
123 public:
124 HANDLEWrapper();
125 /* Prevent synthetics. If assignment is needed, we should duphandles, or refcount */
126 HANDLEWrapper(HANDLEWrapper const &);
127 HANDLEWrapper&operator=(HANDLEWrapper const &);
128 ~HANDLEWrapper();
129 HANDLE &theHANDLE();
130 HANDLE const &theHANDLE() const;
131 private:
132 HANDLE value;
133 };
134
135 HANDLEWrapper::HANDLEWrapper() : value (NULL) {}
136 HANDLEWrapper::~HANDLEWrapper()
137 {
138 if (value)
139 CloseHandle(value);
140 }
141
142 HANDLE &
143 HANDLEWrapper::theHANDLE()
144 {
145 return value;
146 }
147
148 HANDLE const &
149 HANDLEWrapper::theHANDLE() const
150 {
151 return value;
152 }
153
154 };
155
156 class TokenGroupCollection {
157 public:
158 TokenGroupCollection(DWORD, Setup::HANDLEWrapper &);
159 ~TokenGroupCollection();
160 /* prevent synthetics */
161 TokenGroupCollection &operator=(TokenGroupCollection const &);
162 TokenGroupCollection (TokenGroupCollection const &);
163 bool find (Setup::SIDWrapper const &) const;
164 bool populated() const { return populated_;}
165 void populate();
166 private:
167 mutable bool populated_;
168 char *buffer;
169 DWORD bufferSize;
170 Setup::HANDLEWrapper &token;
171 };
172
173 TokenGroupCollection::TokenGroupCollection(DWORD aSize, Setup::HANDLEWrapper &aHandle) : populated_(false), buffer(new char[aSize]), bufferSize(aSize), token(aHandle)
174 {
175 }
176
177 TokenGroupCollection::~TokenGroupCollection()
178 {
179 if (buffer)
180 delete[] buffer;
181 }
182
183 void
184 TokenGroupCollection::populate()
185 {
186 if (!GetTokenInformation (token.theHANDLE(), TokenGroups, buffer, bufferSize, &bufferSize))
187 {
188 log (LOG_TIMESTAMP) << "GetTokenInformation() failed: " <<
189 GetLastError () << endLog;
190 return;
191 }
192 populated_ = true;
193 }
194
195 bool
196 TokenGroupCollection::find (Setup::SIDWrapper const &aSID) const
197 {
198 if (!populated())
199 return false;
200 TOKEN_GROUPS *groups = (TOKEN_GROUPS *) buffer;
201 for (DWORD pg = 0; pg < groups->GroupCount; ++pg)
202 if (EqualSid(groups->Groups[pg].Sid, aSID.theSID()))
203 return true;
204 return false;
205 }
206
207 class NTSecurity
208 {
209 public:
210 static void NoteFailedAPI(String const &);
211 NTSecurity();
212 ~NTSecurity();
213 /* prevent synthetics */
214 NTSecurity &operator=(NTSecurity const&);
215 NTSecurity(NTSecurity const &);
216
217 void setDefaultSecurity();
218 private:
219 void failed(bool const &);
220 bool const &failed() const;
221 void initialiseEveryOneSID();
222 void setDefaultDACL ();
223 Setup::SIDWrapper everyOneSID, administratorsSID, usid;
224 Setup::HANDLEWrapper token;
225 bool failed_;
226 struct {
227 PSID psid;
228 char buf[MAX_SID_LEN];
229 } osid;
230 DWORD size;
231 };
232
233 void
234 set_default_sec()
235 {
236 NTSecurity worker;
237 worker.setDefaultSecurity();
238 }
239
240 void
241 NTSecurity::NoteFailedAPI(String const &api)
242 {
243 log (LOG_TIMESTAMP) << api << "() failed: " << GetLastError () << endLog;
244 }
245
246 NTSecurity::NTSecurity() : everyOneSID (), administratorsSID(), usid(), token(), failed_(false)
247 {}
248
249 NTSecurity::~NTSecurity()
250 {
251 }
252
253 void
254 NTSecurity::failed(bool const &aBool)
255 {
256 failed_ = aBool;
257 }
258
259 bool const &
260 NTSecurity::failed() const
261 {
262 return failed_;
263 }
264
265 void
266 NTSecurity::initialiseEveryOneSID()
267 {
268 SID_IDENTIFIER_AUTHORITY sid_auth = { SECURITY_WORLD_SID_AUTHORITY };
269 if (!AllocateAndInitializeSid (&sid_auth, 1, 0, 0, 0, 0, 0, 0, 0, 0, &everyOneSID.theSID()))
270 {
271 NoteFailedAPI ("AllocateAndInitializeSid");
272 failed(true);
273 }
274 }
275
276 void
277 NTSecurity::setDefaultDACL ()
278 {
279 /* To assure that the created files have a useful ACL, the
280 default DACL in the process token is set to full access to
281 everyone. This applies to files and subdirectories created
282 in directories which don't propagate permissions to child
283 objects.
284 To assure that the files group is meaningful, a token primary
285 group of None is changed to Users or Administrators. */
286
287 initialiseEveryOneSID();
288 if (failed())
289 return;
290
291 /* Create a buffer which has enough room to contain the TOKEN_DEFAULT_DACL
292 structure plus an ACL with one ACE. */
293 size_t bufferSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)
294 + GetLengthSid(everyOneSID.theSID()) - sizeof(DWORD);
295
296 std::auto_ptr<char> buf (new char[bufferSize]);
297
298 /* First initialize the TOKEN_DEFAULT_DACL structure. */
299 PACL dacl = (PACL)buf.get();
300
301 /* Initialize the ACL for containing one ACE. */
302 if (!InitializeAcl (dacl, bufferSize, ACL_REVISION))
303 {
304 NoteFailedAPI ("InitializeAcl");
305 failed(true);
306 return;
307 }
308
309 /* Create the ACE which grants full access to "Everyone" and store it
310 in dacl. */
311 if (!AddAccessAllowedAce
312 (dacl, ACL_REVISION, GENERIC_ALL, everyOneSID.theSID()))
313 {
314 NoteFailedAPI ("AddAccessAllowedAce");
315 failed(true);
316 return;
317 }
318
319 /* Get the processes access token. */
320 if (!OpenProcessToken (GetCurrentProcess (),
321 TOKEN_READ | TOKEN_ADJUST_DEFAULT, &token.theHANDLE()))
322 {
323 NoteFailedAPI ("OpenProcessToken");
324 failed(true);
325 return;
326 }
327
328 /* Set the default DACL to the above computed ACL. */
329 if (!SetTokenInformation (token.theHANDLE(), TokenDefaultDacl, &dacl, bufferSize))
330 {
331 NoteFailedAPI ("SetTokenInformation");
332 failed(true);
333 }
334 }
335
336 void
337 NTSecurity::setDefaultSecurity ()
338 {
339
340 setDefaultDACL();
341 if (failed())
342 return;
343
344 /* Get the user */
345 if (!GetTokenInformation (token.theHANDLE(), TokenUser, &osid,
346 sizeof osid, &size))
347 {
348 NoteFailedAPI("GetTokenInformation");
349 return;
350 }
351 /* Make it the owner */
352 if (!SetTokenInformation (token.theHANDLE(), TokenOwner, &osid,
353 sizeof osid))
354 {
355 NoteFailedAPI("SetTokenInformation");
356 return;
357 }
358
359 SID_IDENTIFIER_AUTHORITY sid_auth;
360 sid_auth = (SID_IDENTIFIER_AUTHORITY) { SECURITY_NT_AUTHORITY };
361 /* Get the SID for "Administrators" S-1-5-32-544 */
362 if (!AllocateAndInitializeSid (&sid_auth, 2, SECURITY_BUILTIN_DOMAIN_RID,
363 DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &administratorsSID.theSID()))
364 {
365 NoteFailedAPI("AllocateAndInitializeSid");
366 return;
367 }
368 /* Get the SID for "Users" S-1-5-32-545 */
369 if (!AllocateAndInitializeSid (&sid_auth, 2, SECURITY_BUILTIN_DOMAIN_RID,
370 DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &usid.theSID()))
371 {
372 NoteFailedAPI("AllocateAndInitializeSid");
373 return;
374 }
375 /* Get the token groups */
376 if (!GetTokenInformation (token.theHANDLE(), TokenGroups, NULL, 0, &size)
377 && GetLastError () != ERROR_INSUFFICIENT_BUFFER)
378 {
379 NoteFailedAPI("GetTokenInformation");
380 return;
381 }
382 TokenGroupCollection ntGroups(size, token);
383 ntGroups.populate();
384 if (!ntGroups.populated())
385 return;
386 /* Set the default group to one of the above computed SID. */
387 PSID nsid = NULL;
388 if (ntGroups.find (usid))
389 {
390 nsid = usid.theSID();
391 log(LOG_TIMESTAMP) << "Changing gid to Users" << endLog;
392 }
393 else if (ntGroups.find (administratorsSID))
394 {
395 nsid = administratorsSID.theSID();
396 log(LOG_TIMESTAMP) << "Changing gid to Administrators" << endLog;
397 }
398 if (nsid && !SetTokenInformation (token.theHANDLE(), TokenPrimaryGroup, &nsid, sizeof nsid))
399 NoteFailedAPI ("SetTokenInformation");
400 }
401
402 // Other threads talk to this page, so we need to have it externable.
403 ThreeBarProgressPage Progress;
404
405 // This is a little ugly, but the decision about where to log occurs
406 // after the source is set AND the root mount obtained
407 // so we make the actual logger available to the appropriate routine(s).
408 LogFile *theLog;
409
410 #ifndef __CYGWIN__
411 int WINAPI
412 WinMain (HINSTANCE h,
413 HINSTANCE hPrevInstance, LPSTR command_line, int cmd_show)
414 {
415
416 hinstance = h;
417 #else
418 int
419 main (int argc, char **argv)
420 {
421 hinstance = GetModuleHandle (NULL);
422 #endif
423
424 try {
425 char *cwd=new char[MAX_PATH];
426 GetCurrentDirectory (MAX_PATH, cwd);
427 local_dir = String (cwd);
428 delete cwd;
429
430 // TODO: make an equivalent for __argv under cygwin.
431 char **_argv;
432 #ifndef __CYGWIN__
433 int argc;
434 for (argc = 0, _argv = __argv; *_argv; _argv++)++argc;
435 _argv = __argv;
436 #else
437 _argv = argv;
438 #endif
439
440 if (!GetOption::GetInstance().Process (argc,_argv, NULL))
441 exit(1);
442
443 LogSingleton::SetInstance (*(theLog = LogFile::createLogFile()));
444 theLog->setFile (LOG_BABBLE, local_dir + "/setup.log.full", false);
445 theLog->setFile (0, local_dir + "/setup.log", true);
446
447 log (LOG_PLAIN) << "Starting cygwin install, version " << setup_version << endLog;
448
449 // Ensure files created by postinstall and preremove scripts
450 // get sane permissions.
451 if (putenv ("CYGWIN=nontsec") != 0)
452 log (LOG_PLAIN) << "Failed to set CYGWIN=nontsec (errno " << errno
453 << ": " << strerror(errno) << ")" << endLog;
454
455 UserSettings::Instance().loadAllSettings();
456
457 SplashPage Splash;
458 AntiVirusPage AntiVirus;
459 SourcePage Source;
460 RootPage Root;
461 LocalDirPage LocalDir;
462 NetPage Net;
463 SitePage Site;
464 ChooserPage Chooser;
465 PrereqPage Prereq;
466 DesktopSetupPage Desktop;
467 PropSheet MainWindow;
468
469 log (LOG_TIMESTAMP) << "Current Directory: " << local_dir << endLog;
470
471 if (HelpOption)
472 {
473 GetOption::GetInstance().ParameterUsage(log(LOG_PLAIN)<<"\nCommand Line Options:\n");
474 theLog->exit(0);
475 }
476
477 unattended_mode = UnattendedOption;
478
479 /* Set the default DACL and Group only on NT/W2K. 9x/ME has
480 no idea of access control lists and security at all. */
481 if (IsWindowsNT())
482 set_default_sec ();
483
484 // Initialize common controls
485 InitCommonControls ();
486
487 // Init window class lib
488 Window::SetAppInstance (hinstance);
489
490 // Create pages
491 Splash.Create ();
492 AntiVirus.Create ();
493 Source.Create ();
494 Root.Create ();
495 LocalDir.Create ();
496 Net.Create ();
497 Site.Create ();
498 Chooser.Create ();
499 Prereq.Create ();
500 Progress.Create ();
501 Desktop.Create ();
502
503 // Add pages to sheet
504 MainWindow.AddPage (&Splash);
505 MainWindow.AddPage (&AntiVirus);
506 MainWindow.AddPage (&Source);
507 MainWindow.AddPage (&Root);
508 MainWindow.AddPage (&LocalDir);
509 MainWindow.AddPage (&Net);
510 MainWindow.AddPage (&Site);
511 MainWindow.AddPage (&Chooser);
512 MainWindow.AddPage (&Prereq);
513 MainWindow.AddPage (&Progress);
514 MainWindow.AddPage (&Desktop);
515
516 // Create the PropSheet main window
517 MainWindow.Create ();
518
519 // Clean exit.. save user options.
520 UserSettings::Instance().saveAllSettings();
521
522 theLog->exit (0);
523 }
524 TOPLEVEL_CATCH("main");
525
526 // Never reached
527 return 0;
528 }
This page took 0.05654 seconds and 5 git commands to generate.