2 * Copyright (c) 2000, Red Hat, Inc.
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.
9 * A copy of the GNU General Public License can be found at
12 * Written by DJ Delorie <dj@cygnus.com>
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. */
23 static const char *cvsid
= "\n%%% $Id$\n";
32 #include <sys/types.h>
37 #include "zlib/zlib.h"
48 #include "filemanip.h"
49 #include "io_stream.h"
51 #include "compress_gz.h"
53 #include "archive_tar.h"
56 #include "package_db.h"
57 #include "package_meta.h"
58 #include "package_version.h"
59 #include "package_source.h"
67 #include "Exception.h"
68 #include "getopt++/BoolOption.h"
70 extern ThreeBarProgressPage Progress
;
72 static int total_bytes
= 0;
73 static int total_bytes_sofar
= 0;
74 static int package_bytes
= 0;
76 static BoolOption
NoReplaceOnReboot (false, 'r', "no-replaceonreboot",
77 "Disable replacing in-use files on next "
83 Progress
.SetText2 ("");
84 Progress
.SetText3 ("");
90 if (package_bytes
> 0)
92 Progress
.SetBar1 (bytes
, package_bytes
);
97 Progress
.SetBar2 (total_bytes_sofar
+ bytes
, total_bytes
);
101 static const char *standard_dirs
[] = {
120 static int num_installs
, num_replacements
, num_uninstalls
;
121 static void uninstall_one (packagemeta
&);
122 static int replace_one (packagemeta
&);
123 static int install_one_source (packagemeta
&, packagesource
&, String
const &,
124 String
const &, package_type_t
);
125 static bool rebootneeded
;
127 /* FIXME: upgrades should be a method too */
129 uninstall_one (packagemeta
& pkgm
)
131 Progress
.SetText1 ("Uninstalling...");
132 Progress
.SetText2 (pkgm
.name
.cstr_oneuse());
133 log (LOG_PLAIN
, String("Uninstalling ") + pkgm
.name
);
138 /* uninstall and install a package, preserving configuration
139 * files and the like.
140 * This method should also know about replacing in-use file.
141 * ASSUMPTIONS: pkgm is installed.
142 * pkgm has a desired package.
145 replace_one (packagemeta
& pkg
)
148 Progress
.SetText1 ("Replacing...");
149 Progress
.SetText2 (pkg
.name
.cstr_oneuse());
150 log (LOG_PLAIN
, String( "Replacing ") + pkg
.name
);
154 install_one_source (pkg
, *pkg
.desired
.source(), "cygfile://","/", package_binary
);
156 pkg
.installed
= pkg
.desired
;
162 /* install one source at a given prefix. */
164 install_one_source (packagemeta
& pkgm
, packagesource
& source
,
165 String
const &prefixURL
, String
const &prefixPath
, package_type_t type
)
168 Progress
.SetText2 (source
.Base ());
169 if (!source
.Cached () || !io_stream::exists (source
.Cached ()))
171 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (), "No such file");
175 if (type
== package_binary
)
179 open (String ("cygfile:///etc/setup/") + pkgm
.name
+ ".lst.gz",
181 lst
= new compress_gz (tmp
, "w9");
189 package_bytes
= source
.size
;
192 strcpy (msg
, "Installing");
193 Progress
.SetText1 (msg
);
194 log (LOG_PLAIN
, String (msg
) + " " + source
.Cached ());
196 if (source
.md5
.isSet())
198 // check the MD5 sum of the cached file here
199 io_stream
*thefile
= io_stream::open (source
.Cached (), "rb");
205 unsigned char buffer
[16384];
207 while ((count
= thefile
->read (buffer
, 16384)) > 0)
208 md5_append (&pns
, buffer
, count
);
211 throw new Exception ("__LINE__ __FILE__", (String ("IO Error reading ") + source
.Cached()).cstr_oneuse(), APPERR_IO_ERROR
);
213 md5_byte_t tempdigest
[16];
214 md5_finish(&pns
, tempdigest
);
216 tempMD5
.set (tempdigest
);
218 log (LOG_BABBLE
, String ("For file") + source
.Cached() + " ini digest is" + source
.md5
.print() + " file digest is " + tempMD5
.print());
220 if (source
.md5
!= tempMD5
)
221 throw new Exception ("__LINE__ __FILE__", (String ("Checksum failure for ") + source
.Cached()).cstr_oneuse(), APPERR_CORRUPT_PACKAGE
);
223 io_stream
*tmp
= io_stream::open (source
.Cached (), "rb");
224 archive
*thefile
= 0;
227 io_stream
*tmp2
= compress::decompress (tmp
);
229 thefile
= archive::extract (tmp2
);
231 thefile
= archive::extract (tmp
);
233 /* FIXME: potential leak of either *tmp or *tmp2 */
237 while ((fn
= thefile
->next_file_name ()).size())
241 String tmp
=fn
+ "\n";
242 lst
->write (tmp
.cstr_oneuse(), tmp
.size());
245 String canonicalfn
= prefixPath
+ fn
;
247 Progress
.SetText3 (canonicalfn
.cstr_oneuse());
248 log (LOG_BABBLE
, String("Installing file ") + prefixURL
+ prefixPath
+ fn
);
249 if (archive::extract_file (thefile
, prefixURL
, prefixPath
) != 0)
251 if (NoReplaceOnReboot
)
254 log (LOG_PLAIN
, String("Not replacing in-use file ") +
255 prefixURL
+ prefixPath
+ fn
);
258 //extract to temp location
259 if (archive::extract_file (thefile
, prefixURL
, prefixPath
, ".new") != 0)
262 String("Unable to install file ") +
263 prefixURL
+ prefixPath
+ fn
);
269 switch (Win32::OS ())
272 /* Get the short file names */
273 char source
[MAX_PATH
];
275 GetShortPathName (cygpath (String ("/") + fn
+
276 ".new").cstr_oneuse(),
278 if (!len
|| len
> MAX_PATH
)
281 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
282 cygpath (String ("/") + fn
).cstr_oneuse(),
283 cygpath (String ("/") + fn
+
284 ".new").cstr_oneuse(),
292 GetShortPathName (cygpath (String ("/") +
295 if (!len
|| len
> MAX_PATH
)
298 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
299 cygpath (String ("/") + fn
).cstr_oneuse(),
300 cygpath (String ("/") + fn
+
301 ".new").cstr_oneuse(),
307 /* trigger a replacement on reboot */
308 if (!WritePrivateProfileString
309 ("rename", dest
, source
, "WININIT.INI"))
312 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
313 cygpath (String ("/") + fn
).cstr_oneuse(),
314 cygpath (String ("/") + fn
+
315 ".new").cstr_oneuse(),
324 /* XXX FIXME: prefix may not be / for in use files -
325 * although it most likely is
326 * - we need a io method to get win32 paths
327 * or to wrap this system call
329 if (!MoveFileEx (cygpath (String ("/") + fn
+
330 ".new").cstr_oneuse(),
331 cygpath (String ("/") + fn
).cstr_oneuse(),
332 MOVEFILE_DELAY_UNTIL_REBOOT
|
333 MOVEFILE_REPLACE_EXISTING
))
336 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
337 cygpath (String ("/") + fn
).cstr_oneuse(),
338 cygpath (String ("/") + fn
+ ".new").cstr_oneuse(),
349 progress (tmp
->tell ());
354 total_bytes_sofar
+= package_bytes
;
360 int df
= diskfull (get_root_dir ().cstr_oneuse());
361 Progress
.SetBar3 (df
);
369 /* install a package, install both the binary and source aspects if needed */
371 install_one (packagemeta
& pkg
)
375 if (pkg
.installed
!= pkg
.desired
&& pkg
.desired
.picked())
378 install_one_source (pkg
, *pkg
.desired
.source(), "cygfile://","/",
381 pkg
.installed
= pkg
.desired
;
383 if (pkg
.desired
.sourcePackage().picked())
385 install_one_source (pkg
, *pkg
.desired
.sourcePackage().source(), "cygfile://","/usr/src/",
388 /* FIXME: make a upgrade method and reinstate this */
395 int n
= strcmp (pi
->version
, pkg
->installed
->version
);
399 msg
= "Reinstalling";
407 msg
+= " previous version...";
413 msg
+= " test version...";
416 /* FIXME: log this somehow */
419 SetWindowText (ins_action
, msg
.cstr_oneuse());
420 log (LOG_PLAIN
, msg
+ " " + file
);
427 check_for_old_cygwin ()
429 char buf
[_MAX_PATH
+ sizeof ("\\cygwin1.dll")];
430 if (!GetSystemDirectory (buf
, sizeof (buf
)))
432 strcat (buf
, "\\cygwin1.dll");
433 if (_access (buf
, 0) != 0)
436 char msg
[sizeof (buf
) + 132];
438 "An old version of cygwin1.dll was found here:\r\n%s\r\nDelete?",
441 (NULL
, msg
, "What's that doing there?",
442 MB_YESNO
| MB_ICONQUESTION
| MB_TASKMODAL
))
445 if (!DeleteFile (buf
))
447 sprintf (msg
, "Couldn't delete file %s.\r\n"
448 "Is the DLL in use by another application?\r\n"
449 "You should delete the old version of cygwin1.dll\r\n"
450 "at your earliest convenience.", buf
);
451 MessageBox (NULL
, buf
, "Couldn't delete file",
452 MB_OK
| MB_ICONEXCLAMATION
| MB_TASKMODAL
);
463 do_install_thread (HINSTANCE h
, HWND owner
)
468 num_installs
= 0, num_uninstalls
= 0, num_replacements
= 0;
469 rebootneeded
= false;
471 next_dialog
= IDD_DESKTOP
;
473 io_stream::mkpath_p (PATH_TO_DIR
, String ("file://") + get_root_dir ());
475 for (i
= 0; standard_dirs
[i
]; i
++)
477 String p
= cygpath (standard_dirs
[i
]);
479 io_stream::mkpath_p (PATH_TO_DIR
, String ("file://") + p
);
482 /* Create /var/run/utmp */
483 io_stream
*utmp
= io_stream::open ("cygfile:///var/run/utmp", "wb");
489 total_bytes_sofar
= 0;
491 int df
= diskfull (get_root_dir ().cstr_oneuse());
492 Progress
.SetBar3 (df
);
494 int istext
= (root_text
== IDC_ROOT_TEXT
) ? 1 : 0;
495 int issystem
= (root_scope
== IDC_ROOT_SYSTEM
) ? 1 : 0;
497 create_mount ("/", get_root_dir (), istext
, issystem
);
498 create_mount ("/usr/bin", cygpath ("/bin"), istext
, issystem
);
499 create_mount ("/usr/lib", cygpath ("/lib"), istext
, issystem
);
500 set_cygdrive_flags (istext
, issystem
);
502 /* Let's hope people won't uninstall packages before installing [b]ash */
506 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
507 i
!= db
.packages
.end (); ++i
)
509 packagemeta
& pkg
= **i
;
511 if (pkg
.desired
.changeRequested())
513 if (pkg
.desired
.picked())
514 total_bytes
+= pkg
.desired
.source()->size
;
515 if (pkg
.desired
.sourcePackage ().picked())
516 total_bytes
+= pkg
.desired
.sourcePackage ().source()->size
;
520 /* start with uninstalls - remove files that new packages may replace */
521 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
522 i
!= db
.packages
.end (); ++i
)
524 packagemeta
& pkg
= **i
;
525 if (pkg
.installed
&& (!pkg
.desired
|| pkg
.desired
!= pkg
.installed
))
529 /* now in-place binary upgrades/reinstalls, as these may remove fils
530 * that have been moved into new packages
533 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
534 i
!= db
.packages
.end (); ++i
)
536 packagemeta
& pkg
= **i
;
537 if (pkg
.installed
&& pkg
.desired
.picked())
541 e
+= replace_one (pkg
);
547 if (yesno (owner
, IDS_INSTALL_ERROR
, e
->what()) != IDYES
)
549 log (LOG_TIMESTAMP
, String ("User cancelled setup after install error"));
550 LogSingleton::GetInstance().exit (1);
557 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
558 i
!= db
.packages
.end (); ++i
)
560 packagemeta
& pkg
= **i
;
562 if (pkg
.desired
&& pkg
.desired
.changeRequested())
567 e
+= install_one (pkg
);
573 if (yesno (owner
, IDS_INSTALL_ERROR
, e
->what()) != IDYES
)
575 log (LOG_TIMESTAMP
, String ("User cancelled setup after install error"));
576 LogSingleton::GetInstance().exit (1);
581 } // end of big package loop
584 note (owner
, IDS_REBOOT_REQUIRED
);
587 if ((temperr
= db
.flush ()))
589 const char *err
= strerror (temperr
);
591 err
= "(unknown error)";
592 fatal (owner
, IDS_ERR_OPEN_WRITE
, "Package Database",
597 check_for_old_cygwin ();
598 if (num_installs
== 0 && num_uninstalls
== 0)
600 if (!unattended_mode
) exit_msg
= IDS_NOTHING_INSTALLED
;
603 if (num_installs
== 0)
605 if (!unattended_mode
) exit_msg
= IDS_UNINSTALL_COMPLETE
;
610 exit_msg
= IDS_INSTALL_INCOMPLETE
;
611 else if (!unattended_mode
)
612 exit_msg
= IDS_INSTALL_COMPLETE
;
616 do_install_reflector (void *p
)
619 context
= (HANDLE
*) p
;
621 do_install_thread ((HINSTANCE
) context
[0], (HWND
) context
[1]);
623 // Tell the progress page that we're done downloading
624 Progress
.PostMessage (WM_APP_INSTALL_THREAD_COMPLETE
);
629 static HANDLE context
[2];
632 do_install (HINSTANCE h
, HWND owner
)
638 CreateThread (NULL
, 0, do_install_reflector
, context
, 0, &threadID
);