]> cygwin.com Git - cygwin-apps/setup.git/blame - install.cc
2002-02-19 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"
40#include "ini.h"
41#include "dialog.h"
23c9e63c 42#include "geturl.h"
23c9e63c 43#include "state.h"
23c9e63c
DD
44#include "diskfull.h"
45#include "msg.h"
46#include "mount.h"
89b1a15b 47#include "log.h"
a351e48c 48#include "mount.h"
c46a33a9 49#include "filemanip.h"
b24c88b3
RC
50#include "io_stream.h"
51#include "compress.h"
52#include "compress_gz.h"
53#include "archive.h"
54#include "archive_tar.h"
8e9aa511 55#include "script.h"
4a83b7b0 56
fa0c0d10
RC
57#include "package_db.h"
58#include "package_meta.h"
bb849dbd
RC
59#include "package_version.h"
60#include "package_source.h"
fa0c0d10 61
4a83b7b0 62#include "port.h"
23c9e63c 63
ab57ceaa
RC
64#include "threebar.h"
65extern ThreeBarProgressPage Progress;
23c9e63c
DD
66
67static int total_bytes = 0;
68static int total_bytes_sofar = 0;
69static int package_bytes = 0;
70
23c9e63c
DD
71static void
72init_dialog ()
73{
ab57ceaa
RC
74 Progress.SetText2 ("");
75 Progress.SetText3 ("");
23c9e63c
DD
76}
77
78static void
79progress (int bytes)
80{
ab57ceaa 81 if (package_bytes > 0)
23c9e63c 82 {
ab57ceaa 83 Progress.SetBar1 (bytes, package_bytes);
23c9e63c
DD
84 }
85
ab57ceaa 86 if (total_bytes > 0)
23c9e63c 87 {
ab57ceaa 88 Progress.SetBar2 (total_bytes_sofar + bytes, total_bytes);
23c9e63c 89 }
23c9e63c
DD
90}
91
b24c88b3 92static const char *standard_dirs[] = {
1a18aed7 93 "/bin",
904d24fe 94 "/etc",
1a18aed7 95 "/lib",
904d24fe 96 "/tmp",
4a83b7b0 97 "/usr",
1a18aed7
DD
98 "/usr/bin",
99 "/usr/lib",
ef45c299 100 "/usr/src",
4a83b7b0 101 "/usr/local",
1a18aed7
DD
102 "/usr/local/bin",
103 "/usr/local/etc",
104 "/usr/local/lib",
904d24fe 105 "/usr/tmp",
1a18aed7 106 "/var/run",
904d24fe
DD
107 "/var/tmp",
108 0
109};
110
ad3c7385
RC
111static int num_installs, num_replacements, num_uninstalls;
112static void uninstall_one (packagemeta &);
113static int replace_one (packagemeta &);
3c054baf
RC
114static int install_one_source (packagemeta &, packagesource &, String const &,
115 String const &, package_type_t);
ad3c7385 116static bool rebootneeded;
3b9077d4 117
fa0c0d10 118/* FIXME: upgrades should be a method too */
3b9077d4 119static void
cbfc4215 120uninstall_one (packagemeta & pkgm)
3b9077d4 121{
ab57ceaa 122 Progress.SetText1 ("Uninstalling...");
3c054baf 123 Progress.SetText2 (pkgm.name.cstr_oneuse());
1ac649ed 124 log (LOG_PLAIN, String("Uninstalling ") + pkgm.name);
ab57ceaa
RC
125 pkgm.uninstall ();
126 num_uninstalls++;
3b9077d4
DD
127}
128
ad3c7385
RC
129/* uninstall and install a package, preserving configuration
130 * files and the like.
131 * This method should also know about replacing in-use file.
132 * ASSUMPTIONS: pkgm is installed.
133 * pkgm has a desired package.
134 */
135static int
136replace_one (packagemeta & pkg)
137{
138 int errors = 0;
139 Progress.SetText1 ("Replacing...");
3c054baf 140 Progress.SetText2 (pkg.name.cstr_oneuse());
1ac649ed 141 log (LOG_PLAIN, String( "Replacing ") + pkg.name);
ad3c7385
RC
142 pkg.uninstall ();
143
144 errors +=
6dc75764 145 install_one_source (pkg, pkg.desired->bin, "cygfile://","/", package_binary);
ad3c7385
RC
146 if (!errors)
147 pkg.installed = pkg.desired;
148 num_replacements++;
149 return errors;
150}
42bf5b92 151
bb849dbd
RC
152
153/* install one source at a given prefix. */
3b9077d4 154static int
bb849dbd 155install_one_source (packagemeta & pkgm, packagesource & source,
3c054baf 156 String const &prefixURL, String const &prefixPath, package_type_t type)
3b9077d4
DD
157{
158 int errors = 0;
ab57ceaa 159 Progress.SetText2 (source.Base ());
bb849dbd 160 if (!io_stream::exists (source.Cached ()))
c46a33a9 161 {
ab57ceaa 162 note (NULL, IDS_ERR_OPEN_READ, source.Cached (), "No such file");
bb849dbd 163 return 1;
c46a33a9 164 }
bb849dbd
RC
165 io_stream *lst = 0;
166 if (type == package_binary)
c46a33a9 167 {
bb849dbd
RC
168 io_stream *tmp =
169 io_stream::
3c054baf 170 open (String ("cygfile:///etc/setup/") + pkgm.name + ".lst.gz",
bb849dbd
RC
171 "wb");
172 lst = new compress_gz (tmp, "w9");
173 if (lst->error ())
174 {
175 delete lst;
176 lst = NULL;
177 }
c46a33a9 178 }
b24c88b3 179
bb849dbd 180 package_bytes = source.size;
3b9077d4 181
bb849dbd
RC
182 char msg[64];
183 strcpy (msg, "Installing");
ab57ceaa 184 Progress.SetText1 (msg);
1ac649ed 185 log (LOG_PLAIN, String (msg) + " " + source.Cached ());
bb849dbd
RC
186 io_stream *tmp = io_stream::open (source.Cached (), "rb");
187 archive *thefile = 0;
188 if (tmp)
d4a4527d 189 {
bb849dbd
RC
190 io_stream *tmp2 = compress::decompress (tmp);
191 if (tmp2)
192 thefile = archive::extract (tmp2);
193 else
194 thefile = archive::extract (tmp);
3b9077d4 195 }
bb849dbd
RC
196 /* FIXME: potential leak of either *tmp or *tmp2 */
197 if (thefile)
d4a4527d 198 {
3c054baf
RC
199 String fn;
200 while ((fn = thefile->next_file_name ()).size())
bb849dbd
RC
201 {
202 if (lst)
3c054baf
RC
203 {
204 String tmp=fn + "\n";
205 lst->write (tmp.cstr_oneuse(), tmp.size() + 1);
206 }
207
208 String canonicalfn = prefixPath + fn;
bb849dbd 209
3c054baf
RC
210 Progress.SetText3 (canonicalfn.cstr_oneuse());
211 log (LOG_BABBLE, String("Installing file ") + prefixURL + prefixPath + fn);
6dc75764 212 if (archive::extract_file (thefile, prefixURL, prefixPath) != 0)
bb849dbd 213 {
ad3c7385 214 //extract to temp location
6dc75764 215 if (archive::extract_file (thefile, prefixURL, prefixPath, ".new") != 0)
ad3c7385 216 {
1ac649ed
RC
217 log (LOG_PLAIN,
218 String("Unable to install file ") +
219 prefixURL + prefixPath + fn);
ad3c7385
RC
220 errors++;
221 }
222 else
223 //switch Win32::OS
224 {
225 switch (Win32::OS ())
226 {
227 case Win32::Win9x:{
228 /* Get the short file names */
229 char source[MAX_PATH];
230 unsigned int len =
1ac649ed
RC
231 GetShortPathName (cygpath (String ("/") + fn +
232 ".new").cstr_oneuse(),
ad3c7385
RC
233 source, MAX_PATH);
234 if (!len || len > MAX_PATH)
235 {
3c054baf 236 log (LOG_TIMESTAMP,
ad3c7385 237 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
1ac649ed
RC
238 cygpath (String ("/") + fn).cstr_oneuse(),
239 cygpath (String ("/") + fn +
240 ".new").cstr_oneuse(),
ad3c7385
RC
241 GetLastError ());
242 ++errors;
243 }
244 else
245 {
246 char dest[MAX_PATH];
247 len =
1ac649ed
RC
248 GetShortPathName (cygpath (String ("/") +
249 fn).cstr_oneuse(),
250 dest, MAX_PATH);
ad3c7385
RC
251 if (!len || len > MAX_PATH)
252 {
3c054baf 253 log (LOG_TIMESTAMP,
ad3c7385 254 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
1ac649ed
RC
255 cygpath (String ("/") + fn).cstr_oneuse(),
256 cygpath (String ("/") + fn +
257 ".new").cstr_oneuse(),
ad3c7385
RC
258 GetLastError ());
259 ++errors;
260
261 }
262 else
263 /* trigger a replacement on reboot */
264 if (!WritePrivateProfileString
265 ("rename", dest, source, "WININIT.INI"))
266 {
3c054baf 267 log (LOG_TIMESTAMP,
ad3c7385 268 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
1ac649ed
RC
269 cygpath (String ("/") + fn).cstr_oneuse(),
270 cygpath (String ("/") + fn +
271 ".new").cstr_oneuse(),
ad3c7385
RC
272 GetLastError ());
273 ++errors;
274 }
275
276 }
277 }
278 break;
279 case Win32::WinNT:
280 /* XXX FIXME: prefix may not be / for in use files -
281 * although it most likely is
282 * - we need a io method to get win32 paths
283 * or to wrap this system call
284 */
1ac649ed
RC
285 if (!MoveFileEx (cygpath (String ("/") + fn +
286 ".new").cstr_oneuse(),
287 cygpath (String ("/") + fn).cstr_oneuse(),
ad3c7385
RC
288 MOVEFILE_DELAY_UNTIL_REBOOT |
289 MOVEFILE_REPLACE_EXISTING))
290 {
3c054baf 291 log (LOG_TIMESTAMP,
ad3c7385 292 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
1ac649ed
RC
293 cygpath (String ("/") + fn).cstr_oneuse(),
294 cygpath (String ("/") + fn + ".new").cstr_oneuse(),
ad3c7385
RC
295 GetLastError ());
296 ++errors;
297 }
298 else
299 rebootneeded = true;
300 break;
301 }
302 }
bb849dbd
RC
303 }
304
305 progress (tmp->tell ());
306 num_installs++;
307 }
308 delete thefile;
309
310 total_bytes_sofar += package_bytes;
d4a4527d 311 }
3b9077d4 312
90d14922 313
bb849dbd
RC
314 progress (0);
315
3c054baf 316 int df = diskfull (get_root_dir ().cstr_oneuse());
ab57ceaa 317 Progress.SetBar3 (df);
bb849dbd
RC
318
319 if (lst)
320 delete lst;
321
322 return errors;
323}
324
325/* install a package, install both the binary and source aspects if needed */
326static int
327install_one (packagemeta & pkg)
328{
329 int errors = 0;
330
ad3c7385 331 if (pkg.installed != pkg.desired && pkg.desired->binpicked)
ab57ceaa 332 {
bb849dbd 333 errors +=
6dc75764 334 install_one_source (pkg, pkg.desired->bin, "cygfile://","/",
ab57ceaa
RC
335 package_binary);
336 if (!errors)
337 pkg.installed = pkg.desired;
338 }
339 if (pkg.desired->srcpicked)
340 errors +=
6dc75764 341 install_one_source (pkg, pkg.desired->src, "cygfile://","/usr/src",
ab57ceaa 342 package_source);
bb849dbd
RC
343
344 /* FIXME: make a upgrade method and reinstate this */
345#if 0
1ac649ed 346 String msg;
c46a33a9 347 if (!pkg->installed)
1ac649ed 348 msg = "Installing";
c46a33a9
CF
349 else
350 {
351 int n = strcmp (pi->version, pkg->installed->version);
352 if (n < 0)
1ac649ed 353 msg = "Reverting";
c46a33a9 354 else if (n == 0)
1ac649ed 355 msg = "Reinstalling";
c46a33a9 356 else
1ac649ed 357 msg = "Upgrading";
c46a33a9
CF
358 }
359
360 switch (pkg->action)
3b9077d4 361 {
c46a33a9 362 case ACTION_PREV:
1ac649ed 363 msg += " previous version...";
3b9077d4 364 break;
c46a33a9 365 case ACTION_CURR:
1ac649ed 366 msg += "...";
3b9077d4 367 break;
c46a33a9 368 case ACTION_TEST:
1ac649ed 369 msg += " test version...";
42bf5b92 370 break;
b24c88b3
RC
371 default:
372 /* FIXME: log this somehow */
373 break;
3b9077d4 374 }
1ac649ed
RC
375 SetWindowText (ins_action, msg.cstr_oneuse());
376 log (LOG_PLAIN, msg + " " + file);
bb849dbd 377#endif
c46a33a9 378
3b9077d4
DD
379 return errors;
380}
381
72fd1d1e
CF
382static void
383check_for_old_cygwin ()
384{
385 char buf[_MAX_PATH + sizeof ("\\cygwin1.dll")];
386 if (!GetSystemDirectory (buf, sizeof (buf)))
387 return;
388 strcat (buf, "\\cygwin1.dll");
389 if (_access (buf, 0) != 0)
390 return;
391
392 char msg[sizeof (buf) + 132];
b24c88b3
RC
393 sprintf (msg,
394 "An old version of cygwin1.dll was found here:\r\n%s\r\nDelete?",
395 buf);
396 switch (MessageBox
397 (NULL, msg, "What's that doing there?",
398 MB_YESNO | MB_ICONQUESTION | MB_TASKMODAL))
72fd1d1e
CF
399 {
400 case IDYES:
401 if (!DeleteFile (buf))
402 {
403 sprintf (msg, "Couldn't delete file %s.\r\n"
b24c88b3 404 "Is the DLL in use by another application?\r\n"
7c7034e8
RC
405 "You should delete the old version of cygwin1.dll\r\n"
406 "at your earliest convenience.", buf);
b24c88b3
RC
407 MessageBox (NULL, buf, "Couldn't delete file",
408 MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
72fd1d1e
CF
409 }
410 break;
411 default:
b24c88b3 412 break;
72fd1d1e
CF
413 }
414
415 return;
416}
417
ab57ceaa
RC
418static void
419do_install_thread (HINSTANCE h, HWND owner)
23c9e63c 420{
3b9077d4 421 int i;
2a1a01e0
DD
422 int errors = 0;
423
ad3c7385
RC
424 num_installs = 0, num_uninstalls = 0, num_replacements = 0;
425 rebootneeded = false;
3b9077d4 426
50225eae 427 next_dialog = IDD_DESKTOP;
23c9e63c 428
3c054baf 429 io_stream::mkpath_p (PATH_TO_DIR, get_root_dir ());
904d24fe 430
b24c88b3 431 for (i = 0; standard_dirs[i]; i++)
904d24fe 432 {
1ac649ed 433 String p = cygpath (standard_dirs[i]);
3c054baf
RC
434 if (p.size())
435 io_stream::mkpath_p (PATH_TO_DIR, p);
904d24fe
DD
436 }
437
1a18aed7 438 /* Create /var/run/utmp */
b24c88b3
RC
439 io_stream *utmp = io_stream::open ("cygfile:///var/run/utmp", "wb");
440 delete utmp;
1a18aed7 441
23c9e63c
DD
442 init_dialog ();
443
444 total_bytes = 0;
445 total_bytes_sofar = 0;
446
3c054baf 447 int df = diskfull (get_root_dir ().cstr_oneuse());
ab57ceaa 448 Progress.SetBar3 (df);
d07591a3 449
0af2d779
CF
450 int istext = (root_text == IDC_ROOT_TEXT) ? 1 : 0;
451 int issystem = (root_scope == IDC_ROOT_SYSTEM) ? 1 : 0;
452
1ac649ed
RC
453 create_mount ("/", get_root_dir (), istext, issystem);
454 create_mount ("/usr/bin", cygpath ("/bin"), istext, issystem);
455 create_mount ("/usr/lib", cygpath ("/lib"), istext, issystem);
0af2d779
CF
456 set_cygdrive_flags (istext, issystem);
457
8e9aa511
RC
458 /* Let's hope people won't uninstall packages before installing [b]ash */
459 init_run_script ();
460
bb849dbd 461 packagedb db;
bc78a6d5 462 for (size_t n = 1; n <= db.packages.number (); n++)
cbfc4215 463 {
ab57ceaa
RC
464 packagemeta & pkg = *db.packages[n];
465
466 if (pkg.desired && (pkg.desired->srcpicked || pkg.desired->binpicked))
467 {
468 if (pkg.desired->srcpicked)
469 total_bytes += pkg.desired->src.size;
470 if (pkg.desired->binpicked)
471 total_bytes += pkg.desired->bin.size;
472 }
cbfc4215 473 }
bb849dbd 474
ad3c7385 475 /* start with uninstalls - remove files that new packages may replace */
bc78a6d5 476 for (size_t n = 1; n <= db.packages.number (); n++)
23c9e63c 477 {
ab57ceaa 478 packagemeta & pkg = *db.packages[n];
cbfc4215 479 if (pkg.installed && (!pkg.desired || pkg.desired != pkg.installed))
ad3c7385
RC
480 uninstall_one (pkg);
481 }
482
483 /* now in-place binary upgrades/reinstalls, as these may remove fils
484 * that have been moved into new packages
485 */
486
487 for (size_t n = 1; n <= db.packages.number (); n++)
488 {
489 packagemeta & pkg = *db.packages[n];
490 if (pkg.installed && pkg.desired && pkg.desired->binpicked)
4a83b7b0 491 {
ad3c7385
RC
492 int e = 0;
493 e += replace_one (pkg);
494 if (e)
495 errors++;
4a83b7b0 496 }
ad3c7385
RC
497 }
498
499 for (size_t n = 1; n <= db.packages.number (); n++)
500 {
501 packagemeta & pkg = *db.packages[n];
23c9e63c 502
ab57ceaa 503 if (pkg.desired && (pkg.desired->srcpicked || pkg.desired->binpicked))
23c9e63c 504 {
c46a33a9 505 int e = 0;
cbfc4215 506 e += install_one (pkg);
3b9077d4 507 if (e)
40aef45e 508 {
3b9077d4 509 errors++;
40aef45e 510 }
4a83b7b0 511 }
b24c88b3 512 } // end of big package loop
4a83b7b0 513
ad3c7385
RC
514 if (rebootneeded)
515 note (owner, IDS_REBOOT_REQUIRED);
516
7c7034e8
RC
517 int temperr;
518 if ((temperr = db.flush ()))
23c9e63c 519 {
7c7034e8 520 const char *err = strerror (temperr);
23c9e63c
DD
521 if (!err)
522 err = "(unknown error)";
3c054baf
RC
523 fatal (owner, IDS_ERR_OPEN_WRITE, "Package Database",
524 err);
23c9e63c
DD
525 }
526
72fd1d1e
CF
527 if (!errors)
528 check_for_old_cygwin ();
4a83b7b0
DD
529 if (num_installs == 0 && num_uninstalls == 0)
530 {
531 exit_msg = IDS_NOTHING_INSTALLED;
532 return;
533 }
534 if (num_installs == 0)
535 {
536 exit_msg = IDS_UNINSTALL_COMPLETE;
537 return;
538 }
539
2a1a01e0
DD
540 if (errors)
541 exit_msg = IDS_INSTALL_INCOMPLETE;
542 else
543 exit_msg = IDS_INSTALL_COMPLETE;
23c9e63c 544}
ab57ceaa
RC
545
546static void
547do_install_reflector (void *p)
548{
549 HANDLE *context;
550 context = (HANDLE *) p;
551
552 do_install_thread ((HINSTANCE) context[0], (HWND) context[1]);
553
554 // Tell the progress page that we're done downloading
555 Progress.PostMessage (WM_APP_INSTALL_THREAD_COMPLETE);
556
557 _endthread ();
558}
559
560static HANDLE context[2];
561
562void
563do_install (HINSTANCE h, HWND owner)
564{
565 context[0] = h;
566 context[1] = owner;
567
568 _beginthread (do_install_reflector, 0, context);
569}
This page took 0.095398 seconds and 5 git commands to generate.