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