]> cygwin.com Git - cygwin-apps/setup.git/blame - install.cc
2002-05-11 Robert Collins <rbtcollins@hotmail.com>
[cygwin-apps/setup.git] / install.cc
CommitLineData
23c9e63c
DD
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
8e9aa511 16/* The purpose of this file is to install all the packages selected in
23c9e63c
DD
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
b24c88b3 20 files in /etc/setup/\* and create the mount points. */
23c9e63c 21
b24c88b3 22#if 0
ad3c7385 23static const char *cvsid = "\n%%% $Id$\n";
b24c88b3 24#endif
8507f105 25
23c9e63c
DD
26#include "win32.h"
27#include "commctrl.h"
28
29#include <stdio.h>
904d24fe 30#include <stdlib.h>
4a83b7b0 31#include <unistd.h>
23c9e63c
DD
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <errno.h>
ab57ceaa
RC
35#include <process.h>
36
87c42361 37#include "zlib/zlib.h"
23c9e63c
DD
38
39#include "resource.h"
23c9e63c 40#include "dialog.h"
23c9e63c 41#include "geturl.h"
23c9e63c 42#include "state.h"
23c9e63c
DD
43#include "diskfull.h"
44#include "msg.h"
45#include "mount.h"
89b1a15b 46#include "log.h"
a351e48c 47#include "mount.h"
c46a33a9 48#include "filemanip.h"
b24c88b3
RC
49#include "io_stream.h"
50#include "compress.h"
51#include "compress_gz.h"
52#include "archive.h"
53#include "archive_tar.h"
8e9aa511 54#include "script.h"
4a83b7b0 55
fa0c0d10
RC
56#include "package_db.h"
57#include "package_meta.h"
bb849dbd
RC
58#include "package_version.h"
59#include "package_source.h"
fa0c0d10 60
4a83b7b0 61#include "port.h"
23c9e63c 62
ab57ceaa 63#include "threebar.h"
58ee6135
RC
64
65#include "md5.h"
66
67#include "Exception.h"
68
ab57ceaa 69extern ThreeBarProgressPage Progress;
23c9e63c
DD
70
71static int total_bytes = 0;
72static int total_bytes_sofar = 0;
73static int package_bytes = 0;
74
23c9e63c
DD
75static void
76init_dialog ()
77{
ab57ceaa
RC
78 Progress.SetText2 ("");
79 Progress.SetText3 ("");
23c9e63c
DD
80}
81
82static void
83progress (int bytes)
84{
ab57ceaa 85 if (package_bytes > 0)
23c9e63c 86 {
ab57ceaa 87 Progress.SetBar1 (bytes, package_bytes);
23c9e63c
DD
88 }
89
ab57ceaa 90 if (total_bytes > 0)
23c9e63c 91 {
ab57ceaa 92 Progress.SetBar2 (total_bytes_sofar + bytes, total_bytes);
23c9e63c 93 }
23c9e63c
DD
94}
95
b24c88b3 96static const char *standard_dirs[] = {
1a18aed7 97 "/bin",
904d24fe 98 "/etc",
1a18aed7 99 "/lib",
904d24fe 100 "/tmp",
4a83b7b0 101 "/usr",
1a18aed7
DD
102 "/usr/bin",
103 "/usr/lib",
ef45c299 104 "/usr/src",
4a83b7b0 105 "/usr/local",
1a18aed7
DD
106 "/usr/local/bin",
107 "/usr/local/etc",
108 "/usr/local/lib",
904d24fe 109 "/usr/tmp",
1a18aed7 110 "/var/run",
904d24fe
DD
111 "/var/tmp",
112 0
113};
114
ad3c7385
RC
115static int num_installs, num_replacements, num_uninstalls;
116static void uninstall_one (packagemeta &);
117static int replace_one (packagemeta &);
3c054baf
RC
118static int install_one_source (packagemeta &, packagesource &, String const &,
119 String const &, package_type_t);
ad3c7385 120static bool rebootneeded;
3b9077d4 121
fa0c0d10 122/* FIXME: upgrades should be a method too */
3b9077d4 123static void
cbfc4215 124uninstall_one (packagemeta & pkgm)
3b9077d4 125{
ab57ceaa 126 Progress.SetText1 ("Uninstalling...");
3c054baf 127 Progress.SetText2 (pkgm.name.cstr_oneuse());
1ac649ed 128 log (LOG_PLAIN, String("Uninstalling ") + pkgm.name);
ab57ceaa
RC
129 pkgm.uninstall ();
130 num_uninstalls++;
3b9077d4
DD
131}
132
ad3c7385
RC
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 */
139static int
140replace_one (packagemeta & pkg)
141{
142 int errors = 0;
143 Progress.SetText1 ("Replacing...");
3c054baf 144 Progress.SetText2 (pkg.name.cstr_oneuse());
1ac649ed 145 log (LOG_PLAIN, String( "Replacing ") + pkg.name);
ad3c7385
RC
146 pkg.uninstall ();
147
148 errors +=
6dc75764 149 install_one_source (pkg, pkg.desired->bin, "cygfile://","/", package_binary);
ad3c7385
RC
150 if (!errors)
151 pkg.installed = pkg.desired;
152 num_replacements++;
153 return errors;
154}
42bf5b92 155
bb849dbd
RC
156
157/* install one source at a given prefix. */
3b9077d4 158static int
bb849dbd 159install_one_source (packagemeta & pkgm, packagesource & source,
3c054baf 160 String const &prefixURL, String const &prefixPath, package_type_t type)
3b9077d4
DD
161{
162 int errors = 0;
ab57ceaa 163 Progress.SetText2 (source.Base ());
7c6ef2c3 164 if (!source.Cached () || !io_stream::exists (source.Cached ()))
c46a33a9 165 {
ab57ceaa 166 note (NULL, IDS_ERR_OPEN_READ, source.Cached (), "No such file");
bb849dbd 167 return 1;
c46a33a9 168 }
bb849dbd
RC
169 io_stream *lst = 0;
170 if (type == package_binary)
c46a33a9 171 {
bb849dbd
RC
172 io_stream *tmp =
173 io_stream::
3c054baf 174 open (String ("cygfile:///etc/setup/") + pkgm.name + ".lst.gz",
bb849dbd
RC
175 "wb");
176 lst = new compress_gz (tmp, "w9");
177 if (lst->error ())
178 {
179 delete lst;
180 lst = NULL;
181 }
c46a33a9 182 }
b24c88b3 183
bb849dbd 184 package_bytes = source.size;
3b9077d4 185
bb849dbd
RC
186 char msg[64];
187 strcpy (msg, "Installing");
ab57ceaa 188 Progress.SetText1 (msg);
1ac649ed 189 log (LOG_PLAIN, String (msg) + " " + source.Cached ());
58ee6135
RC
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 }
bb849dbd
RC
218 io_stream *tmp = io_stream::open (source.Cached (), "rb");
219 archive *thefile = 0;
220 if (tmp)
d4a4527d 221 {
bb849dbd
RC
222 io_stream *tmp2 = compress::decompress (tmp);
223 if (tmp2)
224 thefile = archive::extract (tmp2);
225 else
226 thefile = archive::extract (tmp);
3b9077d4 227 }
bb849dbd
RC
228 /* FIXME: potential leak of either *tmp or *tmp2 */
229 if (thefile)
d4a4527d 230 {
3c054baf
RC
231 String fn;
232 while ((fn = thefile->next_file_name ()).size())
bb849dbd
RC
233 {
234 if (lst)
3c054baf
RC
235 {
236 String tmp=fn + "\n";
2fa7c5a4 237 lst->write (tmp.cstr_oneuse(), tmp.size());
3c054baf
RC
238 }
239
240 String canonicalfn = prefixPath + fn;
bb849dbd 241
3c054baf
RC
242 Progress.SetText3 (canonicalfn.cstr_oneuse());
243 log (LOG_BABBLE, String("Installing file ") + prefixURL + prefixPath + fn);
6dc75764 244 if (archive::extract_file (thefile, prefixURL, prefixPath) != 0)
bb849dbd 245 {
ad3c7385 246 //extract to temp location
6dc75764 247 if (archive::extract_file (thefile, prefixURL, prefixPath, ".new") != 0)
ad3c7385 248 {
1ac649ed
RC
249 log (LOG_PLAIN,
250 String("Unable to install file ") +
251 prefixURL + prefixPath + fn);
ad3c7385
RC
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 =
1ac649ed
RC
263 GetShortPathName (cygpath (String ("/") + fn +
264 ".new").cstr_oneuse(),
ad3c7385
RC
265 source, MAX_PATH);
266 if (!len || len > MAX_PATH)
267 {
3c054baf 268 log (LOG_TIMESTAMP,
ad3c7385 269 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
1ac649ed
RC
270 cygpath (String ("/") + fn).cstr_oneuse(),
271 cygpath (String ("/") + fn +
272 ".new").cstr_oneuse(),
ad3c7385
RC
273 GetLastError ());
274 ++errors;
275 }
276 else
277 {
278 char dest[MAX_PATH];
279 len =
1ac649ed
RC
280 GetShortPathName (cygpath (String ("/") +
281 fn).cstr_oneuse(),
282 dest, MAX_PATH);
ad3c7385
RC
283 if (!len || len > MAX_PATH)
284 {
3c054baf 285 log (LOG_TIMESTAMP,
ad3c7385 286 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
1ac649ed
RC
287 cygpath (String ("/") + fn).cstr_oneuse(),
288 cygpath (String ("/") + fn +
289 ".new").cstr_oneuse(),
ad3c7385
RC
290 GetLastError ());
291 ++errors;
292
293 }
294 else
295 /* trigger a replacement on reboot */
296 if (!WritePrivateProfileString
297 ("rename", dest, source, "WININIT.INI"))
298 {
3c054baf 299 log (LOG_TIMESTAMP,
ad3c7385 300 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
1ac649ed
RC
301 cygpath (String ("/") + fn).cstr_oneuse(),
302 cygpath (String ("/") + fn +
303 ".new").cstr_oneuse(),
ad3c7385
RC
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 */
1ac649ed
RC
317 if (!MoveFileEx (cygpath (String ("/") + fn +
318 ".new").cstr_oneuse(),
319 cygpath (String ("/") + fn).cstr_oneuse(),
ad3c7385
RC
320 MOVEFILE_DELAY_UNTIL_REBOOT |
321 MOVEFILE_REPLACE_EXISTING))
322 {
3c054baf 323 log (LOG_TIMESTAMP,
ad3c7385 324 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
1ac649ed
RC
325 cygpath (String ("/") + fn).cstr_oneuse(),
326 cygpath (String ("/") + fn + ".new").cstr_oneuse(),
ad3c7385
RC
327 GetLastError ());
328 ++errors;
329 }
330 else
331 rebootneeded = true;
332 break;
333 }
334 }
bb849dbd
RC
335 }
336
337 progress (tmp->tell ());
338 num_installs++;
339 }
340 delete thefile;
341
342 total_bytes_sofar += package_bytes;
d4a4527d 343 }
3b9077d4 344
90d14922 345
bb849dbd
RC
346 progress (0);
347
3c054baf 348 int df = diskfull (get_root_dir ().cstr_oneuse());
ab57ceaa 349 Progress.SetBar3 (df);
bb849dbd
RC
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 */
358static int
359install_one (packagemeta & pkg)
360{
361 int errors = 0;
362
ad3c7385 363 if (pkg.installed != pkg.desired && pkg.desired->binpicked)
ab57ceaa 364 {
bb849dbd 365 errors +=
6dc75764 366 install_one_source (pkg, pkg.desired->bin, "cygfile://","/",
ab57ceaa
RC
367 package_binary);
368 if (!errors)
369 pkg.installed = pkg.desired;
370 }
371 if (pkg.desired->srcpicked)
372 errors +=
490717ef 373 install_one_source (pkg, pkg.desired->src, "cygfile://","/usr/src/",
ab57ceaa 374 package_source);
bb849dbd
RC
375
376 /* FIXME: make a upgrade method and reinstate this */
377#if 0
1ac649ed 378 String msg;
c46a33a9 379 if (!pkg->installed)
1ac649ed 380 msg = "Installing";
c46a33a9
CF
381 else
382 {
383 int n = strcmp (pi->version, pkg->installed->version);
384 if (n < 0)
1ac649ed 385 msg = "Reverting";
c46a33a9 386 else if (n == 0)
1ac649ed 387 msg = "Reinstalling";
c46a33a9 388 else
1ac649ed 389 msg = "Upgrading";
c46a33a9
CF
390 }
391
392 switch (pkg->action)
3b9077d4 393 {
c46a33a9 394 case ACTION_PREV:
1ac649ed 395 msg += " previous version...";
3b9077d4 396 break;
c46a33a9 397 case ACTION_CURR:
1ac649ed 398 msg += "...";
3b9077d4 399 break;
c46a33a9 400 case ACTION_TEST:
1ac649ed 401 msg += " test version...";
42bf5b92 402 break;
b24c88b3
RC
403 default:
404 /* FIXME: log this somehow */
405 break;
3b9077d4 406 }
1ac649ed
RC
407 SetWindowText (ins_action, msg.cstr_oneuse());
408 log (LOG_PLAIN, msg + " " + file);
bb849dbd 409#endif
c46a33a9 410
3b9077d4
DD
411 return errors;
412}
413
72fd1d1e
CF
414static void
415check_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];
b24c88b3
RC
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))
72fd1d1e
CF
431 {
432 case IDYES:
433 if (!DeleteFile (buf))
434 {
435 sprintf (msg, "Couldn't delete file %s.\r\n"
b24c88b3 436 "Is the DLL in use by another application?\r\n"
7c7034e8
RC
437 "You should delete the old version of cygwin1.dll\r\n"
438 "at your earliest convenience.", buf);
b24c88b3
RC
439 MessageBox (NULL, buf, "Couldn't delete file",
440 MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
72fd1d1e
CF
441 }
442 break;
443 default:
b24c88b3 444 break;
72fd1d1e
CF
445 }
446
447 return;
448}
449
ab57ceaa
RC
450static void
451do_install_thread (HINSTANCE h, HWND owner)
23c9e63c 452{
3b9077d4 453 int i;
2a1a01e0
DD
454 int errors = 0;
455
ad3c7385
RC
456 num_installs = 0, num_uninstalls = 0, num_replacements = 0;
457 rebootneeded = false;
3b9077d4 458
50225eae 459 next_dialog = IDD_DESKTOP;
23c9e63c 460
490717ef 461 io_stream::mkpath_p (PATH_TO_DIR, String ("file://") + get_root_dir ());
904d24fe 462
b24c88b3 463 for (i = 0; standard_dirs[i]; i++)
904d24fe 464 {
1ac649ed 465 String p = cygpath (standard_dirs[i]);
3c054baf 466 if (p.size())
490717ef 467 io_stream::mkpath_p (PATH_TO_DIR, String ("file://") + p);
904d24fe
DD
468 }
469
1a18aed7 470 /* Create /var/run/utmp */
b24c88b3
RC
471 io_stream *utmp = io_stream::open ("cygfile:///var/run/utmp", "wb");
472 delete utmp;
1a18aed7 473
23c9e63c
DD
474 init_dialog ();
475
476 total_bytes = 0;
477 total_bytes_sofar = 0;
478
3c054baf 479 int df = diskfull (get_root_dir ().cstr_oneuse());
ab57ceaa 480 Progress.SetBar3 (df);
d07591a3 481
0af2d779
CF
482 int istext = (root_text == IDC_ROOT_TEXT) ? 1 : 0;
483 int issystem = (root_scope == IDC_ROOT_SYSTEM) ? 1 : 0;
484
1ac649ed
RC
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);
0af2d779
CF
488 set_cygdrive_flags (istext, issystem);
489
8e9aa511
RC
490 /* Let's hope people won't uninstall packages before installing [b]ash */
491 init_run_script ();
492
bb849dbd 493 packagedb db;
bc78a6d5 494 for (size_t n = 1; n <= db.packages.number (); n++)
cbfc4215 495 {
ab57ceaa
RC
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 }
cbfc4215 505 }
bb849dbd 506
ad3c7385 507 /* start with uninstalls - remove files that new packages may replace */
bc78a6d5 508 for (size_t n = 1; n <= db.packages.number (); n++)
23c9e63c 509 {
ab57ceaa 510 packagemeta & pkg = *db.packages[n];
cbfc4215 511 if (pkg.installed && (!pkg.desired || pkg.desired != pkg.installed))
ad3c7385
RC
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)
4a83b7b0 523 {
58ee6135
RC
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"));
9f4a0c62 535 LogSingleton::GetInstance().exit (1);
58ee6135
RC
536 return;
537 }
538 }
4a83b7b0 539 }
ad3c7385
RC
540 }
541
542 for (size_t n = 1; n <= db.packages.number (); n++)
543 {
544 packagemeta & pkg = *db.packages[n];
23c9e63c 545
ab57ceaa 546 if (pkg.desired && (pkg.desired->srcpicked || pkg.desired->binpicked))
23c9e63c 547 {
58ee6135 548 try
40aef45e 549 {
58ee6135
RC
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"));
9f4a0c62 560 LogSingleton::GetInstance().exit (1);
58ee6135
RC
561 return;
562 }
40aef45e 563 }
4a83b7b0 564 }
b24c88b3 565 } // end of big package loop
4a83b7b0 566
ad3c7385
RC
567 if (rebootneeded)
568 note (owner, IDS_REBOOT_REQUIRED);
569
7c7034e8
RC
570 int temperr;
571 if ((temperr = db.flush ()))
23c9e63c 572 {
7c7034e8 573 const char *err = strerror (temperr);
23c9e63c
DD
574 if (!err)
575 err = "(unknown error)";
3c054baf
RC
576 fatal (owner, IDS_ERR_OPEN_WRITE, "Package Database",
577 err);
23c9e63c
DD
578 }
579
72fd1d1e
CF
580 if (!errors)
581 check_for_old_cygwin ();
4a83b7b0
DD
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
2a1a01e0
DD
593 if (errors)
594 exit_msg = IDS_INSTALL_INCOMPLETE;
595 else
596 exit_msg = IDS_INSTALL_COMPLETE;
23c9e63c 597}
ab57ceaa 598
45e01f23 599static DWORD WINAPI
ab57ceaa
RC
600do_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
45e01f23 610 ExitThread (0);
ab57ceaa
RC
611}
612
613static HANDLE context[2];
614
615void
616do_install (HINSTANCE h, HWND owner)
617{
618 context[0] = h;
619 context[1] = owner;
620
45e01f23
RC
621 DWORD threadID;
622 CreateThread (NULL, 0, do_install_reflector, context, 0, &threadID);
ab57ceaa 623}
This page took 0.102579 seconds and 5 git commands to generate.