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());
200 Installer::preremoveOne (packagemeta
& pkg
)
202 Progress
.SetText1 (IDS_PROGRESS_PREREMOVE
);
203 Progress
.SetText2 (pkg
.name
.c_str());
204 Log (LOG_BABBLE
) << "Running preremove script for " << pkg
.name
<< endLog
;
205 const unsigned numexts
= 4;
206 const char* exts
[numexts
] = { ".dash", ".sh", ".bat", ".cmd" };
207 for (unsigned i
= 0; i
< numexts
; i
++)
208 try_run_script ("/etc/preremove/", pkg
.name
, exts
[i
]);
212 Installer::uninstallOne (packagemeta
& pkg
)
217 Progress
.SetText1 (IDS_PROGRESS_UNINSTALL
);
218 Progress
.SetText2 (pkg
.name
.c_str());
219 Log (LOG_PLAIN
) << "Uninstalling " << pkg
.name
<< endLog
;
221 std::set
<std::string
> dirs
;
223 io_stream
*listfile
= io_stream::open ("cygfile:///etc/setup/" + pkg
.name
+ ".lst.gz", "rb", 0);
224 io_stream
*listdata
= compress::decompress (listfile
);
228 char getfilenamebuffer
[CYG_PATH_MAX
];
229 const char *sz
= listdata
->gets (getfilenamebuffer
, sizeof (getfilenamebuffer
));
233 std::string
line(sz
);
235 /* Insert the paths of all parent directories of line into dirs. */
236 size_t idx
= line
.length();
237 while ((idx
= line
.find_last_of('/', idx
-1)) != std::string::npos
)
239 std::string dir_path
= line
.substr(0, idx
);
240 bool was_new
= dirs
.insert(dir_path
).second
;
241 /* If the path was already present in dirs, then all parent paths
242 * must necessarily be present also, so don't do any further work.
247 std::string d
= cygpath ("/" + line
);
248 WCHAR wname
[d
.size () + 11]; /* Prefix + ".lnk". */
249 mklongpath (wname
, d
.c_str (), d
.size () + 11);
250 DWORD dw
= GetFileAttributesW (wname
);
251 if (dw
!= INVALID_FILE_ATTRIBUTES
252 && !(dw
& FILE_ATTRIBUTE_DIRECTORY
))
254 Log (LOG_BABBLE
) << "unlink " << d
<< endLog
;
255 SetFileAttributesW (wname
, dw
& ~FILE_ATTRIBUTE_READONLY
);
258 /* Check for Windows shortcut of same name. */
260 wcscat (wname
, L
".lnk");
261 dw
= GetFileAttributesW (wname
);
262 if (dw
!= INVALID_FILE_ATTRIBUTES
263 && !(dw
& FILE_ATTRIBUTE_DIRECTORY
))
265 Log (LOG_BABBLE
) << "unlink " << d
<< endLog
;
266 SetFileAttributesW (wname
, dw
& ~FILE_ATTRIBUTE_READONLY
);
271 /* Remove the listing file */
273 io_stream::remove ("cygfile:///etc/setup/" + pkg
.name
+ ".lst.gz");
275 /* An STL set maintains itself in sorted order. Thus, iterating over it
276 * in reverse order will ensure we process directories depth-first. */
277 std::set
<std::string
>::const_iterator it
= dirs
.end();
278 while (it
!= dirs
.begin())
281 std::string d
= cygpath("/" + *it
);
282 WCHAR wname
[d
.size () + 11];
283 mklongpath (wname
, d
.c_str (), d
.size () + 11);
284 if (RemoveDirectoryW (wname
))
285 Log (LOG_BABBLE
) << "rmdir " << d
<< endLog
;
288 pkg
.installed
= packageversion();
292 /* log failed scheduling of replace-on-reboot of a given file. */
293 /* also increment errors. */
295 Installer::replaceOnRebootFailed (const std::string
& fn
)
297 Log (LOG_TIMESTAMP
) << "Unable to schedule reboot replacement of file "
298 << cygpath("/" + fn
) << " with " << cygpath("/" + fn
+ ".new")
299 << " (Win32 Error " << GetLastError() << ")" << endLog
;
303 /* log successful scheduling of replace-on-reboot of a given file. */
304 /* also set rebootneeded. */
306 Installer::replaceOnRebootSucceeded (const std::string
& fn
, bool &rebootneeded
)
308 Log (LOG_TIMESTAMP
) << "Scheduled reboot replacement of file "
309 << cygpath("/" + fn
) << " with " << cygpath("/" + fn
+ ".new") << endLog
;
316 const char *processlist
;
320 static INT_PTR CALLBACK
321 FileInuseDlgProc (HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
327 FileInuseDlgData
*dlg_data
= (FileInuseDlgData
*)lParam
;
329 SetDlgItemTextW (hwndDlg
, IDC_FILE_INUSE_MSG
, dlg_data
->msg
);
330 SetDlgItemText (hwndDlg
, IDC_FILE_INUSE_EDIT
, dlg_data
->processlist
);
332 switch (dlg_data
->iteration
)
335 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_0
), SW_SHOW
);
336 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_1
), SW_HIDE
);
337 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_2
), SW_HIDE
);
341 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_0
), SW_HIDE
);
342 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_1
), SW_SHOW
);
343 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_2
), SW_HIDE
);
344 SetDlgItemTextW (hwndDlg
, IDRETRY
, LoadStringW(IDS_FILE_INUSE_KILL
).c_str());
349 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_0
), SW_HIDE
);
350 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_1
), SW_HIDE
);
351 ShowWindow (GetDlgItem(hwndDlg
, IDC_FILE_INUSE_HELP_2
), SW_SHOW
);
352 SetDlgItemTextW (hwndDlg
, IDRETRY
, LoadStringW(IDS_FILE_INUSE_KILL
).c_str());
355 return TRUE
; // automatically set focus, please
358 if (HIWORD (wParam
) == BN_CLICKED
)
360 switch (LOWORD (wParam
))
365 EndDialog (hwndDlg
, LOWORD (wParam
));
374 /* Helper function to create the registry value "AllowProtectedRenames",
375 which is required to make MOVEFILE_DELAY_UNTIL_REBOOT to work on WFP
376 protected files. By default, the entire system drive is WFP protected,
377 so a Cygwin installation on this drive sufferes from the WFP problem.
378 Even though this value is only required since Windows Server 2003 SP1,
379 we just set it here unconditionally since it doesn't hurt at all on
382 create_allow_protected_renames ()
387 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE
,
388 "System\\CurrentControlSet\\Control\\Session Manager",
389 0, KEY_ALL_ACCESS
| SETUP_KEY_WOW64
, &key
) == ERROR_SUCCESS
)
390 RegSetValueEx (key
, "AllowProtectedRenames", 0, REG_DWORD
,
391 (BYTE
*) &val
, sizeof (DWORD
));
396 Installer::extract_replace_on_reboot (archive
*tarstream
, const std::string
& prefixURL
,
397 const std::string
& prefixPath
, std::string fn
)
399 /* Extract a copy of the file with extension .new appended and
400 indicate it should be replaced on the next reboot. */
401 if (archive::extract_file (tarstream
, prefixURL
, prefixPath
,
404 Log (LOG_PLAIN
) << "Unable to install file " << prefixURL
405 << prefixPath
<< fn
<< ".new" << endLog
;
411 std::string s
= cygpath ("/" + fn
+ ".new"),
412 d
= cygpath ("/" + fn
);
414 WCHAR sname
[s
.size () + 7];
415 WCHAR dname
[d
.size () + 7];
417 mklongpath (sname
, s
.c_str (), s
.size () + 7);
418 mklongpath (dname
, d
.c_str (), d
.size () + 7);
419 if (!MoveFileExW (sname
, dname
,
420 MOVEFILE_DELAY_UNTIL_REBOOT
|
421 MOVEFILE_REPLACE_EXISTING
))
422 replaceOnRebootFailed (fn
);
425 create_allow_protected_renames ();
426 replaceOnRebootSucceeded (fn
, rebootneeded
);
432 static char all_null
[512];
434 /* install one source at a given prefix. */
436 Installer::installOne (packagemeta
&pkgm
, const packageversion
&ver
,
437 packagesource
&source
,
438 const std::string
& prefixURL
,
439 const std::string
& prefixPath
,
442 if (!source
.Canonical())
444 Progress
.SetText1 (IDS_PROGRESS_INSTALL
);
445 Progress
.SetText2 ((pkgm
.name
+ "-" + ver
.Canonical_version()).c_str());
447 io_stream
*pkgfile
= NULL
;
449 if (!source
.Cached())
451 note (NULL
, IDS_ERR_OPEN_READ
, source
.Canonical (), "Unknown filename");
456 if (!io_stream::exists (source
.Cached ())
457 || !(pkgfile
= io_stream::open (source
.Cached (), "rb", 0)))
459 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (), "No such file");
465 /* At this point pkgfile is an opened io_stream to either a .tar.bz2 file,
466 a .tar.gz file, a .tar.lzma file, or just a .tar file. Try it first as
467 a compressed file and if that fails try opening it as a tar directly.
470 Note on io_stream pointer management:
472 Both the archive and decompress classes take ownership of the io_stream
473 pointer they were opened with, meaning they delete it in their dtor. So
474 deleting tarstream should also result in deleting the underlying
475 try_decompress and pkgfile io_streams as well. */
477 archive
*tarstream
= NULL
;
478 io_stream
*try_decompress
= NULL
;
480 if ((try_decompress
= compress::decompress (pkgfile
)) != NULL
)
482 if ((tarstream
= archive::extract (try_decompress
)) == NULL
)
484 /* Decompression succeeded but we couldn't grok it as a valid tar
488 if ((len
= try_decompress
->peek (c
, 512)) < 0
489 || !memcmp (c
, all_null
, len
))
490 /* In some cases, maintainers have uploaded bzipped
491 0-byte files as dummy packages instead of empty tar files.
492 This is common enough that we should not treat this as an
494 Same goes for tar archives consisting of a big block of
495 all zero bytes (the famous 46 bytes tar archives). */
497 if (ver
.Type () == package_binary
)
498 pkgm
.installed
= ver
;
502 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (),
503 "Invalid or unsupported tar format");
506 delete try_decompress
;
510 else if ((tarstream
= archive::extract (pkgfile
)) == NULL
)
512 /* Not a compressed tarball, not a plain tarball, give up. */
514 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (),
515 "Unrecognisable file format");
520 /* For binary packages, create a manifest in /etc/setup/ that lists the
521 filename of each file that was unpacked. */
523 io_stream
*lst
= NULL
;
524 if (ver
.Type () == package_binary
)
526 std::string lstfn
= "cygfile:///etc/setup/" + pkgm
.name
+ ".lst.gz";
529 if ((tmp
= io_stream::open (lstfn
, "wb", 0644)) == NULL
)
530 Log (LOG_PLAIN
) << "Warning: Unable to create lst file " + lstfn
+
531 " - uninstall of this package will leave orphaned files." << endLog
;
534 lst
= new compress_gz (tmp
, "w9");
539 Log (LOG_PLAIN
) << "Warning: gzip unable to write to lst file " +
540 lstfn
+ " - uninstall of this package will leave orphaned files."
546 package_bytes
= source
.size
;
547 Log (LOG_PLAIN
) << "Extracting from " << source
.Cached () << endLog
;
549 bool error_in_this_package
= _installOne(pkgm
, prefixURL
, prefixPath
, owner
,
550 pkgfile
, tarstream
, lst
, false);
551 if (tarstream
->seek(0, IO_SEEK_SET
) == -1)
552 Log (LOG_PLAIN
) << "Error rewinding to extract symlinks" << source
.Cached () << endLog
;
554 error_in_this_package
|= _installOne(pkgm
, prefixURL
, prefixPath
, owner
,
555 pkgfile
, tarstream
, lst
, true);
561 total_bytes_sofar
+= package_bytes
;
564 int df
= diskfull (get_root_dir ().c_str ());
565 Progress
.SetBar3 (df
);
567 if (ver
.Type () == package_binary
&& !error_in_this_package
)
568 pkgm
.installed
= ver
;
572 Installer::_installOne (packagemeta
&pkgm
,
573 const std::string
& prefixURL
,
574 const std::string
& prefixPath
,
581 bool error_in_this_package
= false;
582 bool ignoreInUseErrors
= false;
583 bool ignoreExtractErrors
= unattended_mode
;
586 while ((fn
= tarstream
->next_file_name ()).size ())
588 if (symlink_phase
!= (tarstream
->next_file_type () == ARCHIVE_FILE_SYMLINK
))
590 tarstream
->skip_file();
594 std::string canonicalfn
= prefixPath
+ fn
;
596 // pathnames starting "." (i.e. dotfiles in the root directory) are
597 // reserved for package metadata. Don't extract them.
600 tarstream
->skip_file ();
604 Progress
.SetText3 (canonicalfn
.c_str ());
605 Log (LOG_BABBLE
) << "Installing file " << prefixURL
<< prefixPath
609 std::string tmp
= fn
+ "\n";
610 lst
->write (tmp
.c_str(), tmp
.size());
612 if (Script::isAScript (fn
))
613 pkgm
.addScript (Script (canonicalfn
));
616 archive::extract_results extres
;
617 while ((extres
= archive::extract_file (tarstream
, prefixURL
, prefixPath
)) != archive::extract_ok
)
619 bool error_in_this_file
= false;
623 case archive::extract_inuse
: // in use
625 if (!ignoreInUseErrors
)
627 // convert the file name to long UNC form
628 std::string s
= backslash (cygpath ("/" + fn
));
629 WCHAR sname
[s
.size () + 7];
630 mklongpath (sname
, s
.c_str (), s
.size () + 7);
632 // find any process which has that file loaded into it
633 // (note that this doesn't find when the file is un-writeable because the process has
634 // that file opened exclusively)
635 ProcessList processes
= Process::listProcessesWithModuleLoaded (sname
);
638 for (ProcessList::iterator i
= processes
.begin (); i
!= processes
.end (); i
++)
640 if (i
!= processes
.begin ()) plm
+= "\r\n";
642 std::string processName
= i
->getName ();
643 Log (LOG_BABBLE
) << processName
<< endLog
;
647 INT_PTR rc
= (iteration
< 3) ? IDRETRY
: IDCONTINUE
;
648 if (unattended_mode
== attended
)
650 if (!processes
.empty())
652 // Use the IDD_FILE_INUSE dialog to ask the user if we should try to kill the
653 // listed processes, or just ignore the problem and schedule the file to be
654 // replaced after a reboot
655 FileInuseDlgData dlg_data
;
656 std::wstring msg
= LoadStringW(IDS_FILE_INUSE_MSG
) + L
" /" + string_to_wstring(fn
);
657 dlg_data
.msg
= msg
.c_str ();
658 dlg_data
.processlist
= plm
.c_str ();
659 dlg_data
.iteration
= iteration
;
661 rc
= DialogBoxParam(hinstance
, MAKEINTRESOURCE (IDD_FILE_INUSE
), owner
, FileInuseDlgProc
, (LPARAM
)&dlg_data
);
665 // We couldn't enumerate any processes which have this file loaded into it
666 // either the cause of the error is something else, or something (e.g security
667 // policy) prevents us from listing those processes.
668 // All we can offer the user is a generic "retry or ignore" choice and a chance
669 // to fix the problem themselves
670 rc
= mbox (owner
, IDS_EXTRACTION_INUSE
,
671 MB_RETRYCONTINUE
| MB_ICONWARNING
| MB_TASKMODAL
,
679 // manual intervention may have fixed the problem, retry
682 if (!processes
.empty())
684 // try to stop all the processes
685 for (ProcessList::iterator i
= processes
.begin (); i
!= processes
.end (); i
++)
690 // wait up to 15 seconds for processes to stop
691 for (unsigned int i
= 0; i
< 15; i
++)
693 processes
= Process::listProcessesWithModuleLoaded (sname
);
694 if (processes
.size () == 0)
700 // else, manual intervention may have fixed the problem
706 // ignore this in-use error, and any subsequent in-use errors for other files in the same package
707 ignoreInUseErrors
= true;
712 // fall through to previous functionality
715 if (NoReplaceOnReboot
)
718 error_in_this_file
= true;
719 Log (LOG_PLAIN
) << "Not replacing in-use file " << prefixURL
720 << prefixPath
<< fn
<< endLog
;
724 error_in_this_file
= extract_replace_on_reboot(tarstream
, prefixURL
, prefixPath
, fn
);
728 case archive::extract_other
: // extract failed
730 if (!ignoreExtractErrors
)
732 // XXX: We should offer the option to retry,
733 // continue without extracting this particular archive,
734 // or ignore all extraction errors.
735 // Unfortunately, we don't currently know how to rewind
736 // the archive, so we can't retry at present,
737 // and ignore all errors is mis-implemented at present
738 // to only apply to errors arising from a single archive,
739 // so we degenerate to the continue option.
740 mbox (owner
, IDS_EXTRACTION_FAILED
,
741 MB_OK
| MB_ICONWARNING
| MB_TASKMODAL
,
745 error_in_this_file
= true;
748 case archive::extract_ok
:
752 // We're done with this file
754 // if an error occured ...
755 if (error_in_this_file
)
757 // skip to next file in archive
758 tarstream
->skip_file();
759 // don't mark this package as successfully installed
760 error_in_this_package
= true;
765 progress (pkgfile
->tell ());
769 return error_in_this_package
;
773 check_for_old_cygwin (HWND owner
)
775 /* Paths within system dir expected to be always < MAX_PATH. */
776 char buf
[MAX_PATH
+ sizeof ("\\cygwin1.dll")];
777 if (!GetSystemDirectory (buf
, sizeof (buf
)))
779 strcat (buf
, "\\cygwin1.dll");
780 if (_access (buf
, 0) != 0)
783 switch (mbox(owner
, IDS_INSTALL_OLD_CYGWIN
,
784 MB_YESNO
| MB_ICONQUESTION
| MB_TASKMODAL
,
788 if (!DeleteFile (buf
))
790 mbox (owner
, IDS_INSTALL_DELETE_OLD_CYGWIN_FAILED
,
791 MB_OK
| MB_ICONEXCLAMATION
| MB_TASKMODAL
,
803 do_install_thread (HINSTANCE h
, HWND owner
)
807 num_installs
= 0, num_uninstalls
= 0;
808 rebootneeded
= false;
810 io_stream::mkpath_p (PATH_TO_DIR
,
811 std::string("file://") + std::string(get_root_dir()),
814 for (i
= 0; Installer::StandardDirs
[i
].name
; i
++)
816 std::string p
= cygpath (Installer::StandardDirs
[i
].name
);
818 io_stream::mkpath_p (PATH_TO_DIR
, "file://" + p
,
819 Installer::StandardDirs
[i
].mode
);
822 /* Create /var/run/utmp */
823 io_stream
*utmp
= io_stream::open ("cygfile:///var/run/utmp", "wb", 0666);
826 Installer myInstaller
;
827 myInstaller
.initDialog();
830 total_bytes_sofar
= 0;
832 int df
= diskfull (get_root_dir ().c_str());
833 Progress
.SetBar3 (df
);
835 /* Writes Cygwin/setup/rootdir registry value */
836 create_install_root ();
838 std::vector
<packageversion
> install_q
, uninstall_q
, sourceinstall_q
;
841 const SolverTransactionList
&t
= db
.solution
.transactions();
843 /* Calculate the amount of data to md5sum */
844 Progress
.SetText1(IDS_PROGRESS_CALCULATING
);
845 long long int md5sum_total_bytes
= 0;
846 for (SolverTransactionList::const_iterator i
= t
.begin (); i
!= t
.end (); ++i
)
848 packageversion version
= i
->version
;
850 if (i
->type
== SolverTransaction::transInstall
)
852 md5sum_total_bytes
+= version
.source()->size
;
856 /* md5sum the packages, build lists of packages to install and uninstall
857 and calculate the total amount of data to install.
858 The hash checking is relevant only for local installs. For a
859 net install, the hashes will have already been verified at download
860 time, and all calls to check_hash() below should instantly return. */
861 long long int md5sum_total_bytes_sofar
= 0;
862 for (SolverTransactionList::const_iterator i
= t
.begin (); i
!= t
.end (); ++i
)
864 packageversion version
= i
->version
;
866 if (i
->type
== SolverTransaction::transInstall
)
870 (*version
.source ()).check_hash ();
874 // We used to give the user a yes/no option to skip this
875 // package (with "no" meaning install it even though the
876 // archive is corrupt), but both options could damage the
877 // user's system. In the absence of a safe way to recover, we
879 if (e
->errNo() == APPERR_CORRUPT_PACKAGE
)
880 fatal (owner
, IDS_CORRUPT_PACKAGE
, version
.Name().c_str());
881 // Unexpected exception.
885 md5sum_total_bytes_sofar
+= version
.source()->size
;
886 total_bytes
+= version
.source()->size
;
888 // source packages are kept in a separate queue as they are installed
889 // differently: root is /usr/src, install isn't recorded, etc.
890 if (version
.Type() == package_source
)
891 sourceinstall_q
.push_back (version
);
893 install_q
.push_back (version
);
897 /* Uninstall, upgrade or reinstall */
898 if (i
->type
== SolverTransaction::transErase
)
900 uninstall_q
.push_back (version
);
903 if (md5sum_total_bytes
> 0)
904 Progress
.SetBar2 (md5sum_total_bytes_sofar
, md5sum_total_bytes
);
907 /* start with uninstalls - remove files that new packages may replace */
909 myInstaller
.preremovePerpetual ("0");
912 for (std::vector
<packageversion
>::iterator i
= uninstall_q
.begin ();
913 i
!= uninstall_q
.end (); ++i
)
915 packagemeta
*pkgm
= db
.findBinary (PackageSpecification(i
->Name()));
917 myInstaller
.preremoveOne (*pkgm
);
918 Progress
.SetBar2(std::distance(uninstall_q
.begin(), i
) + 1, uninstall_q
.size());
922 myInstaller
.preremovePerpetual ("z");
925 for (std::vector
<packageversion
>::iterator i
= uninstall_q
.begin ();
926 i
!= uninstall_q
.end (); ++i
)
928 packagemeta
*pkgm
= db
.findBinary (PackageSpecification(i
->Name()));
930 myInstaller
.uninstallOne (*pkgm
);
931 Progress
.SetBar2(std::distance(uninstall_q
.begin(), i
) + 1, uninstall_q
.size());
934 for (std::vector
<packageversion
>::iterator i
= install_q
.begin ();
935 i
!= install_q
.end (); ++i
)
937 packageversion
& pkg
= *i
;
938 packagemeta
*pkgm
= db
.findBinary (PackageSpecification(i
->Name()));
941 myInstaller
.installOne (*pkgm
, pkg
, *pkg
.source(),
942 "cygfile://", "/", owner
);
944 catch (std::exception
*e
)
946 if (yesno (owner
, IDS_INSTALL_ERROR
, e
->what()) != IDYES
)
949 << "User cancelled setup after install error" << endLog
;
956 for (std::vector
<packageversion
>::iterator i
= sourceinstall_q
.begin ();
957 i
!= sourceinstall_q
.end (); ++i
)
959 packagemeta
*pkgm
= db
.findSource (PackageSpecification(i
->Name()));
960 packageversion
& pkg
= *i
;
961 myInstaller
.installOne (*pkgm
, pkg
, *pkg
.source(),
962 "cygfile://", "/usr/src/", owner
);
966 note (owner
, IDS_REBOOT_REQUIRED
);
969 if ((temperr
= db
.flush ()))
971 const char *err
= strerror (temperr
);
973 err
= "(unknown error)";
974 fatal (owner
, IDS_ERR_OPEN_WRITE
, "Package Database",
978 if (!myInstaller
.errors
)
979 check_for_old_cygwin (owner
);
980 if (num_installs
== 0 && num_uninstalls
== 0)
982 if (!unattended_mode
)
983 Logger ().setExitMsg (IDS_NOTHING_INSTALLED
);
986 if (num_installs
== 0)
988 if (!unattended_mode
)
989 Logger ().setExitMsg (IDS_UNINSTALL_COMPLETE
);
993 if (myInstaller
.errors
)
994 Logger ().setExitMsg (IDS_INSTALL_INCOMPLETE
);
995 else if (!unattended_mode
)
996 Logger ().setExitMsg (IDS_INSTALL_COMPLETE
);
999 Logger ().setExitMsg (IDS_REBOOT_REQUIRED
);
1003 do_install_reflector (void *p
)
1006 context
= (HANDLE
*) p
;
1008 SetThreadUILanguage(langid
);
1012 do_install_thread ((HINSTANCE
) context
[0], (HWND
) context
[1]);
1014 // Tell the progress page that we're done downloading
1015 Progress
.PostMessageNow (WM_APP_INSTALL_THREAD_COMPLETE
);
1017 TOPLEVEL_CATCH((HWND
) context
[1], "install");
1022 static HANDLE context
[2];
1025 do_install (HINSTANCE h
, HWND owner
)
1031 CreateThread (NULL
, 0, do_install_reflector
, context
, 0, &threadID
);