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"
49 #include "filemanip.h"
50 #include "io_stream.h"
52 #include "compress_gz.h"
54 #include "archive_tar.h"
57 #include "package_db.h"
58 #include "package_meta.h"
59 #include "package_version.h"
60 #include "package_source.h"
68 #include "Exception.h"
70 extern ThreeBarProgressPage Progress
;
72 static int total_bytes
= 0;
73 static int total_bytes_sofar
= 0;
74 static int package_bytes
= 0;
79 Progress
.SetText2 ("");
80 Progress
.SetText3 ("");
86 if (package_bytes
> 0)
88 Progress
.SetBar1 (bytes
, package_bytes
);
93 Progress
.SetBar2 (total_bytes_sofar
+ bytes
, total_bytes
);
97 static const char *standard_dirs
[] = {
116 static int num_installs
, num_replacements
, num_uninstalls
;
117 static void uninstall_one (packagemeta
&);
118 static int replace_one (packagemeta
&);
119 static int install_one_source (packagemeta
&, packagesource
&, String
const &,
120 String
const &, package_type_t
);
121 static bool rebootneeded
;
123 /* FIXME: upgrades should be a method too */
125 uninstall_one (packagemeta
& pkgm
)
127 Progress
.SetText1 ("Uninstalling...");
128 Progress
.SetText2 (pkgm
.name
.cstr_oneuse());
129 log (LOG_PLAIN
, String("Uninstalling ") + pkgm
.name
);
134 /* uninstall and install a package, preserving configuration
135 * files and the like.
136 * This method should also know about replacing in-use file.
137 * ASSUMPTIONS: pkgm is installed.
138 * pkgm has a desired package.
141 replace_one (packagemeta
& pkg
)
144 Progress
.SetText1 ("Replacing...");
145 Progress
.SetText2 (pkg
.name
.cstr_oneuse());
146 log (LOG_PLAIN
, String( "Replacing ") + pkg
.name
);
150 install_one_source (pkg
, pkg
.desired
->bin
, "cygfile://","/", package_binary
);
152 pkg
.installed
= pkg
.desired
;
158 /* install one source at a given prefix. */
160 install_one_source (packagemeta
& pkgm
, packagesource
& source
,
161 String
const &prefixURL
, String
const &prefixPath
, package_type_t type
)
164 Progress
.SetText2 (source
.Base ());
165 if (!source
.Cached () || !io_stream::exists (source
.Cached ()))
167 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (), "No such file");
171 if (type
== package_binary
)
175 open (String ("cygfile:///etc/setup/") + pkgm
.name
+ ".lst.gz",
177 lst
= new compress_gz (tmp
, "w9");
185 package_bytes
= source
.size
;
188 strcpy (msg
, "Installing");
189 Progress
.SetText1 (msg
);
190 log (LOG_PLAIN
, String (msg
) + " " + source
.Cached ());
192 if (source
.md5
.isSet())
194 // check the MD5 sum of the cached file here
195 io_stream
*thefile
= io_stream::open (source
.Cached (), "rb");
201 unsigned char buffer
[16384];
203 while ((count
= thefile
->read (buffer
, 16384)) > 0)
204 md5_append (&pns
, buffer
, count
);
207 throw new Exception ("__LINE__ __FILE__", (String ("IO Error reading ") + source
.Cached()).cstr_oneuse(), APPERR_IO_ERROR
);
209 md5_byte_t tempdigest
[16];
210 md5_finish(&pns
, tempdigest
);
212 tempMD5
.set (tempdigest
);
214 log (LOG_BABBLE
, String ("For file") + source
.Cached() + " ini digest is" + source
.md5
.print() + " file digest is " + tempMD5
.print());
216 if (source
.md5
!= tempMD5
)
217 throw new Exception ("__LINE__ __FILE__", (String ("Checksum failure for ") + source
.Cached()).cstr_oneuse(), APPERR_CORRUPT_PACKAGE
);
219 io_stream
*tmp
= io_stream::open (source
.Cached (), "rb");
220 archive
*thefile
= 0;
223 io_stream
*tmp2
= compress::decompress (tmp
);
225 thefile
= archive::extract (tmp2
);
227 thefile
= archive::extract (tmp
);
229 /* FIXME: potential leak of either *tmp or *tmp2 */
233 while ((fn
= thefile
->next_file_name ()).size())
237 String tmp
=fn
+ "\n";
238 lst
->write (tmp
.cstr_oneuse(), tmp
.size());
241 String canonicalfn
= prefixPath
+ fn
;
243 Progress
.SetText3 (canonicalfn
.cstr_oneuse());
244 log (LOG_BABBLE
, String("Installing file ") + prefixURL
+ prefixPath
+ fn
);
245 if (archive::extract_file (thefile
, prefixURL
, prefixPath
) != 0)
247 //extract to temp location
248 if (archive::extract_file (thefile
, prefixURL
, prefixPath
, ".new") != 0)
251 String("Unable to install file ") +
252 prefixURL
+ prefixPath
+ fn
);
258 switch (Win32::OS ())
261 /* Get the short file names */
262 char source
[MAX_PATH
];
264 GetShortPathName (cygpath (String ("/") + fn
+
265 ".new").cstr_oneuse(),
267 if (!len
|| len
> MAX_PATH
)
270 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
271 cygpath (String ("/") + fn
).cstr_oneuse(),
272 cygpath (String ("/") + fn
+
273 ".new").cstr_oneuse(),
281 GetShortPathName (cygpath (String ("/") +
284 if (!len
|| len
> MAX_PATH
)
287 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
288 cygpath (String ("/") + fn
).cstr_oneuse(),
289 cygpath (String ("/") + fn
+
290 ".new").cstr_oneuse(),
296 /* trigger a replacement on reboot */
297 if (!WritePrivateProfileString
298 ("rename", dest
, source
, "WININIT.INI"))
301 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
302 cygpath (String ("/") + fn
).cstr_oneuse(),
303 cygpath (String ("/") + fn
+
304 ".new").cstr_oneuse(),
313 /* XXX FIXME: prefix may not be / for in use files -
314 * although it most likely is
315 * - we need a io method to get win32 paths
316 * or to wrap this system call
318 if (!MoveFileEx (cygpath (String ("/") + fn
+
319 ".new").cstr_oneuse(),
320 cygpath (String ("/") + fn
).cstr_oneuse(),
321 MOVEFILE_DELAY_UNTIL_REBOOT
|
322 MOVEFILE_REPLACE_EXISTING
))
325 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
326 cygpath (String ("/") + fn
).cstr_oneuse(),
327 cygpath (String ("/") + fn
+ ".new").cstr_oneuse(),
338 progress (tmp
->tell ());
343 total_bytes_sofar
+= package_bytes
;
349 int df
= diskfull (get_root_dir ().cstr_oneuse());
350 Progress
.SetBar3 (df
);
358 /* install a package, install both the binary and source aspects if needed */
360 install_one (packagemeta
& pkg
)
364 if (pkg
.installed
!= pkg
.desired
&& pkg
.desired
->binpicked
)
367 install_one_source (pkg
, pkg
.desired
->bin
, "cygfile://","/",
370 pkg
.installed
= pkg
.desired
;
372 if (pkg
.desired
->srcpicked
)
374 install_one_source (pkg
, pkg
.desired
->src
, "cygfile://","/usr/src/",
377 /* FIXME: make a upgrade method and reinstate this */
384 int n
= strcmp (pi
->version
, pkg
->installed
->version
);
388 msg
= "Reinstalling";
396 msg
+= " previous version...";
402 msg
+= " test version...";
405 /* FIXME: log this somehow */
408 SetWindowText (ins_action
, msg
.cstr_oneuse());
409 log (LOG_PLAIN
, msg
+ " " + file
);
416 check_for_old_cygwin ()
418 char buf
[_MAX_PATH
+ sizeof ("\\cygwin1.dll")];
419 if (!GetSystemDirectory (buf
, sizeof (buf
)))
421 strcat (buf
, "\\cygwin1.dll");
422 if (_access (buf
, 0) != 0)
425 char msg
[sizeof (buf
) + 132];
427 "An old version of cygwin1.dll was found here:\r\n%s\r\nDelete?",
430 (NULL
, msg
, "What's that doing there?",
431 MB_YESNO
| MB_ICONQUESTION
| MB_TASKMODAL
))
434 if (!DeleteFile (buf
))
436 sprintf (msg
, "Couldn't delete file %s.\r\n"
437 "Is the DLL in use by another application?\r\n"
438 "You should delete the old version of cygwin1.dll\r\n"
439 "at your earliest convenience.", buf
);
440 MessageBox (NULL
, buf
, "Couldn't delete file",
441 MB_OK
| MB_ICONEXCLAMATION
| MB_TASKMODAL
);
452 do_install_thread (HINSTANCE h
, HWND owner
)
457 num_installs
= 0, num_uninstalls
= 0, num_replacements
= 0;
458 rebootneeded
= false;
460 next_dialog
= IDD_DESKTOP
;
462 io_stream::mkpath_p (PATH_TO_DIR
, String ("file://") + get_root_dir ());
464 for (i
= 0; standard_dirs
[i
]; i
++)
466 String p
= cygpath (standard_dirs
[i
]);
468 io_stream::mkpath_p (PATH_TO_DIR
, String ("file://") + p
);
471 /* Create /var/run/utmp */
472 io_stream
*utmp
= io_stream::open ("cygfile:///var/run/utmp", "wb");
478 total_bytes_sofar
= 0;
480 int df
= diskfull (get_root_dir ().cstr_oneuse());
481 Progress
.SetBar3 (df
);
483 int istext
= (root_text
== IDC_ROOT_TEXT
) ? 1 : 0;
484 int issystem
= (root_scope
== IDC_ROOT_SYSTEM
) ? 1 : 0;
486 create_mount ("/", get_root_dir (), istext
, issystem
);
487 create_mount ("/usr/bin", cygpath ("/bin"), istext
, issystem
);
488 create_mount ("/usr/lib", cygpath ("/lib"), istext
, issystem
);
489 set_cygdrive_flags (istext
, issystem
);
491 /* Let's hope people won't uninstall packages before installing [b]ash */
495 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
497 packagemeta
& pkg
= *db
.packages
[n
];
499 if (pkg
.desired
&& (pkg
.desired
->srcpicked
|| pkg
.desired
->binpicked
))
501 if (pkg
.desired
->srcpicked
)
502 total_bytes
+= pkg
.desired
->src
.size
;
503 if (pkg
.desired
->binpicked
)
504 total_bytes
+= pkg
.desired
->bin
.size
;
508 /* start with uninstalls - remove files that new packages may replace */
509 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
511 packagemeta
& pkg
= *db
.packages
[n
];
512 if (pkg
.installed
&& (!pkg
.desired
|| pkg
.desired
!= pkg
.installed
))
516 /* now in-place binary upgrades/reinstalls, as these may remove fils
517 * that have been moved into new packages
520 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
522 packagemeta
& pkg
= *db
.packages
[n
];
523 if (pkg
.installed
&& pkg
.desired
&& pkg
.desired
->binpicked
)
527 e
+= replace_one (pkg
);
533 if (yesno (owner
, IDS_INSTALL_ERROR
, e
->what()) != IDYES
)
535 log (LOG_TIMESTAMP
, String ("User cancelled setup after install error"));
543 for (size_t n
= 1; n
<= db
.packages
.number (); n
++)
545 packagemeta
& pkg
= *db
.packages
[n
];
547 if (pkg
.desired
&& (pkg
.desired
->srcpicked
|| pkg
.desired
->binpicked
))
552 e
+= install_one (pkg
);
558 if (yesno (owner
, IDS_INSTALL_ERROR
, e
->what()) != IDYES
)
560 log (LOG_TIMESTAMP
, String ("User cancelled setup after install error"));
566 } // end of big package loop
569 note (owner
, IDS_REBOOT_REQUIRED
);
572 if ((temperr
= db
.flush ()))
574 const char *err
= strerror (temperr
);
576 err
= "(unknown error)";
577 fatal (owner
, IDS_ERR_OPEN_WRITE
, "Package Database",
582 check_for_old_cygwin ();
583 if (num_installs
== 0 && num_uninstalls
== 0)
585 exit_msg
= IDS_NOTHING_INSTALLED
;
588 if (num_installs
== 0)
590 exit_msg
= IDS_UNINSTALL_COMPLETE
;
595 exit_msg
= IDS_INSTALL_INCOMPLETE
;
597 exit_msg
= IDS_INSTALL_COMPLETE
;
601 do_install_reflector (void *p
)
604 context
= (HANDLE
*) p
;
606 do_install_thread ((HINSTANCE
) context
[0], (HWND
) context
[1]);
608 // Tell the progress page that we're done downloading
609 Progress
.PostMessage (WM_APP_INSTALL_THREAD_COMPLETE
);
614 static HANDLE context
[2];
617 do_install (HINSTANCE h
, HWND owner
)
623 CreateThread (NULL
, 0, do_install_reflector
, context
, 0, &threadID
);