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
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"
32 #include <sys/types.h>
47 #include "filemanip.h"
48 #include "io_stream.h"
50 #include "compress_gz.h"
52 #include "archive_tar.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"
63 #include "Exception.h"
64 #include "processlist.h"
66 extern ThreeBarProgressPage Progress
;
68 static long long int total_bytes
= 0;
69 static long long int total_bytes_sofar
= 0;
70 static int package_bytes
= 0;
72 static BoolOption
NoReplaceOnReboot (false, 'r', "no-replaceonreboot", IDS_HELPTEXT_NO_REPLACEONREBOOT
);
79 class PerpetualRemoveFindVisitor
: public FindVisitor
82 PerpetualRemoveFindVisitor (std::vector
<Script
> *scripts
, const std::string
& stratum
)
86 virtual void visitFile(const std::string
& basePath
,
87 const WIN32_FIND_DATA
*theFile
)
89 std::string fn
= std::string("/etc/preremove/") + theFile
->cFileName
;
91 if (script
.is_p(stratum
))
92 _scripts
->push_back(Script (fn
));
94 virtual ~ PerpetualRemoveFindVisitor () {}
96 PerpetualRemoveFindVisitor (PerpetualRemoveFindVisitor
const &);
97 PerpetualRemoveFindVisitor
& operator= (PerpetualRemoveFindVisitor
const &);
99 std::vector
<Script
> *_scripts
;
100 const std::string stratum
;
106 static std_dirs_t StandardDirs
[];
109 void progress (int bytes
);
110 void preremovePerpetual (const std::string
& stratum
);
111 void preremoveOne (packagemeta
&);
112 void uninstallOne (packagemeta
&);
113 void replaceOnRebootFailed (const std::string
& fn
);
114 void replaceOnRebootSucceeded (const std::string
& fn
, bool &rebootneeded
);
115 void installOne (packagemeta
&pkg
, const packageversion
&ver
,
116 packagesource
&source
,
117 const std::string
& , const std::string
&, HWND
);
120 bool extract_replace_on_reboot(archive
*, const std::string
&,
121 const std::string
&, std::string
);
122 bool _installOne (packagemeta
&pkgm
,
123 const std::string
& prefixURL
,
124 const std::string
& prefixPath
,
132 Installer::Installer() : errors(0)
137 Installer::initDialog()
139 Progress
.SetText2 ("");
140 Progress
.SetText3 ("");
144 Installer::progress (int bytes
)
146 if (package_bytes
> 0)
147 Progress
.SetBar1 (bytes
, package_bytes
);
150 Progress
.SetBar2 (total_bytes_sofar
+ bytes
, total_bytes
);
154 Installer::StandardDirs
[] = {
157 { "/dev/mqueue", 01777 },
158 { "/dev/shm", 01777 },
160 { "/etc/fstab.d", 01777 },
165 { "/usr/bin", 0755 },
166 { "/usr/lib", 0755 },
167 { "/usr/local", 0755 },
168 { "/usr/local/bin", 0755 },
169 { "/usr/local/etc", 0755 },
170 { "/usr/local/lib", 0755 },
171 { "/usr/src", 0755 },
172 { "/usr/tmp", 01777 },
173 { "/var/log", 01777 },
174 { "/var/run", 01777 },
175 { "/var/tmp", 01777 },
179 static int num_installs
, num_uninstalls
;
182 Installer::preremovePerpetual (const std::string
& stratum
)
184 std::vector
<Script
> perpetual
;
185 PerpetualRemoveFindVisitor
visitor (&perpetual
, stratum
);
186 Find (cygpath ("/etc/preremove")).accept (visitor
);
187 if (perpetual
.empty())
190 Progress
.SetText1 (IDS_PROGRESS_PREREMOVE
);
191 Progress
.SetText2 ((stratum
+ "/Perpetual").c_str ());
192 std::sort (perpetual
.begin(), perpetual
.end());
193 for (std::vector
<Script
>::iterator i
= perpetual
.begin (); i
!= perpetual
.end (); ++i
) {
194 Progress
.SetText3 (i
->fullName ().c_str());
197 Progress
.SetText3 ("");
201 Installer::preremoveOne (packagemeta
& pkg
)
203 Progress
.SetText1 (IDS_PROGRESS_PREREMOVE
);
204 Progress
.SetText2 (pkg
.name
.c_str());
205 Log (LOG_BABBLE
) << "Running preremove script for " << pkg
.name
<< endLog
;
206 const unsigned numexts
= 4;
207 const char* exts
[numexts
] = { ".dash", ".sh", ".bat", ".cmd" };
208 for (unsigned i
= 0; i
< numexts
; i
++)
209 try_run_script ("/etc/preremove/", pkg
.name
, exts
[i
]);
213 Installer::uninstallOne (packagemeta
& pkg
)
218 Progress
.SetText1 (IDS_PROGRESS_UNINSTALL
);
219 Progress
.SetText2 (pkg
.name
.c_str());
220 Log (LOG_PLAIN
) << "Uninstalling " << pkg
.name
<< endLog
;
222 std::set
<std::string
> dirs
;
224 io_stream
*listfile
= io_stream::open ("cygfile:///etc/setup/" + pkg
.name
+ ".lst.gz", "rb", 0);
225 io_stream
*listdata
= compress::decompress (listfile
);
229 char getfilenamebuffer
[CYG_PATH_MAX
];
230 const char *sz
= listdata
->gets (getfilenamebuffer
, sizeof (getfilenamebuffer
));
234 std::string
line(sz
);
236 /* Insert the paths of all parent directories of line into dirs. */
237 size_t idx
= line
.length();
238 while ((idx
= line
.find_last_of('/', idx
-1)) != std::string::npos
)
240 std::string dir_path
= line
.substr(0, idx
);
241 bool was_new
= dirs
.insert(dir_path
).second
;
242 /* If the path was already present in dirs, then all parent paths
243 * must necessarily be present also, so don't do any further work.
248 std::string d
= cygpath ("/" + line
);
249 WCHAR wname
[d
.size () + 11]; /* Prefix + ".lnk". */
250 mklongpath (wname
, d
.c_str (), d
.size () + 11);
251 DWORD dw
= GetFileAttributesW (wname
);
252 if (dw
!= INVALID_FILE_ATTRIBUTES
253 && !(dw
& FILE_ATTRIBUTE_DIRECTORY
))
255 Log (LOG_BABBLE
) << "unlink " << d
<< endLog
;
256 SetFileAttributesW (wname
, dw
& ~FILE_ATTRIBUTE_READONLY
);
259 /* Check for Windows shortcut of same name. */
261 wcscat (wname
, L
".lnk");
262 dw
= GetFileAttributesW (wname
);
263 if (dw
!= INVALID_FILE_ATTRIBUTES
264 && !(dw
& FILE_ATTRIBUTE_DIRECTORY
))
266 Log (LOG_BABBLE
) << "unlink " << d
<< endLog
;
267 SetFileAttributesW (wname
, dw
& ~FILE_ATTRIBUTE_READONLY
);
272 /* Remove the listing file */
274 io_stream::remove ("cygfile:///etc/setup/" + pkg
.name
+ ".lst.gz");
276 /* An STL set maintains itself in sorted order. Thus, iterating over it
277 * in reverse order will ensure we process directories depth-first. */
278 std::set
<std::string
>::const_iterator it
= dirs
.end();
279 while (it
!= dirs
.begin())
282 std::string d
= cygpath("/" + *it
);
283 WCHAR wname
[d
.size () + 11];
284 mklongpath (wname
, d
.c_str (), d
.size () + 11);
285 if (RemoveDirectoryW (wname
))
286 Log (LOG_BABBLE
) << "rmdir " << d
<< endLog
;
289 pkg
.installed
= packageversion();
293 /* log failed scheduling of replace-on-reboot of a given file. */
294 /* also increment errors. */
296 Installer::replaceOnRebootFailed (const std::string
& fn
)
298 Log (LOG_TIMESTAMP
) << "Unable to schedule reboot replacement of file "
299 << cygpath("/" + fn
) << " with " << cygpath("/" + fn
+ ".new")
300 << " (Win32 Error " << GetLastError() << ")" << endLog
;
304 /* log successful scheduling of replace-on-reboot of a given file. */
305 /* also set rebootneeded. */
307 Installer::replaceOnRebootSucceeded (const std::string
& fn
, bool &rebootneeded
)
309 Log (LOG_TIMESTAMP
) << "Scheduled reboot replacement of file "
310 << cygpath("/" + fn
) << " with " << cygpath("/" + fn
+ ".new") << endLog
;
317 const char *processlist
;
321 static INT_PTR CALLBACK
322 FileInuseDlgProc (HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
328 FileInuseDlgData
*dlg_data
= (FileInuseDlgData
*)lParam
;
330 SetDlgItemTextW (hwndDlg
, IDC_FILE_INUSE_MSG
, dlg_data
->msg
);
331 SetDlgItemText (hwndDlg
, IDC_FILE_INUSE_EDIT
, dlg_data
->processlist
);
333 switch (dlg_data
->iteration
)
336 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_0
), SW_SHOW
);
337 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_1
), SW_HIDE
);
338 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_2
), SW_HIDE
);
342 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_0
), SW_HIDE
);
343 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_1
), SW_SHOW
);
344 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_2
), SW_HIDE
);
345 SetDlgItemTextW (hwndDlg
, IDRETRY
, LoadStringW(IDS_FILE_INUSE_KILL
).c_str());
350 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_0
), SW_HIDE
);
351 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_1
), SW_HIDE
);
352 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_2
), SW_SHOW
);
353 SetDlgItemTextW (hwndDlg
, IDRETRY
, LoadStringW(IDS_FILE_INUSE_KILL
).c_str());
356 return TRUE
; // automatically set focus, please
359 if (HIWORD (wParam
) == BN_CLICKED
)
361 switch (LOWORD (wParam
))
366 EndDialog (hwndDlg
, LOWORD (wParam
));
375 /* Helper function to create the registry value "AllowProtectedRenames",
376 which is required to make MOVEFILE_DELAY_UNTIL_REBOOT to work on WFP
377 protected files. By default, the entire system drive is WFP protected,
378 so a Cygwin installation on this drive sufferes from the WFP problem.
379 Even though this value is only required since Windows Server 2003 SP1,
380 we just set it here unconditionally since it doesn't hurt at all on
383 create_allow_protected_renames ()
388 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE
,
389 "System\\CurrentControlSet\\Control\\Session Manager",
390 0, KEY_ALL_ACCESS
| SETUP_KEY_WOW64
, &key
) == ERROR_SUCCESS
)
391 RegSetValueEx (key
, "AllowProtectedRenames", 0, REG_DWORD
,
392 (BYTE
*) &val
, sizeof (DWORD
));
397 Installer::extract_replace_on_reboot (archive
*tarstream
, const std::string
& prefixURL
,
398 const std::string
& prefixPath
, std::string fn
)
400 /* Extract a copy of the file with extension .new appended and
401 indicate it should be replaced on the next reboot. */
402 if (archive::extract_file (tarstream
, prefixURL
, prefixPath
,
405 Log (LOG_PLAIN
) << "Unable to install file " << prefixURL
406 << prefixPath
<< fn
<< ".new" << endLog
;
412 std::string s
= cygpath ("/" + fn
+ ".new"),
413 d
= cygpath ("/" + fn
);
415 WCHAR sname
[s
.size () + 7];
416 WCHAR dname
[d
.size () + 7];
418 mklongpath (sname
, s
.c_str (), s
.size () + 7);
419 mklongpath (dname
, d
.c_str (), d
.size () + 7);
420 if (!MoveFileExW (sname
, dname
,
421 MOVEFILE_DELAY_UNTIL_REBOOT
|
422 MOVEFILE_REPLACE_EXISTING
))
423 replaceOnRebootFailed (fn
);
426 create_allow_protected_renames ();
427 replaceOnRebootSucceeded (fn
, rebootneeded
);
433 static char all_null
[512];
435 /* install one source at a given prefix. */
437 Installer::installOne (packagemeta
&pkgm
, const packageversion
&ver
,
438 packagesource
&source
,
439 const std::string
& prefixURL
,
440 const std::string
& prefixPath
,
443 if (!source
.Canonical())
445 Progress
.SetText1 (IDS_PROGRESS_INSTALL
);
446 Progress
.SetText2 ((pkgm
.name
+ "-" + ver
.Canonical_version()).c_str());
448 io_stream
*pkgfile
= NULL
;
450 if (!source
.Cached())
452 note (NULL
, IDS_ERR_OPEN_READ
, source
.Canonical (), "Unknown filename");
457 if (!io_stream::exists (source
.Cached ())
458 || !(pkgfile
= io_stream::open (source
.Cached (), "rb", 0)))
460 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (), "No such file");
466 /* At this point pkgfile is an opened io_stream to either a .tar.bz2 file,
467 a .tar.gz file, a .tar.lzma file, or just a .tar file. Try it first as
468 a compressed file and if that fails try opening it as a tar directly.
471 Note on io_stream pointer management:
473 Both the archive and decompress classes take ownership of the io_stream
474 pointer they were opened with, meaning they delete it in their dtor. So
475 deleting tarstream should also result in deleting the underlying
476 try_decompress and pkgfile io_streams as well. */
478 archive
*tarstream
= NULL
;
479 io_stream
*try_decompress
= NULL
;
481 if ((try_decompress
= compress::decompress (pkgfile
)) != NULL
)
483 if ((tarstream
= archive::extract (try_decompress
)) == NULL
)
485 /* Decompression succeeded but we couldn't grok it as a valid tar
489 if ((len
= try_decompress
->peek (c
, 512)) < 0
490 || !memcmp (c
, all_null
, len
))
491 /* In some cases, maintainers have uploaded bzipped
492 0-byte files as dummy packages instead of empty tar files.
493 This is common enough that we should not treat this as an
495 Same goes for tar archives consisting of a big block of
496 all zero bytes (the famous 46 bytes tar archives). */
498 if (ver
.Type () == package_binary
)
499 pkgm
.installed
= ver
;
503 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (),
504 "Invalid or unsupported tar format");
507 delete try_decompress
;
511 else if ((tarstream
= archive::extract (pkgfile
)) == NULL
)
513 /* Not a compressed tarball, not a plain tarball, give up. */
515 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (),
516 "Unrecognisable file format");
521 /* For binary packages, create a manifest in /etc/setup/ that lists the
522 filename of each file that was unpacked. */
524 io_stream
*lst
= NULL
;
525 if (ver
.Type () == package_binary
)
527 std::string lstfn
= "cygfile:///etc/setup/" + pkgm
.name
+ ".lst.gz";
530 if ((tmp
= io_stream::open (lstfn
, "wb", 0644)) == NULL
)
531 Log (LOG_PLAIN
) << "Warning: Unable to create lst file " + lstfn
+
532 " - uninstall of this package will leave orphaned files." << endLog
;
535 lst
= new compress_gz (tmp
, "w9");
540 Log (LOG_PLAIN
) << "Warning: gzip unable to write to lst file " +
541 lstfn
+ " - uninstall of this package will leave orphaned files."
547 package_bytes
= source
.size
;
548 Log (LOG_PLAIN
) << "Extracting from " << source
.Cached () << endLog
;
550 bool error_in_this_package
= _installOne(pkgm
, prefixURL
, prefixPath
, owner
,
551 pkgfile
, tarstream
, lst
, false);
552 if (tarstream
->seek(0, IO_SEEK_SET
) == -1)
553 Log (LOG_PLAIN
) << "Error rewinding to extract symlinks" << source
.Cached () << endLog
;
555 error_in_this_package
|= _installOne(pkgm
, prefixURL
, prefixPath
, owner
,
556 pkgfile
, tarstream
, lst
, true);
562 total_bytes_sofar
+= package_bytes
;
565 int df
= diskfull (get_root_dir ().c_str ());
566 Progress
.SetBar3 (df
);
568 if (ver
.Type () == package_binary
&& !error_in_this_package
)
569 pkgm
.installed
= ver
;
573 Installer::_installOne (packagemeta
&pkgm
,
574 const std::string
& prefixURL
,
575 const std::string
& prefixPath
,
582 bool error_in_this_package
= false;
583 bool ignoreInUseErrors
= false;
584 bool ignoreExtractErrors
= unattended_mode
;
587 while ((fn
= tarstream
->next_file_name ()).size ())
589 if (symlink_phase
!= (tarstream
->next_file_type () == ARCHIVE_FILE_SYMLINK
))
591 tarstream
->skip_file();
595 std::string canonicalfn
= prefixPath
+ fn
;
597 // pathnames starting "." (i.e. dotfiles in the root directory) are
598 // reserved for package metadata. Don't extract them.
601 tarstream
->skip_file ();
605 Progress
.SetText3 (canonicalfn
.c_str ());
606 Log (LOG_BABBLE
) << "Installing file " << prefixURL
<< prefixPath
610 std::string tmp
= fn
+ "\n";
611 lst
->write (tmp
.c_str(), tmp
.size());
613 if (Script::isAScript (fn
))
614 pkgm
.addScript (Script (canonicalfn
));
617 archive::extract_results extres
;
618 while ((extres
= archive::extract_file (tarstream
, prefixURL
, prefixPath
)) != archive::extract_ok
)
620 bool error_in_this_file
= false;
624 case archive::extract_inuse
: // in use
626 if (!ignoreInUseErrors
)
628 // convert the file name to long UNC form
629 std::string s
= backslash (cygpath ("/" + fn
));
630 WCHAR sname
[s
.size () + 7];
631 mklongpath (sname
, s
.c_str (), s
.size () + 7);
633 // find any process which has that file loaded into it
634 // (note that this doesn't find when the file is un-writeable because the process has
635 // that file opened exclusively)
636 ProcessList processes
= Process::listProcessesWithModuleLoaded (sname
);
639 for (ProcessList::iterator i
= processes
.begin (); i
!= processes
.end (); i
++)
641 if (i
!= processes
.begin ()) plm
+= "\r\n";
643 std::string processName
= i
->getName ();
644 Log (LOG_BABBLE
) << processName
<< endLog
;
648 INT_PTR rc
= (iteration
< 3) ? IDRETRY
: IDCONTINUE
;
649 if (unattended_mode
== attended
)
651 if (!processes
.empty())
653 // Use the IDD_FILE_INUSE dialog to ask the user if we should try to kill the
654 // listed processes, or just ignore the problem and schedule the file to be
655 // replaced after a reboot
656 FileInuseDlgData dlg_data
;
657 std::wstring msg
= LoadStringW(IDS_FILE_INUSE_MSG
) + L
" /" + string_to_wstring(fn
);
658 dlg_data
.msg
= msg
.c_str ();
659 dlg_data
.processlist
= plm
.c_str ();
660 dlg_data
.iteration
= iteration
;
662 rc
= DialogBoxParam(hinstance
, MAKEINTRESOURCE (IDD_FILE_INUSE
), owner
, FileInuseDlgProc
, (LPARAM
)&dlg_data
);
666 // We couldn't enumerate any processes which have this file loaded into it
667 // either the cause of the error is something else, or something (e.g security
668 // policy) prevents us from listing those processes.
669 // All we can offer the user is a generic "retry or ignore" choice and a chance
670 // to fix the problem themselves
671 rc
= mbox (owner
, IDS_EXTRACTION_INUSE
,
672 MB_RETRYCONTINUE
| MB_ICONWARNING
| MB_TASKMODAL
,
680 // manual intervention may have fixed the problem, retry
683 if (!processes
.empty())
685 // try to stop all the processes
686 for (ProcessList::iterator i
= processes
.begin (); i
!= processes
.end (); i
++)
691 // wait up to 15 seconds for processes to stop
692 for (unsigned int i
= 0; i
< 15; i
++)
694 processes
= Process::listProcessesWithModuleLoaded (sname
);
695 if (processes
.size () == 0)
701 // else, manual intervention may have fixed the problem
707 // ignore this in-use error, and any subsequent in-use errors for other files in the same package
708 ignoreInUseErrors
= true;
713 // fall through to previous functionality
716 if (NoReplaceOnReboot
)
719 error_in_this_file
= true;
720 Log (LOG_PLAIN
) << "Not replacing in-use file " << prefixURL
721 << prefixPath
<< fn
<< endLog
;
725 error_in_this_file
= extract_replace_on_reboot(tarstream
, prefixURL
, prefixPath
, fn
);
729 case archive::extract_other
: // extract failed
731 if (!ignoreExtractErrors
)
733 // XXX: We should offer the option to retry,
734 // continue without extracting this particular archive,
735 // or ignore all extraction errors.
736 // Unfortunately, we don't currently know how to rewind
737 // the archive, so we can't retry at present,
738 // and ignore all errors is mis-implemented at present
739 // to only apply to errors arising from a single archive,
740 // so we degenerate to the continue option.
741 mbox (owner
, IDS_EXTRACTION_FAILED
,
742 MB_OK
| MB_ICONWARNING
| MB_TASKMODAL
,
746 error_in_this_file
= true;
749 case archive::extract_ok
:
753 // We're done with this file
755 // if an error occured ...
756 if (error_in_this_file
)
758 // skip to next file in archive
759 tarstream
->skip_file();
760 // don't mark this package as successfully installed
761 error_in_this_package
= true;
766 progress (pkgfile
->tell ());
770 return error_in_this_package
;
774 check_for_old_cygwin (HWND owner
)
776 /* Paths within system dir expected to be always < MAX_PATH. */
777 char buf
[MAX_PATH
+ sizeof ("\\cygwin1.dll")];
778 if (!GetSystemDirectory (buf
, sizeof (buf
)))
780 strcat (buf
, "\\cygwin1.dll");
781 if (_access (buf
, 0) != 0)
784 switch (mbox(owner
, IDS_INSTALL_OLD_CYGWIN
,
785 MB_YESNO
| MB_ICONQUESTION
| MB_TASKMODAL
,
789 if (!DeleteFile (buf
))
791 mbox (owner
, IDS_INSTALL_DELETE_OLD_CYGWIN_FAILED
,
792 MB_OK
| MB_ICONEXCLAMATION
| MB_TASKMODAL
,
804 do_install_thread (HINSTANCE h
, HWND owner
)
808 num_installs
= 0, num_uninstalls
= 0;
809 rebootneeded
= false;
811 io_stream::mkpath_p (PATH_TO_DIR
,
812 std::string("file://") + std::string(get_root_dir()),
815 for (i
= 0; Installer::StandardDirs
[i
].name
; i
++)
817 std::string p
= cygpath (Installer::StandardDirs
[i
].name
);
819 io_stream::mkpath_p (PATH_TO_DIR
, "file://" + p
,
820 Installer::StandardDirs
[i
].mode
);
823 /* Create /var/run/utmp */
824 io_stream
*utmp
= io_stream::open ("cygfile:///var/run/utmp", "wb", 0666);
827 Installer myInstaller
;
828 myInstaller
.initDialog();
831 total_bytes_sofar
= 0;
833 int df
= diskfull (get_root_dir ().c_str());
834 Progress
.SetBar3 (df
);
836 /* Writes Cygwin/setup/rootdir registry value */
837 create_install_root ();
839 std::vector
<packageversion
> install_q
, uninstall_q
, sourceinstall_q
;
842 const SolverTransactionList
&t
= db
.solution
.transactions();
844 /* Calculate the amount of data to md5sum */
845 Progress
.SetText1(IDS_PROGRESS_CALCULATING
);
846 long long int md5sum_total_bytes
= 0;
847 for (SolverTransactionList::const_iterator i
= t
.begin (); i
!= t
.end (); ++i
)
849 packageversion version
= i
->version
;
851 if (i
->type
== SolverTransaction::transInstall
)
853 md5sum_total_bytes
+= version
.source()->size
;
857 /* md5sum the packages, build lists of packages to install and uninstall
858 and calculate the total amount of data to install.
859 The hash checking is relevant only for local installs. For a
860 net install, the hashes will have already been verified at download
861 time, and all calls to check_hash() below should instantly return. */
862 long long int md5sum_total_bytes_sofar
= 0;
863 for (SolverTransactionList::const_iterator i
= t
.begin (); i
!= t
.end (); ++i
)
865 packageversion version
= i
->version
;
867 if (i
->type
== SolverTransaction::transInstall
)
871 (*version
.source ()).check_hash ();
875 // We used to give the user a yes/no option to skip this
876 // package (with "no" meaning install it even though the
877 // archive is corrupt), but both options could damage the
878 // user's system. In the absence of a safe way to recover, we
880 if (e
->errNo() == APPERR_CORRUPT_PACKAGE
)
881 fatal (owner
, IDS_CORRUPT_PACKAGE
, version
.Name().c_str());
882 // Unexpected exception.
886 md5sum_total_bytes_sofar
+= version
.source()->size
;
887 total_bytes
+= version
.source()->size
;
889 // source packages are kept in a separate queue as they are installed
890 // differently: root is /usr/src, install isn't recorded, etc.
891 if (version
.Type() == package_source
)
892 sourceinstall_q
.push_back (version
);
894 install_q
.push_back (version
);
898 /* Uninstall, upgrade or reinstall */
899 if (i
->type
== SolverTransaction::transErase
)
901 uninstall_q
.push_back (version
);
904 if (md5sum_total_bytes
> 0)
905 Progress
.SetBar2 (md5sum_total_bytes_sofar
, md5sum_total_bytes
);
908 /* start with uninstalls - remove files that new packages may replace */
910 myInstaller
.preremovePerpetual ("0");
913 for (std::vector
<packageversion
>::iterator i
= uninstall_q
.begin ();
914 i
!= uninstall_q
.end (); ++i
)
916 packagemeta
*pkgm
= db
.findBinary (PackageSpecification(i
->Name()));
918 myInstaller
.preremoveOne (*pkgm
);
919 Progress
.SetBar2(std::distance(uninstall_q
.begin(), i
) + 1, uninstall_q
.size());
923 myInstaller
.preremovePerpetual ("z");
926 for (std::vector
<packageversion
>::iterator i
= uninstall_q
.begin ();
927 i
!= uninstall_q
.end (); ++i
)
929 packagemeta
*pkgm
= db
.findBinary (PackageSpecification(i
->Name()));
931 myInstaller
.uninstallOne (*pkgm
);
932 Progress
.SetBar2(std::distance(uninstall_q
.begin(), i
) + 1, uninstall_q
.size());
935 for (std::vector
<packageversion
>::iterator i
= install_q
.begin ();
936 i
!= install_q
.end (); ++i
)
938 packageversion
& pkg
= *i
;
939 packagemeta
*pkgm
= db
.findBinary (PackageSpecification(i
->Name()));
942 myInstaller
.installOne (*pkgm
, pkg
, *pkg
.source(),
943 "cygfile://", "/", owner
);
945 catch (std::exception
*e
)
947 if (yesno (owner
, IDS_INSTALL_ERROR
, e
->what()) != IDYES
)
950 << "User cancelled setup after install error" << endLog
;
957 for (std::vector
<packageversion
>::iterator i
= sourceinstall_q
.begin ();
958 i
!= sourceinstall_q
.end (); ++i
)
960 packagemeta
*pkgm
= db
.findSource (PackageSpecification(i
->Name()));
961 packageversion
& pkg
= *i
;
962 myInstaller
.installOne (*pkgm
, pkg
, *pkg
.source(),
963 "cygfile://", "/usr/src/", owner
);
967 note (owner
, IDS_REBOOT_REQUIRED
);
970 if ((temperr
= db
.flush ()))
972 const char *err
= strerror (temperr
);
974 err
= "(unknown error)";
975 fatal (owner
, IDS_ERR_OPEN_WRITE
, "Package Database",
979 if (!myInstaller
.errors
)
980 check_for_old_cygwin (owner
);
981 if (num_installs
== 0 && num_uninstalls
== 0)
983 if (!unattended_mode
)
984 Logger ().setExitMsg (IDS_NOTHING_INSTALLED
);
987 if (num_installs
== 0)
989 if (!unattended_mode
)
990 Logger ().setExitMsg (IDS_UNINSTALL_COMPLETE
);
994 if (myInstaller
.errors
)
995 Logger ().setExitMsg (IDS_INSTALL_INCOMPLETE
);
996 else if (!unattended_mode
)
997 Logger ().setExitMsg (IDS_INSTALL_COMPLETE
);
1000 Logger ().setExitMsg (IDS_REBOOT_REQUIRED
);
1004 do_install_reflector (void *p
)
1007 context
= (HANDLE
*) p
;
1009 SetThreadUILanguage(langid
);
1013 do_install_thread ((HINSTANCE
) context
[0], (HWND
) context
[1]);
1015 // Tell the progress page that we're done downloading
1016 Progress
.PostMessageNow (WM_APP_INSTALL_THREAD_COMPLETE
);
1018 TOPLEVEL_CATCH((HWND
) context
[1], "install");
1023 static HANDLE context
[2];
1026 do_install (HINSTANCE h
, HWND owner
)
1032 CreateThread (NULL
, 0, do_install_reflector
, context
, 0, &threadID
);