Regenerate Japanese resources
[cygwin-setup.git] / install.cc
blob628dbd0cbf15b851de92f87d5f50bb50ffe4bd13
1 /*
2 * Copyright (c) 2000, Red Hat, Inc.
3 * Copyright (c) 2003, Robert Collins <rbtcollins@hotmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * A copy of the GNU General Public License can be found at
11 * http://www.gnu.org/
13 * Originally Written by DJ Delorie <dj@cygnus.com>
17 /* The purpose of this file is to install all the packages selected in
18 the install list (in ini.h). Note that we use a separate thread to
19 maintain the progress dialog, so we avoid the complexity of
20 handling two tasks in one thread. We also create or update all the
21 files in /etc/setup/\* and create the mount points. */
23 #include "getopt++/BoolOption.h"
24 #include "LogFile.h"
26 #include "win32.h"
27 #include "commctrl.h"
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 #include <algorithm>
38 #include "ini.h"
39 #include "resource.h"
40 #include "dialog.h"
41 #include "geturl.h"
42 #include "state.h"
43 #include "diskfull.h"
44 #include "msg.h"
45 #include "mount.h"
46 #include "mount.h"
47 #include "filemanip.h"
48 #include "io_stream.h"
49 #include "compress.h"
50 #include "compress_gz.h"
51 #include "archive.h"
52 #include "archive_tar.h"
53 #include "script.h"
54 #include "find.h"
55 #include "FindVisitor.h"
57 #include "package_db.h"
58 #include "package_meta.h"
59 #include "package_version.h"
60 #include "package_source.h"
62 #include "threebar.h"
63 #include "Exception.h"
64 #include "processlist.h"
66 extern ThreeBarProgressPage Progress;
68 static off_t total_bytes = 0;
69 static off_t total_bytes_sofar = 0;
70 static off_t package_bytes = 0;
72 static BoolOption NoReplaceOnReboot (false, 'r', "no-replaceonreboot", IDS_HELPTEXT_NO_REPLACEONREBOOT);
73 static BoolOption NoWriteRegistry (false, '\0', "no-write-registry", IDS_HELPTEXT_NO_WRITE_REGISTRY);
75 struct std_dirs_t {
76 const char *name;
77 mode_t mode;
80 class PerpetualRemoveFindVisitor : public FindVisitor
82 public:
83 PerpetualRemoveFindVisitor (std::vector<Script> *scripts, const std::string& stratum)
84 : _scripts(scripts),
85 stratum(stratum)
87 virtual void visitFile(const std::string& basePath,
88 const WIN32_FIND_DATA *theFile)
90 std::string fn = std::string("/etc/preremove/") + theFile->cFileName;
91 Script script(fn);
92 if (script.is_p(stratum))
93 _scripts->push_back(Script (fn));
95 virtual ~ PerpetualRemoveFindVisitor () {}
96 protected:
97 PerpetualRemoveFindVisitor (PerpetualRemoveFindVisitor const &);
98 PerpetualRemoveFindVisitor & operator= (PerpetualRemoveFindVisitor const &);
99 private:
100 std::vector<Script> *_scripts;
101 const std::string stratum;
104 class Installer
106 public:
107 static std_dirs_t StandardDirs[];
108 Installer();
109 void initDialog();
110 void progress (off_t bytes);
111 void preremovePerpetual (const std::string& stratum);
112 void preremoveOne (packagemeta &);
113 void uninstallOne (packagemeta &);
114 void replaceOnRebootFailed (const std::string& fn);
115 void replaceOnRebootSucceeded (const std::string& fn, bool &rebootneeded);
116 void installOne (packagemeta &pkg, const packageversion &ver,
117 packagesource &source,
118 const std::string& , const std::string&, HWND );
119 int errors;
120 private:
121 bool extract_replace_on_reboot(archive *, const std::string&,
122 const std::string&, std::string);
123 bool _installOne (packagemeta &pkgm,
124 const std::string& prefixURL,
125 const std::string& prefixPath,
126 HWND owner,
127 io_stream *pkgfile,
128 archive *tarstream,
129 io_stream *lst,
130 bool symlink_phase);
133 Installer::Installer() : errors(0)
137 void
138 Installer::initDialog()
140 Progress.SetText2 ("");
141 Progress.SetText3 ("");
144 void
145 Installer::progress (off_t bytes)
147 if (package_bytes > 0)
148 Progress.SetBar1 (bytes, package_bytes);
150 if (total_bytes > 0)
151 Progress.SetBar2 (total_bytes_sofar + bytes, total_bytes);
154 std_dirs_t
155 Installer::StandardDirs[] = {
156 { "/bin", 0755 },
157 { "/dev", 0755 },
158 { "/dev/mqueue", 01777 },
159 { "/dev/shm", 01777 },
160 { "/etc", 0755 },
161 { "/etc/fstab.d", 01777 },
162 { "/home", 01777 },
163 { "/lib", 0755 },
164 { "/tmp", 01777 },
165 { "/usr", 0755 },
166 { "/usr/bin", 0755 },
167 { "/usr/lib", 0755 },
168 { "/usr/local", 0755 },
169 { "/usr/local/bin", 0755 },
170 { "/usr/local/etc", 0755 },
171 { "/usr/local/lib", 0755 },
172 { "/usr/src", 0755 },
173 { "/usr/tmp", 01777 },
174 { "/var/log", 01777 },
175 { "/var/run", 01777 },
176 { "/var/tmp", 01777 },
177 { NULL, 0 }
180 static int num_installs, num_uninstalls;
182 void
183 Installer::preremovePerpetual (const std::string& stratum)
185 std::vector<Script> perpetual;
186 PerpetualRemoveFindVisitor visitor (&perpetual, stratum);
187 Find (cygpath ("/etc/preremove")).accept (visitor);
188 if (perpetual.empty())
189 return;
191 Progress.SetText1 (IDS_PROGRESS_PREREMOVE);
192 Progress.SetText2 ((stratum + "/Perpetual").c_str ());
193 std::sort (perpetual.begin(), perpetual.end());
194 for (std::vector<Script>::iterator i = perpetual.begin (); i != perpetual.end (); ++i) {
195 Progress.SetText3 (i->fullName ().c_str());
196 i->run();
198 Progress.SetText3 ("");
201 void
202 Installer::preremoveOne (packagemeta & pkg)
204 Progress.SetText1 (IDS_PROGRESS_PREREMOVE);
205 Progress.SetText2 (pkg.name.c_str());
206 Log (LOG_BABBLE) << "Running preremove script for " << pkg.name << endLog;
207 const unsigned numexts = 4;
208 const char* exts[numexts] = { ".dash", ".sh", ".bat", ".cmd" };
209 for (unsigned i = 0; i < numexts; i++)
210 try_run_script ("/etc/preremove/", pkg.name, exts[i]);
213 void
214 Installer::uninstallOne (packagemeta & pkg)
216 if (!pkg.installed)
217 return;
219 Progress.SetText1 (IDS_PROGRESS_UNINSTALL);
220 Progress.SetText2 (pkg.name.c_str());
221 Log (LOG_PLAIN) << "Uninstalling " << pkg.name << endLog;
223 std::set<std::string> dirs;
225 io_stream *listfile = io_stream::open ("cygfile:///etc/setup/" + pkg.name + ".lst.gz", "rb", 0);
226 io_stream *listdata = compress::decompress (listfile);
228 while (listdata)
230 char getfilenamebuffer[CYG_PATH_MAX];
231 const char *sz = listdata->gets (getfilenamebuffer, sizeof (getfilenamebuffer));
232 if (sz == NULL)
233 break;
235 std::string line(sz);
237 /* Insert the paths of all parent directories of line into dirs. */
238 size_t idx = line.length();
239 while ((idx = line.find_last_of('/', idx-1)) != std::string::npos)
241 std::string dir_path = line.substr(0, idx);
242 bool was_new = dirs.insert(dir_path).second;
243 /* If the path was already present in dirs, then all parent paths
244 * must necessarily be present also, so don't do any further work.
245 * */
246 if (!was_new) break;
249 std::string d = cygpath ("/" + line);
250 WCHAR wname[d.size () + 11]; /* Prefix + ".lnk". */
251 mklongpath (wname, d.c_str (), d.size () + 11);
252 DWORD dw = GetFileAttributesW (wname);
253 if (dw != INVALID_FILE_ATTRIBUTES
254 && !(dw & FILE_ATTRIBUTE_DIRECTORY))
256 Log (LOG_BABBLE) << "unlink " << d << endLog;
257 SetFileAttributesW (wname, dw & ~FILE_ATTRIBUTE_READONLY);
258 DeleteFileW (wname);
260 /* Check for Windows shortcut of same name. */
261 d += ".lnk";
262 wcscat (wname, L".lnk");
263 dw = GetFileAttributesW (wname);
264 if (dw != INVALID_FILE_ATTRIBUTES
265 && !(dw & FILE_ATTRIBUTE_DIRECTORY))
267 Log (LOG_BABBLE) << "unlink " << d << endLog;
268 SetFileAttributesW (wname, dw & ~FILE_ATTRIBUTE_READONLY);
269 DeleteFileW (wname);
273 /* Remove the listing file */
274 delete listdata;
275 io_stream::remove ("cygfile:///etc/setup/" + pkg.name + ".lst.gz");
277 /* An STL set maintains itself in sorted order. Thus, iterating over it
278 * in reverse order will ensure we process directories depth-first. */
279 std::set<std::string>::const_iterator it = dirs.end();
280 while (it != dirs.begin())
282 it--;
283 std::string d = cygpath("/" + *it);
284 WCHAR wname[d.size () + 11];
285 mklongpath (wname, d.c_str (), d.size () + 11);
286 if (RemoveDirectoryW (wname))
287 Log (LOG_BABBLE) << "rmdir " << d << endLog;
290 pkg.installed = packageversion();
291 num_uninstalls++;
294 /* log failed scheduling of replace-on-reboot of a given file. */
295 /* also increment errors. */
296 void
297 Installer::replaceOnRebootFailed (const std::string& fn)
299 Log (LOG_TIMESTAMP) << "Unable to schedule reboot replacement of file "
300 << cygpath("/" + fn) << " with " << cygpath("/" + fn + ".new")
301 << " (Win32 Error " << GetLastError() << ")" << endLog;
302 ++errors;
305 /* log successful scheduling of replace-on-reboot of a given file. */
306 /* also set rebootneeded. */
307 void
308 Installer::replaceOnRebootSucceeded (const std::string& fn, bool &rebootneeded)
310 Log (LOG_TIMESTAMP) << "Scheduled reboot replacement of file "
311 << cygpath("/" + fn) << " with " << cygpath("/" + fn + ".new") << endLog;
312 rebootneeded = true;
315 typedef struct
317 const wchar_t *msg;
318 const char *processlist;
319 int iteration;
320 } FileInuseDlgData;
322 static INT_PTR CALLBACK
323 FileInuseDlgProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
325 switch (uMsg)
327 case WM_INITDIALOG:
329 FileInuseDlgData *dlg_data = (FileInuseDlgData *)lParam;
331 SetDlgItemTextW (hwndDlg, IDC_FILE_INUSE_MSG, dlg_data->msg);
332 SetDlgItemText (hwndDlg, IDC_FILE_INUSE_EDIT, dlg_data->processlist);
334 switch (dlg_data->iteration)
336 case 0:
337 ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_0), SW_SHOW);
338 ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_1), SW_HIDE);
339 ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_2), SW_HIDE);
340 break;
342 case 1:
343 ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_0), SW_HIDE);
344 ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_1), SW_SHOW);
345 ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_2), SW_HIDE);
346 SetDlgItemTextW (hwndDlg, IDRETRY, LoadStringW(IDS_FILE_INUSE_KILL).c_str());
347 break;
349 default:
350 case 2:
351 ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_0), SW_HIDE);
352 ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_1), SW_HIDE);
353 ShowWindow (GetDlgItem(hwndDlg, IDC_FILE_INUSE_HELP_2), SW_SHOW);
354 SetDlgItemTextW (hwndDlg, IDRETRY, LoadStringW(IDS_FILE_INUSE_KILL).c_str());
357 return TRUE; // automatically set focus, please
359 case WM_COMMAND:
360 if (HIWORD (wParam) == BN_CLICKED)
362 switch (LOWORD (wParam))
364 case IDIGNORE:
365 case IDRETRY:
366 case IDCONTINUE:
367 EndDialog (hwndDlg, LOWORD (wParam));
368 return TRUE;
373 return FALSE;
376 /* Helper function to create the registry value "AllowProtectedRenames",
377 which is required to make MOVEFILE_DELAY_UNTIL_REBOOT to work on WFP
378 protected files. By default, the entire system drive is WFP protected,
379 so a Cygwin installation on this drive sufferes from the WFP problem.
380 Even though this value is only required since Windows Server 2003 SP1,
381 we just set it here unconditionally since it doesn't hurt at all on
382 older systems. */
383 void
384 create_allow_protected_renames ()
386 HKEY key;
387 DWORD val = 1;
389 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
390 "System\\CurrentControlSet\\Control\\Session Manager",
391 0, KEY_ALL_ACCESS | SETUP_KEY_WOW64, &key) == ERROR_SUCCESS)
392 RegSetValueEx (key, "AllowProtectedRenames", 0, REG_DWORD,
393 (BYTE *) &val, sizeof (DWORD));
394 RegCloseKey (key);
397 bool
398 Installer::extract_replace_on_reboot (archive *tarstream, const std::string& prefixURL,
399 const std::string& prefixPath, std::string fn)
401 /* Extract a copy of the file with extension .new appended and
402 indicate it should be replaced on the next reboot. */
403 if (archive::extract_file (tarstream, prefixURL, prefixPath,
404 ".new") != 0)
406 Log (LOG_PLAIN) << "Unable to install file " << prefixURL
407 << prefixPath << fn << ".new" << endLog;
408 ++errors;
409 return true;
411 else
413 std::string s = cygpath ("/" + fn + ".new"),
414 d = cygpath ("/" + fn);
416 WCHAR sname[s.size () + 7];
417 WCHAR dname[d.size () + 7];
419 mklongpath (sname, s.c_str (), s.size () + 7);
420 mklongpath (dname, d.c_str (), d.size () + 7);
421 if (!MoveFileExW (sname, dname,
422 MOVEFILE_DELAY_UNTIL_REBOOT |
423 MOVEFILE_REPLACE_EXISTING))
424 replaceOnRebootFailed (fn);
425 else
427 create_allow_protected_renames ();
428 replaceOnRebootSucceeded (fn, rebootneeded);
431 return false;
434 static char all_null[512];
436 /* install one source at a given prefix. */
437 void
438 Installer::installOne (packagemeta &pkgm, const packageversion &ver,
439 packagesource &source,
440 const std::string& prefixURL,
441 const std::string& prefixPath,
442 HWND owner)
444 if (!source.Canonical())
445 return;
446 Progress.SetText1 (IDS_PROGRESS_INSTALL);
447 Progress.SetText2 ((pkgm.name + "-" + ver.Canonical_version()).c_str());
449 io_stream *pkgfile = NULL;
451 if (!source.Cached())
453 note (NULL, IDS_ERR_OPEN_READ, source.Canonical (), "Unknown filename");
454 ++errors;
455 return;
458 if (!io_stream::exists (source.Cached ())
459 || !(pkgfile = io_stream::open (source.Cached (), "rb", 0)))
461 note (NULL, IDS_ERR_OPEN_READ, source.Cached (), "No such file");
462 ++errors;
463 return;
467 /* At this point pkgfile is an opened io_stream to either a .tar.bz2 file,
468 a .tar.gz file, a .tar.lzma file, or just a .tar file. Try it first as
469 a compressed file and if that fails try opening it as a tar directly.
470 If both fail, abort.
472 Note on io_stream pointer management:
474 Both the archive and decompress classes take ownership of the io_stream
475 pointer they were opened with, meaning they delete it in their dtor. So
476 deleting tarstream should also result in deleting the underlying
477 try_decompress and pkgfile io_streams as well. */
479 archive *tarstream = NULL;
480 io_stream *try_decompress = NULL;
482 if ((try_decompress = compress::decompress (pkgfile)) != NULL)
484 if ((tarstream = archive::extract (try_decompress)) == NULL)
486 /* Decompression succeeded but we couldn't grok it as a valid tar
487 archive. */
488 char c[512];
489 ssize_t len;
490 if ((len = try_decompress->peek (c, 512)) < 0
491 || !memcmp (c, all_null, len))
492 /* In some cases, maintainers have uploaded bzipped
493 0-byte files as dummy packages instead of empty tar files.
494 This is common enough that we should not treat this as an
495 error condition.
496 Same goes for tar archives consisting of a big block of
497 all zero bytes (the famous 46 bytes tar archives). */
499 if (ver.Type () == package_binary)
500 pkgm.installed = ver;
502 else
504 note (NULL, IDS_ERR_OPEN_READ, source.Cached (),
505 "Invalid or unsupported tar format");
506 ++errors;
508 delete try_decompress;
509 return;
512 else if ((tarstream = archive::extract (pkgfile)) == NULL)
514 /* Not a compressed tarball, not a plain tarball, give up. */
515 delete pkgfile;
516 note (NULL, IDS_ERR_OPEN_READ, source.Cached (),
517 "Unrecognisable file format");
518 ++errors;
519 return;
522 /* For binary packages, create a manifest in /etc/setup/ that lists the
523 filename of each file that was unpacked. */
525 io_stream *lst = NULL;
526 if (ver.Type () == package_binary)
528 std::string lstfn = "cygfile:///etc/setup/" + pkgm.name + ".lst.gz";
530 io_stream *tmp;
531 if ((tmp = io_stream::open (lstfn, "wb", 0644)) == NULL)
532 Log (LOG_PLAIN) << "Warning: Unable to create lst file " + lstfn +
533 " - uninstall of this package will leave orphaned files." << endLog;
534 else
536 lst = new compress_gz (tmp, "w9");
537 if (lst->error ())
539 delete lst;
540 lst = NULL;
541 Log (LOG_PLAIN) << "Warning: gzip unable to write to lst file " +
542 lstfn + " - uninstall of this package will leave orphaned files."
543 << endLog;
548 package_bytes = source.size;
549 Log (LOG_PLAIN) << "Extracting from " << source.Cached () << endLog;
551 bool error_in_this_package = _installOne(pkgm, prefixURL, prefixPath, owner,
552 pkgfile, tarstream, lst, false);
553 if (tarstream->seek(0, IO_SEEK_SET) == -1)
554 Log (LOG_PLAIN) << "Error rewinding to extract symlinks" << source.Cached () << endLog;
556 error_in_this_package |= _installOne(pkgm, prefixURL, prefixPath, owner,
557 pkgfile, tarstream, lst, true);
559 if (lst)
560 delete lst;
561 delete tarstream;
563 total_bytes_sofar += package_bytes;
564 progress (0);
566 int df = diskfull (get_root_dir ().c_str ());
567 Progress.SetBar3 (df);
569 if (ver.Type () == package_binary && !error_in_this_package)
570 pkgm.installed = ver;
573 bool
574 Installer::_installOne (packagemeta &pkgm,
575 const std::string& prefixURL,
576 const std::string& prefixPath,
577 HWND owner,
578 io_stream *pkgfile,
579 archive *tarstream,
580 io_stream *lst,
581 bool symlink_phase)
583 bool error_in_this_package = false;
584 bool ignoreInUseErrors = false;
585 bool ignoreExtractErrors = unattended_mode;
587 std::string fn;
588 while ((fn = tarstream->next_file_name ()).size ())
590 if (symlink_phase != (tarstream->next_file_type () == ARCHIVE_FILE_SYMLINK))
592 tarstream->skip_file();
593 continue;
596 std::string canonicalfn = prefixPath + fn;
598 // pathnames starting "." (i.e. dotfiles in the root directory) are
599 // reserved for package metadata. Don't extract them.
600 if (fn[0] == '.')
602 tarstream->skip_file ();
603 continue;
606 Progress.SetText3 (canonicalfn.c_str ());
607 Log (LOG_BABBLE) << "Installing file " << prefixURL << prefixPath
608 << fn << endLog;
609 if (lst)
611 std::string tmp = fn + "\n";
612 lst->write (tmp.c_str(), tmp.size());
614 if (Script::isAScript (fn))
615 pkgm.addScript (Script (canonicalfn));
617 int iteration = 0;
618 archive::extract_results extres;
619 while ((extres = archive::extract_file (tarstream, prefixURL, prefixPath)) != archive::extract_ok)
621 bool error_in_this_file = false;
623 switch (extres)
625 case archive::extract_inuse: // in use
627 if (!ignoreInUseErrors)
629 // convert the file name to long UNC form
630 std::string s = backslash (cygpath ("/" + fn));
631 WCHAR sname[s.size () + 7];
632 mklongpath (sname, s.c_str (), s.size () + 7);
634 // find any process which has that file loaded into it
635 // (note that this doesn't find when the file is un-writeable because the process has
636 // that file opened exclusively)
637 ProcessList processes = Process::listProcessesWithModuleLoaded (sname);
639 std::string plm;
640 for (ProcessList::iterator i = processes.begin (); i != processes.end (); i++)
642 if (i != processes.begin ()) plm += "\r\n";
644 std::string processName = i->getName ();
645 Log (LOG_BABBLE) << processName << endLog;
646 plm += processName;
649 INT_PTR rc = (iteration < 3) ? IDRETRY : IDCONTINUE;
650 if (unattended_mode == attended)
652 if (!processes.empty())
654 // Use the IDD_FILE_INUSE dialog to ask the user if we should try to kill the
655 // listed processes, or just ignore the problem and schedule the file to be
656 // replaced after a reboot
657 FileInuseDlgData dlg_data;
658 std::wstring msg = LoadStringW(IDS_FILE_INUSE_MSG) + L" /" + string_to_wstring(fn);
659 dlg_data.msg = msg.c_str ();
660 dlg_data.processlist = plm.c_str ();
661 dlg_data.iteration = iteration;
663 rc = DialogBoxParam(hinstance, MAKEINTRESOURCE (IDD_FILE_INUSE), owner, FileInuseDlgProc, (LPARAM)&dlg_data);
665 else
667 // We couldn't enumerate any processes which have this file loaded into it
668 // either the cause of the error is something else, or something (e.g security
669 // policy) prevents us from listing those processes.
670 // All we can offer the user is a generic "retry or ignore" choice and a chance
671 // to fix the problem themselves
672 rc = mbox (owner, IDS_EXTRACTION_INUSE,
673 MB_RETRYCONTINUE | MB_ICONWARNING | MB_TASKMODAL,
674 fn.c_str());
678 switch (rc)
680 case IDIGNORE:
681 // manual intervention may have fixed the problem, retry
682 continue;
683 case IDRETRY:
684 if (!processes.empty())
686 // try to stop all the processes
687 for (ProcessList::iterator i = processes.begin (); i != processes.end (); i++)
689 i->kill (iteration);
692 // wait up to 15 seconds for processes to stop
693 for (unsigned int i = 0; i < 15; i++)
695 processes = Process::listProcessesWithModuleLoaded (sname);
696 if (processes.size () == 0)
697 break;
699 Sleep (1000);
702 // else, manual intervention may have fixed the problem
704 // retry
705 iteration++;
706 continue;
707 case IDCONTINUE:
708 // ignore this in-use error, and any subsequent in-use errors for other files in the same package
709 ignoreInUseErrors = true;
710 break;
711 default:
712 break;
714 // fall through to previous functionality
717 if (NoReplaceOnReboot)
719 ++errors;
720 error_in_this_file = true;
721 Log (LOG_PLAIN) << "Not replacing in-use file " << prefixURL
722 << prefixPath << fn << endLog;
724 else
726 error_in_this_file = extract_replace_on_reboot(tarstream, prefixURL, prefixPath, fn);
729 break;
730 case archive::extract_other: // extract failed
732 if (!ignoreExtractErrors)
734 // XXX: We should offer the option to retry,
735 // continue without extracting this particular archive,
736 // or ignore all extraction errors.
737 // Unfortunately, we don't currently know how to rewind
738 // the archive, so we can't retry at present,
739 // and ignore all errors is mis-implemented at present
740 // to only apply to errors arising from a single archive,
741 // so we degenerate to the continue option.
742 mbox (owner, IDS_EXTRACTION_FAILED,
743 MB_OK | MB_ICONWARNING | MB_TASKMODAL,
744 fn.c_str());
747 error_in_this_file = true;
749 break;
750 case archive::extract_ok:
751 break;
754 // We're done with this file
756 // if an error occured ...
757 if (error_in_this_file)
759 // skip to next file in archive
760 tarstream->skip_file();
761 // don't mark this package as successfully installed
762 error_in_this_package = true;
765 break;
767 progress (pkgfile->tell ());
768 num_installs++;
771 return error_in_this_package;
774 static void
775 check_for_old_cygwin (HWND owner)
777 /* Paths within system dir expected to be always < MAX_PATH. */
778 char buf[MAX_PATH + sizeof ("\\cygwin1.dll")];
779 if (!GetSystemDirectory (buf, sizeof (buf)))
780 return;
781 strcat (buf, "\\cygwin1.dll");
782 if (_access (buf, 0) != 0)
783 return;
785 switch (mbox(owner, IDS_INSTALL_OLD_CYGWIN,
786 MB_YESNO | MB_ICONQUESTION | MB_TASKMODAL,
787 buf))
789 case IDYES:
790 if (!DeleteFile (buf))
792 mbox (owner, IDS_INSTALL_DELETE_OLD_CYGWIN_FAILED,
793 MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL,
794 buf);
796 break;
797 default:
798 break;
801 return;
804 static void
805 do_install_thread (HINSTANCE h, HWND owner)
807 int i;
809 num_installs = 0, num_uninstalls = 0;
810 rebootneeded = false;
812 io_stream::mkpath_p (PATH_TO_DIR,
813 std::string("file://") + std::string(get_root_dir()),
814 0755);
816 for (i = 0; Installer::StandardDirs[i].name; i++)
818 std::string p = cygpath (Installer::StandardDirs[i].name);
819 if (p.size())
820 io_stream::mkpath_p (PATH_TO_DIR, "file://" + p,
821 Installer::StandardDirs[i].mode);
824 /* Create /var/run/utmp */
825 io_stream *utmp = io_stream::open ("cygfile:///var/run/utmp", "wb", 0666);
826 delete utmp;
828 Installer myInstaller;
829 myInstaller.initDialog();
831 total_bytes = 0;
832 total_bytes_sofar = 0;
834 int df = diskfull (get_root_dir ().c_str());
835 Progress.SetBar3 (df);
837 if (!NoWriteRegistry)
838 /* Writes Cygwin/setup/rootdir registry value */
839 create_install_root ();
841 std::vector <packageversion> install_q, uninstall_q, sourceinstall_q;
843 packagedb db;
844 const SolverTransactionList &t = db.solution.transactions();
846 /* Calculate the amount of data to md5sum */
847 Progress.SetText1(IDS_PROGRESS_CALCULATING);
848 long long int md5sum_total_bytes = 0;
849 for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
851 packageversion version = i->version;
853 if (i->type == SolverTransaction::transInstall)
855 md5sum_total_bytes += version.source()->size;
859 /* md5sum the packages, build lists of packages to install and uninstall
860 and calculate the total amount of data to install.
861 The hash checking is relevant only for local installs. For a
862 net install, the hashes will have already been verified at download
863 time, and all calls to check_hash() below should instantly return. */
864 long long int md5sum_total_bytes_sofar = 0;
865 for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
867 packageversion version = i->version;
869 if (i->type == SolverTransaction::transInstall)
873 (*version.source ()).check_hash ();
875 catch (Exception *e)
877 // We used to give the user a yes/no option to skip this
878 // package (with "no" meaning install it even though the
879 // archive is corrupt), but both options could damage the
880 // user's system. In the absence of a safe way to recover, we
881 // just bail out.
882 if (e->errNo() == APPERR_CORRUPT_PACKAGE)
883 fatal (owner, IDS_CORRUPT_PACKAGE, version.Name().c_str());
884 // Unexpected exception.
885 throw e;
888 md5sum_total_bytes_sofar += version.source()->size;
889 total_bytes += version.source()->size;
891 // source packages are kept in a separate queue as they are installed
892 // differently: root is /usr/src, install isn't recorded, etc.
893 if (version.Type() == package_source)
894 sourceinstall_q.push_back (version);
895 else
896 install_q.push_back (version);
900 /* Uninstall, upgrade or reinstall */
901 if (i->type == SolverTransaction::transErase)
903 uninstall_q.push_back (version);
906 if (md5sum_total_bytes > 0)
907 Progress.SetBar2 (md5sum_total_bytes_sofar, md5sum_total_bytes);
910 /* start with uninstalls - remove files that new packages may replace */
911 Progress.SetBar2(0);
912 myInstaller.preremovePerpetual ("0");
914 Progress.SetBar2(0);
915 for (std::vector <packageversion>::iterator i = uninstall_q.begin ();
916 i != uninstall_q.end (); ++i)
918 packagemeta *pkgm = db.findBinary (PackageSpecification(i->Name()));
919 if (pkgm)
920 myInstaller.preremoveOne (*pkgm);
921 Progress.SetBar2(std::distance(uninstall_q.begin(), i) + 1, uninstall_q.size());
924 Progress.SetBar2(0);
925 myInstaller.preremovePerpetual ("z");
927 Progress.SetBar2(0);
928 for (std::vector <packageversion>::iterator i = uninstall_q.begin ();
929 i != uninstall_q.end (); ++i)
931 packagemeta *pkgm = db.findBinary (PackageSpecification(i->Name()));
932 if (pkgm)
933 myInstaller.uninstallOne (*pkgm);
934 Progress.SetBar2(std::distance(uninstall_q.begin(), i) + 1, uninstall_q.size());
937 for (std::vector <packageversion>::iterator i = install_q.begin ();
938 i != install_q.end (); ++i)
940 packageversion & pkg = *i;
941 packagemeta *pkgm = db.findBinary (PackageSpecification(i->Name()));
943 try {
944 myInstaller.installOne (*pkgm, pkg, *pkg.source(),
945 "cygfile://", "/", owner);
947 catch (std::exception *e)
949 if (yesno (owner, IDS_INSTALL_ERROR, e->what()) != IDYES)
951 Log (LOG_TIMESTAMP)
952 << "User cancelled setup after install error" << endLog;
953 Logger ().exit (1);
954 return;
959 for (std::vector <packageversion>::iterator i = sourceinstall_q.begin ();
960 i != sourceinstall_q.end (); ++i)
962 packagemeta *pkgm = db.findSource (PackageSpecification(i->Name()));
963 packageversion & pkg = *i;
964 myInstaller.installOne (*pkgm, pkg, *pkg.source(),
965 "cygfile://", "/usr/src/", owner);
968 if (rebootneeded)
969 note (owner, IDS_REBOOT_REQUIRED);
971 int temperr;
972 if ((temperr = db.flush ()))
974 const char *err = strerror (temperr);
975 if (!err)
976 err = "(unknown error)";
977 fatal (owner, IDS_ERR_OPEN_WRITE, "Package Database",
978 err);
981 if (!myInstaller.errors)
982 check_for_old_cygwin (owner);
983 if (num_installs == 0 && num_uninstalls == 0)
985 if (!unattended_mode)
986 Logger ().setExitMsg (IDS_NOTHING_INSTALLED);
987 return;
989 if (num_installs == 0)
991 if (!unattended_mode)
992 Logger ().setExitMsg (IDS_UNINSTALL_COMPLETE);
993 return;
996 if (myInstaller.errors)
997 Logger ().setExitMsg (IDS_INSTALL_INCOMPLETE);
998 else if (!unattended_mode)
999 Logger ().setExitMsg (IDS_INSTALL_COMPLETE);
1001 if (rebootneeded)
1002 Logger ().setExitMsg (IDS_REBOOT_REQUIRED);
1005 static DWORD WINAPI
1006 do_install_reflector (void *p)
1008 HANDLE *context;
1009 context = (HANDLE *) p;
1011 SetThreadUILanguage(langid);
1015 do_install_thread ((HINSTANCE) context[0], (HWND) context[1]);
1017 // Tell the progress page that we're done downloading
1018 Progress.PostMessageNow (WM_APP_INSTALL_THREAD_COMPLETE);
1020 TOPLEVEL_CATCH((HWND) context[1], "install");
1022 ExitThread (0);
1025 static HANDLE context[2];
1027 void
1028 do_install (HINSTANCE h, HWND owner)
1030 context[0] = h;
1031 context[1] = owner;
1033 DWORD threadID;
1034 CreateThread (NULL, 0, do_install_reflector, context, 0, &threadID);