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"
72 extern ThreeBarProgressPage Progress
;
74 static int total_bytes
= 0;
75 static int total_bytes_sofar
= 0;
76 static int package_bytes
= 0;
78 static BoolOption
NoReplaceOnReboot (false, 'r', "no-replaceonreboot",
79 "Disable replacing in-use files on next "
85 Progress
.SetText2 ("");
86 Progress
.SetText3 ("");
92 if (package_bytes
> 0)
94 Progress
.SetBar1 (bytes
, package_bytes
);
99 Progress
.SetBar2 (total_bytes_sofar
+ bytes
, total_bytes
);
103 static const char *standard_dirs
[] = {
122 static int num_installs
, num_replacements
, num_uninstalls
;
123 static void uninstall_one (packagemeta
&);
124 static int replace_one (packagemeta
&);
125 static int install_one_source (packagemeta
&, packagesource
&, String
const &,
126 String
const &, package_type_t
);
127 static bool rebootneeded
;
129 /* FIXME: upgrades should be a method too */
131 uninstall_one (packagemeta
& pkgm
)
133 Progress
.SetText1 ("Uninstalling...");
134 Progress
.SetText2 (pkgm
.name
.cstr_oneuse());
135 log (LOG_PLAIN
, String("Uninstalling ") + pkgm
.name
);
140 /* uninstall and install a package, preserving configuration
141 * files and the like.
142 * This method should also know about replacing in-use file.
143 * ASSUMPTIONS: pkgm is installed.
144 * pkgm has a desired package.
147 replace_one (packagemeta
& pkg
)
150 Progress
.SetText1 ("Replacing...");
151 Progress
.SetText2 (pkg
.name
.cstr_oneuse());
152 log (LOG_PLAIN
, String( "Replacing ") + pkg
.name
);
156 install_one_source (pkg
, *pkg
.desired
.source(), "cygfile://","/", package_binary
);
158 pkg
.installed
= pkg
.desired
;
164 /* install one source at a given prefix. */
166 install_one_source (packagemeta
& pkgm
, packagesource
& source
,
167 String
const &prefixURL
, String
const &prefixPath
, package_type_t type
)
170 Progress
.SetText2 (source
.Base ());
171 if (!source
.Cached () || !io_stream::exists (source
.Cached ()))
173 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (), "No such file");
177 if (type
== package_binary
)
181 open (String ("cygfile:///etc/setup/") + pkgm
.name
+ ".lst.gz",
183 lst
= new compress_gz (tmp
, "w9");
191 package_bytes
= source
.size
;
194 strcpy (msg
, "Installing");
195 Progress
.SetText1 (msg
);
196 log (LOG_PLAIN
, String (msg
) + " " + source
.Cached ());
198 if (source
.md5
.isSet())
200 // check the MD5 sum of the cached file here
201 io_stream
*thefile
= io_stream::open (source
.Cached (), "rb");
207 unsigned char buffer
[16384];
209 while ((count
= thefile
->read (buffer
, 16384)) > 0)
210 md5_append (&pns
, buffer
, count
);
213 throw new Exception ("__LINE__ __FILE__", (String ("IO Error reading ") + source
.Cached()).cstr_oneuse(), APPERR_IO_ERROR
);
215 md5_byte_t tempdigest
[16];
216 md5_finish(&pns
, tempdigest
);
218 tempMD5
.set (tempdigest
);
220 log (LOG_BABBLE
, String ("For file ") + source
.Cached() + " ini digest is " + source
.md5
.print() + " file digest is " + tempMD5
.print());
222 if (source
.md5
!= tempMD5
)
223 throw new Exception ("__LINE__ __FILE__", (String ("Checksum failure for ") + source
.Cached()).cstr_oneuse(), APPERR_CORRUPT_PACKAGE
);
225 io_stream
*tmp
= io_stream::open (source
.Cached (), "rb");
226 archive
*thefile
= 0;
229 io_stream
*tmp2
= compress::decompress (tmp
);
231 thefile
= archive::extract (tmp2
);
233 thefile
= archive::extract (tmp
);
235 /* FIXME: potential leak of either *tmp or *tmp2 */
239 while ((fn
= thefile
->next_file_name ()).size())
243 String tmp
=fn
+ "\n";
244 lst
->write (tmp
.cstr_oneuse(), tmp
.size());
247 String canonicalfn
= prefixPath
+ fn
;
249 Progress
.SetText3 (canonicalfn
.cstr_oneuse());
250 log (LOG_BABBLE
, String("Installing file ") + prefixURL
+ prefixPath
+ fn
);
251 if (archive::extract_file (thefile
, prefixURL
, prefixPath
) != 0)
253 if (NoReplaceOnReboot
)
256 log (LOG_PLAIN
, String("Not replacing in-use file ") +
257 prefixURL
+ prefixPath
+ fn
);
260 //extract to temp location
261 if (archive::extract_file (thefile
, prefixURL
, prefixPath
, ".new") != 0)
264 String("Unable to install file ") +
265 prefixURL
+ prefixPath
+ fn
);
271 switch (Win32::OS ())
274 /* Get the short file names */
275 char source
[MAX_PATH
];
277 GetShortPathName (cygpath (String ("/") + fn
+
278 ".new").cstr_oneuse(),
280 if (!len
|| len
> MAX_PATH
)
283 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
284 cygpath (String ("/") + fn
).cstr_oneuse(),
285 cygpath (String ("/") + fn
+
286 ".new").cstr_oneuse(),
294 GetShortPathName (cygpath (String ("/") +
297 if (!len
|| len
> MAX_PATH
)
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(),
309 /* trigger a replacement on reboot */
310 if (!WritePrivateProfileString
311 ("rename", dest
, source
, "WININIT.INI"))
314 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
315 cygpath (String ("/") + fn
).cstr_oneuse(),
316 cygpath (String ("/") + fn
+
317 ".new").cstr_oneuse(),
326 /* XXX FIXME: prefix may not be / for in use files -
327 * although it most likely is
328 * - we need a io method to get win32 paths
329 * or to wrap this system call
331 if (!MoveFileEx (cygpath (String ("/") + fn
+
332 ".new").cstr_oneuse(),
333 cygpath (String ("/") + fn
).cstr_oneuse(),
334 MOVEFILE_DELAY_UNTIL_REBOOT
|
335 MOVEFILE_REPLACE_EXISTING
))
338 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
339 cygpath (String ("/") + fn
).cstr_oneuse(),
340 cygpath (String ("/") + fn
+ ".new").cstr_oneuse(),
351 progress (tmp
->tell ());
356 total_bytes_sofar
+= package_bytes
;
362 int df
= diskfull (get_root_dir ().cstr_oneuse());
363 Progress
.SetBar3 (df
);
371 /* install a package, install both the binary and source aspects if needed */
373 install_one (packagemeta
& pkg
)
377 if (pkg
.installed
!= pkg
.desired
&& pkg
.desired
.picked())
380 install_one_source (pkg
, *pkg
.desired
.source(), "cygfile://","/",
383 pkg
.installed
= pkg
.desired
;
385 if (pkg
.desired
.sourcePackage().picked())
387 install_one_source (pkg
, *pkg
.desired
.sourcePackage().source(), "cygfile://","/usr/src/",
390 /* FIXME: make a upgrade method and reinstate this */
397 int n
= strcmp (pi
->version
, pkg
->installed
->version
);
401 msg
= "Reinstalling";
409 msg
+= " previous version...";
415 msg
+= " test version...";
418 /* FIXME: log this somehow */
421 SetWindowText (ins_action
, msg
.cstr_oneuse());
422 log (LOG_PLAIN
, msg
+ " " + file
);
429 check_for_old_cygwin ()
431 char buf
[_MAX_PATH
+ sizeof ("\\cygwin1.dll")];
432 if (!GetSystemDirectory (buf
, sizeof (buf
)))
434 strcat (buf
, "\\cygwin1.dll");
435 if (_access (buf
, 0) != 0)
438 char msg
[sizeof (buf
) + 132];
440 "An old version of cygwin1.dll was found here:\r\n%s\r\nDelete?",
443 (NULL
, msg
, "What's that doing there?",
444 MB_YESNO
| MB_ICONQUESTION
| MB_TASKMODAL
))
447 if (!DeleteFile (buf
))
449 sprintf (msg
, "Couldn't delete file %s.\r\n"
450 "Is the DLL in use by another application?\r\n"
451 "You should delete the old version of cygwin1.dll\r\n"
452 "at your earliest convenience.", buf
);
453 MessageBox (NULL
, buf
, "Couldn't delete file",
454 MB_OK
| MB_ICONEXCLAMATION
| MB_TASKMODAL
);
465 do_install_thread (HINSTANCE h
, HWND owner
)
470 num_installs
= 0, num_uninstalls
= 0, num_replacements
= 0;
471 rebootneeded
= false;
473 next_dialog
= IDD_DESKTOP
;
475 io_stream::mkpath_p (PATH_TO_DIR
, String ("file://") + get_root_dir ());
477 for (i
= 0; standard_dirs
[i
]; i
++)
479 String p
= cygpath (standard_dirs
[i
]);
481 io_stream::mkpath_p (PATH_TO_DIR
, String ("file://") + p
);
484 /* Create /var/run/utmp */
485 io_stream
*utmp
= io_stream::open ("cygfile:///var/run/utmp", "wb");
491 total_bytes_sofar
= 0;
493 int df
= diskfull (get_root_dir ().cstr_oneuse());
494 Progress
.SetBar3 (df
);
496 int istext
= (root_text
== IDC_ROOT_TEXT
) ? 1 : 0;
497 int issystem
= (root_scope
== IDC_ROOT_SYSTEM
) ? 1 : 0;
499 create_mount ("/", get_root_dir (), istext
, issystem
);
500 create_mount ("/usr/bin", cygpath ("/bin"), istext
, issystem
);
501 create_mount ("/usr/lib", cygpath ("/lib"), istext
, issystem
);
502 set_cygdrive_flags (istext
, issystem
);
504 /* Let's hope people won't uninstall packages before installing [b]ash */
508 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
509 i
!= db
.packages
.end (); ++i
)
511 packagemeta
& pkg
= **i
;
513 if (pkg
.desired
.changeRequested())
515 if (pkg
.desired
.picked())
516 total_bytes
+= pkg
.desired
.source()->size
;
517 if (pkg
.desired
.sourcePackage ().picked())
518 total_bytes
+= pkg
.desired
.sourcePackage ().source()->size
;
522 /* start with uninstalls - remove files that new packages may replace */
523 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
524 i
!= db
.packages
.end (); ++i
)
526 packagemeta
& pkg
= **i
;
527 if (pkg
.installed
&& (!pkg
.desired
|| pkg
.desired
!= pkg
.installed
))
531 /* now in-place binary upgrades/reinstalls, as these may remove fils
532 * that have been moved into new packages
535 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
536 i
!= db
.packages
.end (); ++i
)
538 packagemeta
& pkg
= **i
;
539 if (pkg
.installed
&& pkg
.desired
.picked())
543 e
+= replace_one (pkg
);
549 if (yesno (owner
, IDS_INSTALL_ERROR
, e
->what()) != IDYES
)
551 log (LOG_TIMESTAMP
, String ("User cancelled setup after install error"));
552 LogSingleton::GetInstance().exit (1);
559 for (vector
<packagemeta
*>::iterator i
= db
.packages
.begin ();
560 i
!= db
.packages
.end (); ++i
)
562 packagemeta
& pkg
= **i
;
564 if (pkg
.desired
&& pkg
.desired
.changeRequested())
569 e
+= install_one (pkg
);
575 if (yesno (owner
, IDS_INSTALL_ERROR
, e
->what()) != IDYES
)
577 log (LOG_TIMESTAMP
, String ("User cancelled setup after install error"));
578 LogSingleton::GetInstance().exit (1);
583 } // end of big package loop
586 note (owner
, IDS_REBOOT_REQUIRED
);
589 if ((temperr
= db
.flush ()))
591 const char *err
= strerror (temperr
);
593 err
= "(unknown error)";
594 fatal (owner
, IDS_ERR_OPEN_WRITE
, "Package Database",
599 check_for_old_cygwin ();
600 if (num_installs
== 0 && num_uninstalls
== 0)
602 if (!unattended_mode
) exit_msg
= IDS_NOTHING_INSTALLED
;
605 if (num_installs
== 0)
607 if (!unattended_mode
) exit_msg
= IDS_UNINSTALL_COMPLETE
;
612 exit_msg
= IDS_INSTALL_INCOMPLETE
;
613 else if (!unattended_mode
)
614 exit_msg
= IDS_INSTALL_COMPLETE
;
618 do_install_reflector (void *p
)
621 context
= (HANDLE
*) p
;
623 do_install_thread ((HINSTANCE
) context
[0], (HWND
) context
[1]);
625 // Tell the progress page that we're done downloading
626 Progress
.PostMessage (WM_APP_INSTALL_THREAD_COMPLETE
);
631 static HANDLE context
[2];
634 do_install (HINSTANCE h
, HWND owner
)
640 CreateThread (NULL
, 0, do_install_reflector
, context
, 0, &threadID
);