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"
69 extern ThreeBarProgressPage Progress
;
71 static int total_bytes
= 0;
72 static int total_bytes_sofar
= 0;
73 static int package_bytes
= 0;
78 Progress
.SetText2 ("");
79 Progress
.SetText3 ("");
85 if (package_bytes
> 0)
87 Progress
.SetBar1 (bytes
, package_bytes
);
92 Progress
.SetBar2 (total_bytes_sofar
+ bytes
, total_bytes
);
96 static const char *standard_dirs
[] = {
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
;
122 /* FIXME: upgrades should be a method too */
124 uninstall_one (packagemeta
& pkgm
)
126 Progress
.SetText1 ("Uninstalling...");
127 Progress
.SetText2 (pkgm
.name
.cstr_oneuse());
128 log (LOG_PLAIN
, String("Uninstalling ") + pkgm
.name
);
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.
140 replace_one (packagemeta
& pkg
)
143 Progress
.SetText1 ("Replacing...");
144 Progress
.SetText2 (pkg
.name
.cstr_oneuse());
145 log (LOG_PLAIN
, String( "Replacing ") + pkg
.name
);
149 install_one_source (pkg
, *pkg
.desired
.source(), "cygfile://","/", package_binary
);
151 pkg
.installed
= pkg
.desired
;
157 /* install one source at a given prefix. */
159 install_one_source (packagemeta
& pkgm
, packagesource
& source
,
160 String
const &prefixURL
, String
const &prefixPath
, package_type_t type
)
163 Progress
.SetText2 (source
.Base ());
164 if (!source
.Cached () || !io_stream::exists (source
.Cached ()))
166 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (), "No such file");
170 if (type
== package_binary
)
174 open (String ("cygfile:///etc/setup/") + pkgm
.name
+ ".lst.gz",
176 lst
= new compress_gz (tmp
, "w9");
184 package_bytes
= source
.size
;
187 strcpy (msg
, "Installing");
188 Progress
.SetText1 (msg
);
189 log (LOG_PLAIN
, String (msg
) + " " + source
.Cached ());
191 if (source
.md5
.isSet())
193 // check the MD5 sum of the cached file here
194 io_stream
*thefile
= io_stream::open (source
.Cached (), "rb");
200 unsigned char buffer
[16384];
202 while ((count
= thefile
->read (buffer
, 16384)) > 0)
203 md5_append (&pns
, buffer
, count
);
206 throw new Exception ("__LINE__ __FILE__", (String ("IO Error reading ") + source
.Cached()).cstr_oneuse(), APPERR_IO_ERROR
);
208 md5_byte_t tempdigest
[16];
209 md5_finish(&pns
, tempdigest
);
211 tempMD5
.set (tempdigest
);
213 log (LOG_BABBLE
, String ("For file") + source
.Cached() + " ini digest is" + source
.md5
.print() + " file digest is " + tempMD5
.print());
215 if (source
.md5
!= tempMD5
)
216 throw new Exception ("__LINE__ __FILE__", (String ("Checksum failure for ") + source
.Cached()).cstr_oneuse(), APPERR_CORRUPT_PACKAGE
);
218 io_stream
*tmp
= io_stream::open (source
.Cached (), "rb");
219 archive
*thefile
= 0;
222 io_stream
*tmp2
= compress::decompress (tmp
);
224 thefile
= archive::extract (tmp2
);
226 thefile
= archive::extract (tmp
);
228 /* FIXME: potential leak of either *tmp or *tmp2 */
232 while ((fn
= thefile
->next_file_name ()).size())
236 String tmp
=fn
+ "\n";
237 lst
->write (tmp
.cstr_oneuse(), tmp
.size());
240 String canonicalfn
= prefixPath
+ fn
;
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)
246 //extract to temp location
247 if (archive::extract_file (thefile
, prefixURL
, prefixPath
, ".new") != 0)
250 String("Unable to install file ") +
251 prefixURL
+ prefixPath
+ fn
);
257 switch (Win32::OS ())
260 /* Get the short file names */
261 char source
[MAX_PATH
];
263 GetShortPathName (cygpath (String ("/") + fn
+
264 ".new").cstr_oneuse(),
266 if (!len
|| len
> MAX_PATH
)
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(),
280 GetShortPathName (cygpath (String ("/") +
283 if (!len
|| len
> MAX_PATH
)
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(),
295 /* trigger a replacement on reboot */
296 if (!WritePrivateProfileString
297 ("rename", dest
, source
, "WININIT.INI"))
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(),
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
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
))
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(),
337 progress (tmp
->tell ());
342 total_bytes_sofar
+= package_bytes
;
348 int df
= diskfull (get_root_dir ().cstr_oneuse());
349 Progress
.SetBar3 (df
);
357 /* install a package, install both the binary and source aspects if needed */
359 install_one (packagemeta
& pkg
)
363 if (pkg
.installed
!= pkg
.desired
&& pkg
.desired
.picked())
366 install_one_source (pkg
, *pkg
.desired
.source(), "cygfile://","/",
369 pkg
.installed
= pkg
.desired
;
371 if (pkg
.desired
.sourcePackage().picked())
373 install_one_source (pkg
, *pkg
.desired
.sourcePackage().source(), "cygfile://","/usr/src/",
376 /* FIXME: make a upgrade method and reinstate this */
383 int n
= strcmp (pi
->version
, pkg
->installed
->version
);
387 msg
= "Reinstalling";
395 msg
+= " previous version...";
401 msg
+= " test version...";
404 /* FIXME: log this somehow */
407 SetWindowText (ins_action
, msg
.cstr_oneuse());
408 log (LOG_PLAIN
, msg
+ " " + file
);
415 check_for_old_cygwin ()
417 char buf
[_MAX_PATH
+ sizeof ("\\cygwin1.dll")];
418 if (!GetSystemDirectory (buf
, sizeof (buf
)))
420 strcat (buf
, "\\cygwin1.dll");
421 if (_access (buf
, 0) != 0)
424 char msg
[sizeof (buf
) + 132];
426 "An old version of cygwin1.dll was found here:\r\n%s\r\nDelete?",
429 (NULL
, msg
, "What's that doing there?",
430 MB_YESNO
| MB_ICONQUESTION
| MB_TASKMODAL
))
433 if (!DeleteFile (buf
))
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
);
451 do_install_thread (HINSTANCE h
, HWND owner
)
456 num_installs
= 0, num_uninstalls
= 0, num_replacements
= 0;
457 rebootneeded
= false;
459 next_dialog
= IDD_DESKTOP
;
461 io_stream::mkpath_p (PATH_TO_DIR
, String ("file://") + get_root_dir ());
463 for (i
= 0; standard_dirs
[i
]; i
++)
465 String p
= cygpath (standard_dirs
[i
]);
467 io_stream::mkpath_p (PATH_TO_DIR
, String ("file://") + p
);
470 /* Create /var/run/utmp */
471 io_stream
*utmp
= io_stream::open ("cygfile:///var/run/utmp", "wb");
477 total_bytes_sofar
= 0;
479 int df
= diskfull (get_root_dir ().cstr_oneuse());
480 Progress
.SetBar3 (df
);
482 int istext
= (root_text
== IDC_ROOT_TEXT
) ? 1 : 0;
483 int issystem
= (root_scope
== IDC_ROOT_SYSTEM
) ? 1 : 0;
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
);
490 /* Let's hope people won't uninstall packages before installing [b]ash */
494 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
496 packagemeta
& pkg
= *db
.packages
[n
];
498 if (pkg
.desired
.changeRequested())
500 if (pkg
.desired
.picked())
501 total_bytes
+= pkg
.desired
.source()->size
;
502 if (pkg
.desired
.sourcePackage ().picked())
503 total_bytes
+= pkg
.desired
.sourcePackage ().source()->size
;
507 /* start with uninstalls - remove files that new packages may replace */
508 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
510 packagemeta
& pkg
= *db
.packages
[n
];
511 if (pkg
.installed
&& (!pkg
.desired
|| pkg
.desired
!= pkg
.installed
))
515 /* now in-place binary upgrades/reinstalls, as these may remove fils
516 * that have been moved into new packages
519 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
521 packagemeta
& pkg
= *db
.packages
[n
];
522 if (pkg
.installed
&& pkg
.desired
.picked())
526 e
+= replace_one (pkg
);
532 if (yesno (owner
, IDS_INSTALL_ERROR
, e
->what()) != IDYES
)
534 log (LOG_TIMESTAMP
, String ("User cancelled setup after install error"));
535 LogSingleton::GetInstance().exit (1);
542 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
544 packagemeta
& pkg
= *db
.packages
[n
];
546 if (pkg
.desired
&& pkg
.desired
.changeRequested())
551 e
+= install_one (pkg
);
557 if (yesno (owner
, IDS_INSTALL_ERROR
, e
->what()) != IDYES
)
559 log (LOG_TIMESTAMP
, String ("User cancelled setup after install error"));
560 LogSingleton::GetInstance().exit (1);
565 } // end of big package loop
568 note (owner
, IDS_REBOOT_REQUIRED
);
571 if ((temperr
= db
.flush ()))
573 const char *err
= strerror (temperr
);
575 err
= "(unknown error)";
576 fatal (owner
, IDS_ERR_OPEN_WRITE
, "Package Database",
581 check_for_old_cygwin ();
582 if (num_installs
== 0 && num_uninstalls
== 0)
584 exit_msg
= IDS_NOTHING_INSTALLED
;
587 if (num_installs
== 0)
589 exit_msg
= IDS_UNINSTALL_COMPLETE
;
594 exit_msg
= IDS_INSTALL_INCOMPLETE
;
596 exit_msg
= IDS_INSTALL_COMPLETE
;
600 do_install_reflector (void *p
)
603 context
= (HANDLE
*) p
;
605 do_install_thread ((HINSTANCE
) context
[0], (HWND
) context
[1]);
607 // Tell the progress page that we're done downloading
608 Progress
.PostMessage (WM_APP_INSTALL_THREAD_COMPLETE
);
613 static HANDLE context
[2];
616 do_install (HINSTANCE h
, HWND owner
)
622 CreateThread (NULL
, 0, do_install_reflector
, context
, 0, &threadID
);