]> cygwin.com Git - cygwin-apps/setup.git/blob - install.cc
2002-05-02 Robert Collins <rbtcollins@hotmail.com>
[cygwin-apps/setup.git] / install.cc
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
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. */
21
22 #if 0
23 static const char *cvsid = "\n%%% $Id$\n";
24 #endif
25
26 #include "win32.h"
27 #include "commctrl.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <errno.h>
35 #include <process.h>
36
37 #include "zlib/zlib.h"
38
39 #include "resource.h"
40 #include "ini.h"
41 #include "dialog.h"
42 #include "geturl.h"
43 #include "state.h"
44 #include "diskfull.h"
45 #include "msg.h"
46 #include "mount.h"
47 #include "log.h"
48 #include "mount.h"
49 #include "filemanip.h"
50 #include "io_stream.h"
51 #include "compress.h"
52 #include "compress_gz.h"
53 #include "archive.h"
54 #include "archive_tar.h"
55 #include "script.h"
56
57 #include "package_db.h"
58 #include "package_meta.h"
59 #include "package_version.h"
60 #include "package_source.h"
61
62 #include "port.h"
63
64 #include "threebar.h"
65
66 #include "md5.h"
67
68 #include "Exception.h"
69
70 extern ThreeBarProgressPage Progress;
71
72 static int total_bytes = 0;
73 static int total_bytes_sofar = 0;
74 static int package_bytes = 0;
75
76 static void
77 init_dialog ()
78 {
79 Progress.SetText2 ("");
80 Progress.SetText3 ("");
81 }
82
83 static void
84 progress (int bytes)
85 {
86 if (package_bytes > 0)
87 {
88 Progress.SetBar1 (bytes, package_bytes);
89 }
90
91 if (total_bytes > 0)
92 {
93 Progress.SetBar2 (total_bytes_sofar + bytes, total_bytes);
94 }
95 }
96
97 static const char *standard_dirs[] = {
98 "/bin",
99 "/etc",
100 "/lib",
101 "/tmp",
102 "/usr",
103 "/usr/bin",
104 "/usr/lib",
105 "/usr/src",
106 "/usr/local",
107 "/usr/local/bin",
108 "/usr/local/etc",
109 "/usr/local/lib",
110 "/usr/tmp",
111 "/var/run",
112 "/var/tmp",
113 0
114 };
115
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;
122
123 /* FIXME: upgrades should be a method too */
124 static void
125 uninstall_one (packagemeta & pkgm)
126 {
127 Progress.SetText1 ("Uninstalling...");
128 Progress.SetText2 (pkgm.name.cstr_oneuse());
129 log (LOG_PLAIN, String("Uninstalling ") + pkgm.name);
130 pkgm.uninstall ();
131 num_uninstalls++;
132 }
133
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.
139 */
140 static int
141 replace_one (packagemeta & pkg)
142 {
143 int errors = 0;
144 Progress.SetText1 ("Replacing...");
145 Progress.SetText2 (pkg.name.cstr_oneuse());
146 log (LOG_PLAIN, String( "Replacing ") + pkg.name);
147 pkg.uninstall ();
148
149 errors +=
150 install_one_source (pkg, pkg.desired->bin, "cygfile://","/", package_binary);
151 if (!errors)
152 pkg.installed = pkg.desired;
153 num_replacements++;
154 return errors;
155 }
156
157
158 /* install one source at a given prefix. */
159 static int
160 install_one_source (packagemeta & pkgm, packagesource & source,
161 String const &prefixURL, String const &prefixPath, package_type_t type)
162 {
163 int errors = 0;
164 Progress.SetText2 (source.Base ());
165 if (!source.Cached () || !io_stream::exists (source.Cached ()))
166 {
167 note (NULL, IDS_ERR_OPEN_READ, source.Cached (), "No such file");
168 return 1;
169 }
170 io_stream *lst = 0;
171 if (type == package_binary)
172 {
173 io_stream *tmp =
174 io_stream::
175 open (String ("cygfile:///etc/setup/") + pkgm.name + ".lst.gz",
176 "wb");
177 lst = new compress_gz (tmp, "w9");
178 if (lst->error ())
179 {
180 delete lst;
181 lst = NULL;
182 }
183 }
184
185 package_bytes = source.size;
186
187 char msg[64];
188 strcpy (msg, "Installing");
189 Progress.SetText1 (msg);
190 log (LOG_PLAIN, String (msg) + " " + source.Cached ());
191
192 if (source.md5.isSet())
193 {
194 // check the MD5 sum of the cached file here
195 io_stream *thefile = io_stream::open (source.Cached (), "rb");
196 if (!thefile)
197 return 0;
198 md5_state_t pns;
199 md5_init (&pns);
200
201 unsigned char buffer[16384];
202 ssize_t count;
203 while ((count = thefile->read (buffer, 16384)) > 0)
204 md5_append (&pns, buffer, count);
205 delete thefile;
206 if (count < 0)
207 throw new Exception ("__LINE__ __FILE__", (String ("IO Error reading ") + source.Cached()).cstr_oneuse(), APPERR_IO_ERROR);
208
209 md5_byte_t tempdigest[16];
210 md5_finish(&pns, tempdigest);
211 md5 tempMD5;
212 tempMD5.set (tempdigest);
213
214 log (LOG_BABBLE, String ("For file") + source.Cached() + " ini digest is" + source.md5.print() + " file digest is " + tempMD5.print());
215
216 if (source.md5 != tempMD5)
217 throw new Exception ("__LINE__ __FILE__", (String ("Checksum failure for ") + source.Cached()).cstr_oneuse(), APPERR_CORRUPT_PACKAGE);
218 }
219 io_stream *tmp = io_stream::open (source.Cached (), "rb");
220 archive *thefile = 0;
221 if (tmp)
222 {
223 io_stream *tmp2 = compress::decompress (tmp);
224 if (tmp2)
225 thefile = archive::extract (tmp2);
226 else
227 thefile = archive::extract (tmp);
228 }
229 /* FIXME: potential leak of either *tmp or *tmp2 */
230 if (thefile)
231 {
232 String fn;
233 while ((fn = thefile->next_file_name ()).size())
234 {
235 if (lst)
236 {
237 String tmp=fn + "\n";
238 lst->write (tmp.cstr_oneuse(), tmp.size());
239 }
240
241 String canonicalfn = prefixPath + fn;
242
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)
246 {
247 //extract to temp location
248 if (archive::extract_file (thefile, prefixURL, prefixPath, ".new") != 0)
249 {
250 log (LOG_PLAIN,
251 String("Unable to install file ") +
252 prefixURL + prefixPath + fn);
253 errors++;
254 }
255 else
256 //switch Win32::OS
257 {
258 switch (Win32::OS ())
259 {
260 case Win32::Win9x:{
261 /* Get the short file names */
262 char source[MAX_PATH];
263 unsigned int len =
264 GetShortPathName (cygpath (String ("/") + fn +
265 ".new").cstr_oneuse(),
266 source, MAX_PATH);
267 if (!len || len > MAX_PATH)
268 {
269 log (LOG_TIMESTAMP,
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(),
274 GetLastError ());
275 ++errors;
276 }
277 else
278 {
279 char dest[MAX_PATH];
280 len =
281 GetShortPathName (cygpath (String ("/") +
282 fn).cstr_oneuse(),
283 dest, MAX_PATH);
284 if (!len || len > MAX_PATH)
285 {
286 log (LOG_TIMESTAMP,
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(),
291 GetLastError ());
292 ++errors;
293
294 }
295 else
296 /* trigger a replacement on reboot */
297 if (!WritePrivateProfileString
298 ("rename", dest, source, "WININIT.INI"))
299 {
300 log (LOG_TIMESTAMP,
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(),
305 GetLastError ());
306 ++errors;
307 }
308
309 }
310 }
311 break;
312 case Win32::WinNT:
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
317 */
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))
323 {
324 log (LOG_TIMESTAMP,
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(),
328 GetLastError ());
329 ++errors;
330 }
331 else
332 rebootneeded = true;
333 break;
334 }
335 }
336 }
337
338 progress (tmp->tell ());
339 num_installs++;
340 }
341 delete thefile;
342
343 total_bytes_sofar += package_bytes;
344 }
345
346
347 progress (0);
348
349 int df = diskfull (get_root_dir ().cstr_oneuse());
350 Progress.SetBar3 (df);
351
352 if (lst)
353 delete lst;
354
355 return errors;
356 }
357
358 /* install a package, install both the binary and source aspects if needed */
359 static int
360 install_one (packagemeta & pkg)
361 {
362 int errors = 0;
363
364 if (pkg.installed != pkg.desired && pkg.desired->binpicked)
365 {
366 errors +=
367 install_one_source (pkg, pkg.desired->bin, "cygfile://","/",
368 package_binary);
369 if (!errors)
370 pkg.installed = pkg.desired;
371 }
372 if (pkg.desired->srcpicked)
373 errors +=
374 install_one_source (pkg, pkg.desired->src, "cygfile://","/usr/src/",
375 package_source);
376
377 /* FIXME: make a upgrade method and reinstate this */
378 #if 0
379 String msg;
380 if (!pkg->installed)
381 msg = "Installing";
382 else
383 {
384 int n = strcmp (pi->version, pkg->installed->version);
385 if (n < 0)
386 msg = "Reverting";
387 else if (n == 0)
388 msg = "Reinstalling";
389 else
390 msg = "Upgrading";
391 }
392
393 switch (pkg->action)
394 {
395 case ACTION_PREV:
396 msg += " previous version...";
397 break;
398 case ACTION_CURR:
399 msg += "...";
400 break;
401 case ACTION_TEST:
402 msg += " test version...";
403 break;
404 default:
405 /* FIXME: log this somehow */
406 break;
407 }
408 SetWindowText (ins_action, msg.cstr_oneuse());
409 log (LOG_PLAIN, msg + " " + file);
410 #endif
411
412 return errors;
413 }
414
415 static void
416 check_for_old_cygwin ()
417 {
418 char buf[_MAX_PATH + sizeof ("\\cygwin1.dll")];
419 if (!GetSystemDirectory (buf, sizeof (buf)))
420 return;
421 strcat (buf, "\\cygwin1.dll");
422 if (_access (buf, 0) != 0)
423 return;
424
425 char msg[sizeof (buf) + 132];
426 sprintf (msg,
427 "An old version of cygwin1.dll was found here:\r\n%s\r\nDelete?",
428 buf);
429 switch (MessageBox
430 (NULL, msg, "What's that doing there?",
431 MB_YESNO | MB_ICONQUESTION | MB_TASKMODAL))
432 {
433 case IDYES:
434 if (!DeleteFile (buf))
435 {
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);
442 }
443 break;
444 default:
445 break;
446 }
447
448 return;
449 }
450
451 static void
452 do_install_thread (HINSTANCE h, HWND owner)
453 {
454 int i;
455 int errors = 0;
456
457 num_installs = 0, num_uninstalls = 0, num_replacements = 0;
458 rebootneeded = false;
459
460 next_dialog = IDD_DESKTOP;
461
462 io_stream::mkpath_p (PATH_TO_DIR, String ("file://") + get_root_dir ());
463
464 for (i = 0; standard_dirs[i]; i++)
465 {
466 String p = cygpath (standard_dirs[i]);
467 if (p.size())
468 io_stream::mkpath_p (PATH_TO_DIR, String ("file://") + p);
469 }
470
471 /* Create /var/run/utmp */
472 io_stream *utmp = io_stream::open ("cygfile:///var/run/utmp", "wb");
473 delete utmp;
474
475 init_dialog ();
476
477 total_bytes = 0;
478 total_bytes_sofar = 0;
479
480 int df = diskfull (get_root_dir ().cstr_oneuse());
481 Progress.SetBar3 (df);
482
483 int istext = (root_text == IDC_ROOT_TEXT) ? 1 : 0;
484 int issystem = (root_scope == IDC_ROOT_SYSTEM) ? 1 : 0;
485
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);
490
491 /* Let's hope people won't uninstall packages before installing [b]ash */
492 init_run_script ();
493
494 packagedb db;
495 for (size_t n = 1; n <= db.packages.number (); n++)
496 {
497 packagemeta & pkg = *db.packages[n];
498
499 if (pkg.desired && (pkg.desired->srcpicked || pkg.desired->binpicked))
500 {
501 if (pkg.desired->srcpicked)
502 total_bytes += pkg.desired->src.size;
503 if (pkg.desired->binpicked)
504 total_bytes += pkg.desired->bin.size;
505 }
506 }
507
508 /* start with uninstalls - remove files that new packages may replace */
509 for (size_t n = 1; n <= db.packages.number (); n++)
510 {
511 packagemeta & pkg = *db.packages[n];
512 if (pkg.installed && (!pkg.desired || pkg.desired != pkg.installed))
513 uninstall_one (pkg);
514 }
515
516 /* now in-place binary upgrades/reinstalls, as these may remove fils
517 * that have been moved into new packages
518 */
519
520 for (size_t n = 1; n <= db.packages.number (); n++)
521 {
522 packagemeta & pkg = *db.packages[n];
523 if (pkg.installed && pkg.desired && pkg.desired->binpicked)
524 {
525 try {
526 int e = 0;
527 e += replace_one (pkg);
528 if (e)
529 errors++;
530 }
531 catch (exception *e)
532 {
533 if (yesno (owner, IDS_INSTALL_ERROR, e->what()) != IDYES)
534 {
535 log (LOG_TIMESTAMP, String ("User cancelled setup after install error"));
536 exit_setup (1);
537 return;
538 }
539 }
540 }
541 }
542
543 for (size_t n = 1; n <= db.packages.number (); n++)
544 {
545 packagemeta & pkg = *db.packages[n];
546
547 if (pkg.desired && (pkg.desired->srcpicked || pkg.desired->binpicked))
548 {
549 try
550 {
551 int e = 0;
552 e += install_one (pkg);
553 if (e)
554 errors++;
555 }
556 catch (exception *e)
557 {
558 if (yesno (owner, IDS_INSTALL_ERROR, e->what()) != IDYES)
559 {
560 log (LOG_TIMESTAMP, String ("User cancelled setup after install error"));
561 exit_setup (1);
562 return;
563 }
564 }
565 }
566 } // end of big package loop
567
568 if (rebootneeded)
569 note (owner, IDS_REBOOT_REQUIRED);
570
571 int temperr;
572 if ((temperr = db.flush ()))
573 {
574 const char *err = strerror (temperr);
575 if (!err)
576 err = "(unknown error)";
577 fatal (owner, IDS_ERR_OPEN_WRITE, "Package Database",
578 err);
579 }
580
581 if (!errors)
582 check_for_old_cygwin ();
583 if (num_installs == 0 && num_uninstalls == 0)
584 {
585 exit_msg = IDS_NOTHING_INSTALLED;
586 return;
587 }
588 if (num_installs == 0)
589 {
590 exit_msg = IDS_UNINSTALL_COMPLETE;
591 return;
592 }
593
594 if (errors)
595 exit_msg = IDS_INSTALL_INCOMPLETE;
596 else
597 exit_msg = IDS_INSTALL_COMPLETE;
598 }
599
600 static DWORD WINAPI
601 do_install_reflector (void *p)
602 {
603 HANDLE *context;
604 context = (HANDLE *) p;
605
606 do_install_thread ((HINSTANCE) context[0], (HWND) context[1]);
607
608 // Tell the progress page that we're done downloading
609 Progress.PostMessage (WM_APP_INSTALL_THREAD_COMPLETE);
610
611 ExitThread (0);
612 }
613
614 static HANDLE context[2];
615
616 void
617 do_install (HINSTANCE h, HWND owner)
618 {
619 context[0] = h;
620 context[1] = owner;
621
622 DWORD threadID;
623 CreateThread (NULL, 0, do_install_reflector, context, 0, &threadID);
624 }
This page took 0.062321 seconds and 5 git commands to generate.