]> cygwin.com Git - cygwin-apps/setup.git/blob - install.cc
copyright
[cygwin-apps/setup.git] / install.cc
1 /*
2 * Copyright (c) 2000, Red Hat, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
11 *
12 * Written by DJ Delorie <dj@cygnus.com>
13 *
14 */
15
16 /* The purpose of this file is to install all the packages selected in
17 the install list (in ini.h). Note that we use a separate thread to
18 maintain the progress dialog, so we avoid the complexity of
19 handling two tasks in one thread. We also create or update all the
20 files in /etc/setup/\* and create the mount points. */
21
22 #if 0
23 static const char *cvsid = "\n%%% $Id$\n";
24 #endif
25
26 #include "win32.h"
27 #include "commctrl.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <errno.h>
35 #include <process.h>
36
37 #include "zlib/zlib.h"
38
39 #include "resource.h"
40 #include "dialog.h"
41 #include "geturl.h"
42 #include "state.h"
43 #include "diskfull.h"
44 #include "msg.h"
45 #include "mount.h"
46 #include "log.h"
47 #include "mount.h"
48 #include "filemanip.h"
49 #include "io_stream.h"
50 #include "compress.h"
51 #include "compress_gz.h"
52 #include "archive.h"
53 #include "archive_tar.h"
54 #include "script.h"
55
56 #include "package_db.h"
57 #include "package_meta.h"
58 #include "package_version.h"
59 #include "package_source.h"
60
61 #include "port.h"
62
63 #include "threebar.h"
64
65 #include "md5.h"
66
67 #include "Exception.h"
68
69 extern ThreeBarProgressPage Progress;
70
71 static int total_bytes = 0;
72 static int total_bytes_sofar = 0;
73 static int package_bytes = 0;
74
75 static void
76 init_dialog ()
77 {
78 Progress.SetText2 ("");
79 Progress.SetText3 ("");
80 }
81
82 static void
83 progress (int bytes)
84 {
85 if (package_bytes > 0)
86 {
87 Progress.SetBar1 (bytes, package_bytes);
88 }
89
90 if (total_bytes > 0)
91 {
92 Progress.SetBar2 (total_bytes_sofar + bytes, total_bytes);
93 }
94 }
95
96 static const char *standard_dirs[] = {
97 "/bin",
98 "/etc",
99 "/lib",
100 "/tmp",
101 "/usr",
102 "/usr/bin",
103 "/usr/lib",
104 "/usr/src",
105 "/usr/local",
106 "/usr/local/bin",
107 "/usr/local/etc",
108 "/usr/local/lib",
109 "/usr/tmp",
110 "/var/run",
111 "/var/tmp",
112 0
113 };
114
115 static int num_installs, num_replacements, num_uninstalls;
116 static void uninstall_one (packagemeta &);
117 static int replace_one (packagemeta &);
118 static int install_one_source (packagemeta &, packagesource &, String const &,
119 String const &, package_type_t);
120 static bool rebootneeded;
121
122 /* FIXME: upgrades should be a method too */
123 static void
124 uninstall_one (packagemeta & pkgm)
125 {
126 Progress.SetText1 ("Uninstalling...");
127 Progress.SetText2 (pkgm.name.cstr_oneuse());
128 log (LOG_PLAIN, String("Uninstalling ") + pkgm.name);
129 pkgm.uninstall ();
130 num_uninstalls++;
131 }
132
133 /* uninstall and install a package, preserving configuration
134 * files and the like.
135 * This method should also know about replacing in-use file.
136 * ASSUMPTIONS: pkgm is installed.
137 * pkgm has a desired package.
138 */
139 static int
140 replace_one (packagemeta & pkg)
141 {
142 int errors = 0;
143 Progress.SetText1 ("Replacing...");
144 Progress.SetText2 (pkg.name.cstr_oneuse());
145 log (LOG_PLAIN, String( "Replacing ") + pkg.name);
146 pkg.uninstall ();
147
148 errors +=
149 install_one_source (pkg, pkg.desired->bin, "cygfile://","/", package_binary);
150 if (!errors)
151 pkg.installed = pkg.desired;
152 num_replacements++;
153 return errors;
154 }
155
156
157 /* install one source at a given prefix. */
158 static int
159 install_one_source (packagemeta & pkgm, packagesource & source,
160 String const &prefixURL, String const &prefixPath, package_type_t type)
161 {
162 int errors = 0;
163 Progress.SetText2 (source.Base ());
164 if (!source.Cached () || !io_stream::exists (source.Cached ()))
165 {
166 note (NULL, IDS_ERR_OPEN_READ, source.Cached (), "No such file");
167 return 1;
168 }
169 io_stream *lst = 0;
170 if (type == package_binary)
171 {
172 io_stream *tmp =
173 io_stream::
174 open (String ("cygfile:///etc/setup/") + pkgm.name + ".lst.gz",
175 "wb");
176 lst = new compress_gz (tmp, "w9");
177 if (lst->error ())
178 {
179 delete lst;
180 lst = NULL;
181 }
182 }
183
184 package_bytes = source.size;
185
186 char msg[64];
187 strcpy (msg, "Installing");
188 Progress.SetText1 (msg);
189 log (LOG_PLAIN, String (msg) + " " + source.Cached ());
190
191 if (source.md5.isSet())
192 {
193 // check the MD5 sum of the cached file here
194 io_stream *thefile = io_stream::open (source.Cached (), "rb");
195 if (!thefile)
196 return 0;
197 md5_state_t pns;
198 md5_init (&pns);
199
200 unsigned char buffer[16384];
201 ssize_t count;
202 while ((count = thefile->read (buffer, 16384)) > 0)
203 md5_append (&pns, buffer, count);
204 delete thefile;
205 if (count < 0)
206 throw new Exception ("__LINE__ __FILE__", (String ("IO Error reading ") + source.Cached()).cstr_oneuse(), APPERR_IO_ERROR);
207
208 md5_byte_t tempdigest[16];
209 md5_finish(&pns, tempdigest);
210 md5 tempMD5;
211 tempMD5.set (tempdigest);
212
213 log (LOG_BABBLE, String ("For file") + source.Cached() + " ini digest is" + source.md5.print() + " file digest is " + tempMD5.print());
214
215 if (source.md5 != tempMD5)
216 throw new Exception ("__LINE__ __FILE__", (String ("Checksum failure for ") + source.Cached()).cstr_oneuse(), APPERR_CORRUPT_PACKAGE);
217 }
218 io_stream *tmp = io_stream::open (source.Cached (), "rb");
219 archive *thefile = 0;
220 if (tmp)
221 {
222 io_stream *tmp2 = compress::decompress (tmp);
223 if (tmp2)
224 thefile = archive::extract (tmp2);
225 else
226 thefile = archive::extract (tmp);
227 }
228 /* FIXME: potential leak of either *tmp or *tmp2 */
229 if (thefile)
230 {
231 String fn;
232 while ((fn = thefile->next_file_name ()).size())
233 {
234 if (lst)
235 {
236 String tmp=fn + "\n";
237 lst->write (tmp.cstr_oneuse(), tmp.size());
238 }
239
240 String canonicalfn = prefixPath + fn;
241
242 Progress.SetText3 (canonicalfn.cstr_oneuse());
243 log (LOG_BABBLE, String("Installing file ") + prefixURL + prefixPath + fn);
244 if (archive::extract_file (thefile, prefixURL, prefixPath) != 0)
245 {
246 //extract to temp location
247 if (archive::extract_file (thefile, prefixURL, prefixPath, ".new") != 0)
248 {
249 log (LOG_PLAIN,
250 String("Unable to install file ") +
251 prefixURL + prefixPath + fn);
252 errors++;
253 }
254 else
255 //switch Win32::OS
256 {
257 switch (Win32::OS ())
258 {
259 case Win32::Win9x:{
260 /* Get the short file names */
261 char source[MAX_PATH];
262 unsigned int len =
263 GetShortPathName (cygpath (String ("/") + fn +
264 ".new").cstr_oneuse(),
265 source, MAX_PATH);
266 if (!len || len > MAX_PATH)
267 {
268 log (LOG_TIMESTAMP,
269 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
270 cygpath (String ("/") + fn).cstr_oneuse(),
271 cygpath (String ("/") + fn +
272 ".new").cstr_oneuse(),
273 GetLastError ());
274 ++errors;
275 }
276 else
277 {
278 char dest[MAX_PATH];
279 len =
280 GetShortPathName (cygpath (String ("/") +
281 fn).cstr_oneuse(),
282 dest, MAX_PATH);
283 if (!len || len > MAX_PATH)
284 {
285 log (LOG_TIMESTAMP,
286 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
287 cygpath (String ("/") + fn).cstr_oneuse(),
288 cygpath (String ("/") + fn +
289 ".new").cstr_oneuse(),
290 GetLastError ());
291 ++errors;
292
293 }
294 else
295 /* trigger a replacement on reboot */
296 if (!WritePrivateProfileString
297 ("rename", dest, source, "WININIT.INI"))
298 {
299 log (LOG_TIMESTAMP,
300 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
301 cygpath (String ("/") + fn).cstr_oneuse(),
302 cygpath (String ("/") + fn +
303 ".new").cstr_oneuse(),
304 GetLastError ());
305 ++errors;
306 }
307
308 }
309 }
310 break;
311 case Win32::WinNT:
312 /* XXX FIXME: prefix may not be / for in use files -
313 * although it most likely is
314 * - we need a io method to get win32 paths
315 * or to wrap this system call
316 */
317 if (!MoveFileEx (cygpath (String ("/") + fn +
318 ".new").cstr_oneuse(),
319 cygpath (String ("/") + fn).cstr_oneuse(),
320 MOVEFILE_DELAY_UNTIL_REBOOT |
321 MOVEFILE_REPLACE_EXISTING))
322 {
323 log (LOG_TIMESTAMP,
324 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
325 cygpath (String ("/") + fn).cstr_oneuse(),
326 cygpath (String ("/") + fn + ".new").cstr_oneuse(),
327 GetLastError ());
328 ++errors;
329 }
330 else
331 rebootneeded = true;
332 break;
333 }
334 }
335 }
336
337 progress (tmp->tell ());
338 num_installs++;
339 }
340 delete thefile;
341
342 total_bytes_sofar += package_bytes;
343 }
344
345
346 progress (0);
347
348 int df = diskfull (get_root_dir ().cstr_oneuse());
349 Progress.SetBar3 (df);
350
351 if (lst)
352 delete lst;
353
354 return errors;
355 }
356
357 /* install a package, install both the binary and source aspects if needed */
358 static int
359 install_one (packagemeta & pkg)
360 {
361 int errors = 0;
362
363 if (pkg.installed != pkg.desired && pkg.desired->binpicked)
364 {
365 errors +=
366 install_one_source (pkg, pkg.desired->bin, "cygfile://","/",
367 package_binary);
368 if (!errors)
369 pkg.installed = pkg.desired;
370 }
371 if (pkg.desired->srcpicked)
372 errors +=
373 install_one_source (pkg, pkg.desired->src, "cygfile://","/usr/src/",
374 package_source);
375
376 /* FIXME: make a upgrade method and reinstate this */
377 #if 0
378 String msg;
379 if (!pkg->installed)
380 msg = "Installing";
381 else
382 {
383 int n = strcmp (pi->version, pkg->installed->version);
384 if (n < 0)
385 msg = "Reverting";
386 else if (n == 0)
387 msg = "Reinstalling";
388 else
389 msg = "Upgrading";
390 }
391
392 switch (pkg->action)
393 {
394 case ACTION_PREV:
395 msg += " previous version...";
396 break;
397 case ACTION_CURR:
398 msg += "...";
399 break;
400 case ACTION_TEST:
401 msg += " test version...";
402 break;
403 default:
404 /* FIXME: log this somehow */
405 break;
406 }
407 SetWindowText (ins_action, msg.cstr_oneuse());
408 log (LOG_PLAIN, msg + " " + file);
409 #endif
410
411 return errors;
412 }
413
414 static void
415 check_for_old_cygwin ()
416 {
417 char buf[_MAX_PATH + sizeof ("\\cygwin1.dll")];
418 if (!GetSystemDirectory (buf, sizeof (buf)))
419 return;
420 strcat (buf, "\\cygwin1.dll");
421 if (_access (buf, 0) != 0)
422 return;
423
424 char msg[sizeof (buf) + 132];
425 sprintf (msg,
426 "An old version of cygwin1.dll was found here:\r\n%s\r\nDelete?",
427 buf);
428 switch (MessageBox
429 (NULL, msg, "What's that doing there?",
430 MB_YESNO | MB_ICONQUESTION | MB_TASKMODAL))
431 {
432 case IDYES:
433 if (!DeleteFile (buf))
434 {
435 sprintf (msg, "Couldn't delete file %s.\r\n"
436 "Is the DLL in use by another application?\r\n"
437 "You should delete the old version of cygwin1.dll\r\n"
438 "at your earliest convenience.", buf);
439 MessageBox (NULL, buf, "Couldn't delete file",
440 MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
441 }
442 break;
443 default:
444 break;
445 }
446
447 return;
448 }
449
450 static void
451 do_install_thread (HINSTANCE h, HWND owner)
452 {
453 int i;
454 int errors = 0;
455
456 num_installs = 0, num_uninstalls = 0, num_replacements = 0;
457 rebootneeded = false;
458
459 next_dialog = IDD_DESKTOP;
460
461 io_stream::mkpath_p (PATH_TO_DIR, String ("file://") + get_root_dir ());
462
463 for (i = 0; standard_dirs[i]; i++)
464 {
465 String p = cygpath (standard_dirs[i]);
466 if (p.size())
467 io_stream::mkpath_p (PATH_TO_DIR, String ("file://") + p);
468 }
469
470 /* Create /var/run/utmp */
471 io_stream *utmp = io_stream::open ("cygfile:///var/run/utmp", "wb");
472 delete utmp;
473
474 init_dialog ();
475
476 total_bytes = 0;
477 total_bytes_sofar = 0;
478
479 int df = diskfull (get_root_dir ().cstr_oneuse());
480 Progress.SetBar3 (df);
481
482 int istext = (root_text == IDC_ROOT_TEXT) ? 1 : 0;
483 int issystem = (root_scope == IDC_ROOT_SYSTEM) ? 1 : 0;
484
485 create_mount ("/", get_root_dir (), istext, issystem);
486 create_mount ("/usr/bin", cygpath ("/bin"), istext, issystem);
487 create_mount ("/usr/lib", cygpath ("/lib"), istext, issystem);
488 set_cygdrive_flags (istext, issystem);
489
490 /* Let's hope people won't uninstall packages before installing [b]ash */
491 init_run_script ();
492
493 packagedb db;
494 for (size_t n = 1; n <= db.packages.number (); n++)
495 {
496 packagemeta & pkg = *db.packages[n];
497
498 if (pkg.desired && (pkg.desired->srcpicked || pkg.desired->binpicked))
499 {
500 if (pkg.desired->srcpicked)
501 total_bytes += pkg.desired->src.size;
502 if (pkg.desired->binpicked)
503 total_bytes += pkg.desired->bin.size;
504 }
505 }
506
507 /* start with uninstalls - remove files that new packages may replace */
508 for (size_t n = 1; n <= db.packages.number (); n++)
509 {
510 packagemeta & pkg = *db.packages[n];
511 if (pkg.installed && (!pkg.desired || pkg.desired != pkg.installed))
512 uninstall_one (pkg);
513 }
514
515 /* now in-place binary upgrades/reinstalls, as these may remove fils
516 * that have been moved into new packages
517 */
518
519 for (size_t n = 1; n <= db.packages.number (); n++)
520 {
521 packagemeta & pkg = *db.packages[n];
522 if (pkg.installed && pkg.desired && pkg.desired->binpicked)
523 {
524 try {
525 int e = 0;
526 e += replace_one (pkg);
527 if (e)
528 errors++;
529 }
530 catch (exception *e)
531 {
532 if (yesno (owner, IDS_INSTALL_ERROR, e->what()) != IDYES)
533 {
534 log (LOG_TIMESTAMP, String ("User cancelled setup after install error"));
535 LogSingleton::GetInstance().exit (1);
536 return;
537 }
538 }
539 }
540 }
541
542 for (size_t n = 1; n <= db.packages.number (); n++)
543 {
544 packagemeta & pkg = *db.packages[n];
545
546 if (pkg.desired && (pkg.desired->srcpicked || pkg.desired->binpicked))
547 {
548 try
549 {
550 int e = 0;
551 e += install_one (pkg);
552 if (e)
553 errors++;
554 }
555 catch (exception *e)
556 {
557 if (yesno (owner, IDS_INSTALL_ERROR, e->what()) != IDYES)
558 {
559 log (LOG_TIMESTAMP, String ("User cancelled setup after install error"));
560 LogSingleton::GetInstance().exit (1);
561 return;
562 }
563 }
564 }
565 } // end of big package loop
566
567 if (rebootneeded)
568 note (owner, IDS_REBOOT_REQUIRED);
569
570 int temperr;
571 if ((temperr = db.flush ()))
572 {
573 const char *err = strerror (temperr);
574 if (!err)
575 err = "(unknown error)";
576 fatal (owner, IDS_ERR_OPEN_WRITE, "Package Database",
577 err);
578 }
579
580 if (!errors)
581 check_for_old_cygwin ();
582 if (num_installs == 0 && num_uninstalls == 0)
583 {
584 exit_msg = IDS_NOTHING_INSTALLED;
585 return;
586 }
587 if (num_installs == 0)
588 {
589 exit_msg = IDS_UNINSTALL_COMPLETE;
590 return;
591 }
592
593 if (errors)
594 exit_msg = IDS_INSTALL_INCOMPLETE;
595 else
596 exit_msg = IDS_INSTALL_COMPLETE;
597 }
598
599 static DWORD WINAPI
600 do_install_reflector (void *p)
601 {
602 HANDLE *context;
603 context = (HANDLE *) p;
604
605 do_install_thread ((HINSTANCE) context[0], (HWND) context[1]);
606
607 // Tell the progress page that we're done downloading
608 Progress.PostMessage (WM_APP_INSTALL_THREAD_COMPLETE);
609
610 ExitThread (0);
611 }
612
613 static HANDLE context[2];
614
615 void
616 do_install (HINSTANCE h, HWND owner)
617 {
618 context[0] = h;
619 context[1] = owner;
620
621 DWORD threadID;
622 CreateThread (NULL, 0, do_install_reflector, context, 0, &threadID);
623 }
This page took 0.066077 seconds and 5 git commands to generate.