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>
46 #include "filemanip.h"
47 #include "io_stream.h"
49 #include "compress_gz.h"
51 #include "archive_tar.h"
54 #include "package_db.h"
55 #include "package_meta.h"
56 #include "package_version.h"
57 #include "package_source.h"
60 #include "Exception.h"
61 #include "processlist.h"
63 extern ThreeBarProgressPage Progress
;
65 static long long int total_bytes
= 0;
66 static long long int total_bytes_sofar
= 0;
67 static int package_bytes
= 0;
69 static BoolOption
NoReplaceOnReboot (false, 'r', "no-replaceonreboot",
70 "Disable replacing in-use files on next "
81 static std_dirs_t StandardDirs
[];
84 void progress (int bytes
);
85 void preremoveOne (packagemeta
&);
86 void uninstallOne (packagemeta
&);
87 void replaceOnRebootFailed (const std::string
& fn
);
88 void replaceOnRebootSucceeded (const std::string
& fn
, bool &rebootneeded
);
89 void installOne (packagemeta
&pkg
, const packageversion
&ver
,
90 packagesource
&source
,
91 const std::string
& , const std::string
&, HWND
);
94 bool extract_replace_on_reboot(archive
*, const std::string
&,
95 const std::string
&, std::string
);
99 Installer::Installer() : errors(0)
104 Installer::initDialog()
106 Progress
.SetText2 ("");
107 Progress
.SetText3 ("");
111 Installer::progress (int bytes
)
113 if (package_bytes
> 0)
114 Progress
.SetBar1 (bytes
, package_bytes
);
117 Progress
.SetBar2 (total_bytes_sofar
+ bytes
, total_bytes
);
121 Installer::StandardDirs
[] = {
124 { "/dev/mqueue", 01777 },
125 { "/dev/shm", 01777 },
127 { "/etc/fstab.d", 01777 },
132 { "/usr/bin", 0755 },
133 { "/usr/lib", 0755 },
134 { "/usr/local", 0755 },
135 { "/usr/local/bin", 0755 },
136 { "/usr/local/etc", 0755 },
137 { "/usr/local/lib", 0755 },
138 { "/usr/src", 0755 },
139 { "/usr/tmp", 01777 },
140 { "/var/log", 01777 },
141 { "/var/run", 01777 },
142 { "/var/tmp", 01777 },
146 static int num_installs
, num_uninstalls
;
149 Installer::preremoveOne (packagemeta
& pkg
)
151 Progress
.SetText1 (IDS_PROGRESS_PREREMOVE
);
152 Progress
.SetText2 (pkg
.name
.c_str());
153 Log (LOG_BABBLE
) << "Running preremove script for " << pkg
.name
<< endLog
;
154 const unsigned numexts
= 4;
155 const char* exts
[numexts
] = { ".dash", ".sh", ".bat", ".cmd" };
156 for (unsigned i
= 0; i
< numexts
; i
++)
157 try_run_script ("/etc/preremove/", pkg
.name
, exts
[i
]);
161 Installer::uninstallOne (packagemeta
& pkg
)
166 Progress
.SetText1 (IDS_PROGRESS_UNINSTALL
);
167 Progress
.SetText2 (pkg
.name
.c_str());
168 Log (LOG_PLAIN
) << "Uninstalling " << pkg
.name
<< endLog
;
170 std::set
<std::string
> dirs
;
172 io_stream
*listfile
= io_stream::open ("cygfile:///etc/setup/" + pkg
.name
+ ".lst.gz", "rb", 0);
173 io_stream
*listdata
= compress::decompress (listfile
);
177 char getfilenamebuffer
[CYG_PATH_MAX
];
178 const char *sz
= listdata
->gets (getfilenamebuffer
, sizeof (getfilenamebuffer
));
182 std::string
line(sz
);
184 /* Insert the paths of all parent directories of line into dirs. */
185 size_t idx
= line
.length();
186 while ((idx
= line
.find_last_of('/', idx
-1)) != std::string::npos
)
188 std::string dir_path
= line
.substr(0, idx
);
189 bool was_new
= dirs
.insert(dir_path
).second
;
190 /* If the path was already present in dirs, then all parent paths
191 * must necessarily be present also, so don't do any further work.
196 std::string d
= cygpath ("/" + line
);
197 WCHAR wname
[d
.size () + 11]; /* Prefix + ".lnk". */
198 mklongpath (wname
, d
.c_str (), d
.size () + 11);
199 DWORD dw
= GetFileAttributesW (wname
);
200 if (dw
!= INVALID_FILE_ATTRIBUTES
201 && !(dw
& FILE_ATTRIBUTE_DIRECTORY
))
203 Log (LOG_BABBLE
) << "unlink " << d
<< endLog
;
204 SetFileAttributesW (wname
, dw
& ~FILE_ATTRIBUTE_READONLY
);
207 /* Check for Windows shortcut of same name. */
209 wcscat (wname
, L
".lnk");
210 dw
= GetFileAttributesW (wname
);
211 if (dw
!= INVALID_FILE_ATTRIBUTES
212 && !(dw
& FILE_ATTRIBUTE_DIRECTORY
))
214 Log (LOG_BABBLE
) << "unlink " << d
<< endLog
;
215 SetFileAttributesW (wname
, dw
& ~FILE_ATTRIBUTE_READONLY
);
220 /* Remove the listing file */
222 io_stream::remove ("cygfile:///etc/setup/" + pkg
.name
+ ".lst.gz");
224 /* An STL set maintains itself in sorted order. Thus, iterating over it
225 * in reverse order will ensure we process directories depth-first. */
226 std::set
<std::string
>::const_iterator it
= dirs
.end();
227 while (it
!= dirs
.begin())
230 std::string d
= cygpath("/" + *it
);
231 WCHAR wname
[d
.size () + 11];
232 mklongpath (wname
, d
.c_str (), d
.size () + 11);
233 if (RemoveDirectoryW (wname
))
234 Log (LOG_BABBLE
) << "rmdir " << d
<< endLog
;
237 pkg
.installed
= packageversion();
241 /* log failed scheduling of replace-on-reboot of a given file. */
242 /* also increment errors. */
244 Installer::replaceOnRebootFailed (const std::string
& fn
)
246 Log (LOG_TIMESTAMP
) << "Unable to schedule reboot replacement of file "
247 << cygpath("/" + fn
) << " with " << cygpath("/" + fn
+ ".new")
248 << " (Win32 Error " << GetLastError() << ")" << endLog
;
252 /* log successful scheduling of replace-on-reboot of a given file. */
253 /* also set rebootneeded. */
255 Installer::replaceOnRebootSucceeded (const std::string
& fn
, bool &rebootneeded
)
257 Log (LOG_TIMESTAMP
) << "Scheduled reboot replacement of file "
258 << cygpath("/" + fn
) << " with " << cygpath("/" + fn
+ ".new") << endLog
;
262 #define MB_RETRYCONTINUE 7
263 #if !defined(IDCONTINUE)
264 #define IDCONTINUE IDCANCEL
267 static HHOOK hMsgBoxHook
;
268 LRESULT CALLBACK
CBTProc(int nCode
, WPARAM wParam
, LPARAM lParam
) {
273 if (GetDlgItem(hWnd
, IDCANCEL
) != NULL
)
274 SetDlgItemText(hWnd
, IDCANCEL
, "Continue");
275 UnhookWindowsHookEx(hMsgBoxHook
);
277 return CallNextHookEx(hMsgBoxHook
, nCode
, wParam
, lParam
);
280 int _custom_MessageBox(HWND hWnd
, LPCTSTR szText
, LPCTSTR szCaption
, UINT uType
) {
282 bool retry_continue
= (uType
& MB_TYPEMASK
) == MB_RETRYCONTINUE
;
283 if (retry_continue
) {
284 uType
&= ~MB_TYPEMASK
; uType
|= MB_RETRYCANCEL
;
285 // Install a window hook, so we can intercept the message-box
286 // creation, and customize it
287 // Only install for THIS thread!!!
288 hMsgBoxHook
= SetWindowsHookEx(WH_CBT
, CBTProc
, NULL
, GetCurrentThreadId());
290 retval
= MessageBox(hWnd
, szText
, szCaption
, uType
);
291 // Intercept the return value for less confusing results
292 if (retry_continue
&& retval
== IDCANCEL
)
298 #define MessageBox _custom_MessageBox
303 const char *processlist
;
307 static INT_PTR CALLBACK
308 FileInuseDlgProc (HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
314 FileInuseDlgData
*dlg_data
= (FileInuseDlgData
*)lParam
;
316 SetDlgItemText (hwndDlg
, IDC_FILE_INUSE_MSG
, dlg_data
->msg
);
317 SetDlgItemText (hwndDlg
, IDC_FILE_INUSE_EDIT
, dlg_data
->processlist
);
319 switch (dlg_data
->iteration
)
322 break; // show the dialog the way it is in the resource
325 SetDlgItemText (hwndDlg
, IDRETRY
, "&Kill Processes");
326 SetDlgItemText (hwndDlg
, IDC_FILE_INUSE_HELP
,
327 "Select 'Retry' to retry, "
328 "Select 'Kill' to kill processes and retry, or "
329 "select 'Continue' to go on anyway (the file will be updated after a reboot).");
334 SetDlgItemText (hwndDlg
, IDRETRY
, "&Kill Processes");
335 SetDlgItemText (hwndDlg
, IDC_FILE_INUSE_HELP
,
336 "Select 'Retry' to retry, "
337 "select 'Kill' to forcibly kill all processes and retry, or "
338 "select 'Continue' to go on anyway (the file will be updated after a reboot).");
341 return TRUE
; // automatically set focus, please
344 if (HIWORD (wParam
) == BN_CLICKED
)
346 switch (LOWORD (wParam
))
351 EndDialog (hwndDlg
, LOWORD (wParam
));
360 /* Helper function to create the registry value "AllowProtectedRenames",
361 which is required to make MOVEFILE_DELAY_UNTIL_REBOOT to work on WFP
362 protected files. By default, the entire system drive is WFP protected,
363 so a Cygwin installation on this drive sufferes from the WFP problem.
364 Even though this value is only required since Windows Server 2003 SP1,
365 we just set it here unconditionally since it doesn't hurt at all on
368 create_allow_protected_renames ()
373 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE
,
374 "System\\CurrentControlSet\\Control\\Session Manager",
375 0, KEY_ALL_ACCESS
| SETUP_KEY_WOW64
, &key
) == ERROR_SUCCESS
)
376 RegSetValueEx (key
, "AllowProtectedRenames", 0, REG_DWORD
,
377 (BYTE
*) &val
, sizeof (DWORD
));
382 Installer::extract_replace_on_reboot (archive
*tarstream
, const std::string
& prefixURL
,
383 const std::string
& prefixPath
, std::string fn
)
385 /* Extract a copy of the file with extension .new appended and
386 indicate it should be replaced on the next reboot. */
387 if (archive::extract_file (tarstream
, prefixURL
, prefixPath
,
390 Log (LOG_PLAIN
) << "Unable to install file " << prefixURL
391 << prefixPath
<< fn
<< ".new" << endLog
;
397 std::string s
= cygpath ("/" + fn
+ ".new"),
398 d
= cygpath ("/" + fn
);
400 WCHAR sname
[s
.size () + 7];
401 WCHAR dname
[d
.size () + 7];
403 mklongpath (sname
, s
.c_str (), s
.size () + 7);
404 mklongpath (dname
, d
.c_str (), d
.size () + 7);
405 if (!MoveFileExW (sname
, dname
,
406 MOVEFILE_DELAY_UNTIL_REBOOT
|
407 MOVEFILE_REPLACE_EXISTING
))
408 replaceOnRebootFailed (fn
);
411 create_allow_protected_renames ();
412 replaceOnRebootSucceeded (fn
, rebootneeded
);
418 static char all_null
[512];
420 /* install one source at a given prefix. */
422 Installer::installOne (packagemeta
&pkgm
, const packageversion
&ver
,
423 packagesource
&source
,
424 const std::string
& prefixURL
,
425 const std::string
& prefixPath
,
428 if (!source
.Canonical())
430 Progress
.SetText1 (IDS_PROGRESS_INSTALL
);
431 Progress
.SetText2 ((pkgm
.name
+ "-" + ver
.Canonical_version()).c_str());
433 io_stream
*pkgfile
= NULL
;
435 if (!source
.Cached())
437 note (NULL
, IDS_ERR_OPEN_READ
, source
.Canonical (), "Unknown filename");
442 if (!io_stream::exists (source
.Cached ())
443 || !(pkgfile
= io_stream::open (source
.Cached (), "rb", 0)))
445 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (), "No such file");
451 /* At this point pkgfile is an opened io_stream to either a .tar.bz2 file,
452 a .tar.gz file, a .tar.lzma file, or just a .tar file. Try it first as
453 a compressed file and if that fails try opening it as a tar directly.
456 Note on io_stream pointer management:
458 Both the archive and decompress classes take ownership of the io_stream
459 pointer they were opened with, meaning they delete it in their dtor. So
460 deleting tarstream should also result in deleting the underlying
461 try_decompress and pkgfile io_streams as well. */
463 archive
*tarstream
= NULL
;
464 io_stream
*try_decompress
= NULL
;
466 if ((try_decompress
= compress::decompress (pkgfile
)) != NULL
)
468 if ((tarstream
= archive::extract (try_decompress
)) == NULL
)
470 /* Decompression succeeded but we couldn't grok it as a valid tar
474 if ((len
= try_decompress
->peek (c
, 512)) < 0
475 || !memcmp (c
, all_null
, len
))
476 /* In some cases, maintainers have uploaded bzipped
477 0-byte files as dummy packages instead of empty tar files.
478 This is common enough that we should not treat this as an
480 Same goes for tar archives consisting of a big block of
481 all zero bytes (the famous 46 bytes tar archives). */
483 if (ver
.Type () == package_binary
)
484 pkgm
.installed
= ver
;
488 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (),
489 "Invalid or unsupported tar format");
492 delete try_decompress
;
496 else if ((tarstream
= archive::extract (pkgfile
)) == NULL
)
498 /* Not a compressed tarball, not a plain tarball, give up. */
500 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (),
501 "Unrecognisable file format");
506 /* For binary packages, create a manifest in /etc/setup/ that lists the
507 filename of each file that was unpacked. */
509 io_stream
*lst
= NULL
;
510 if (ver
.Type () == package_binary
)
512 std::string lstfn
= "cygfile:///etc/setup/" + pkgm
.name
+ ".lst.gz";
515 if ((tmp
= io_stream::open (lstfn
, "wb", 0644)) == NULL
)
516 Log (LOG_PLAIN
) << "Warning: Unable to create lst file " + lstfn
+
517 " - uninstall of this package will leave orphaned files." << endLog
;
520 lst
= new compress_gz (tmp
, "w9");
525 Log (LOG_PLAIN
) << "Warning: gzip unable to write to lst file " +
526 lstfn
+ " - uninstall of this package will leave orphaned files."
532 bool error_in_this_package
= false;
533 bool ignoreInUseErrors
= false;
534 bool ignoreExtractErrors
= unattended_mode
;
536 package_bytes
= source
.size
;
537 Log (LOG_PLAIN
) << "Extracting from " << source
.Cached () << endLog
;
540 while ((fn
= tarstream
->next_file_name ()).size ())
542 std::string canonicalfn
= prefixPath
+ fn
;
544 // pathnames starting "." (i.e. dotfiles in the root directory) are
545 // reserved for package metadata. Don't extract them.
548 tarstream
->skip_file ();
552 Progress
.SetText3 (canonicalfn
.c_str ());
553 Log (LOG_BABBLE
) << "Installing file " << prefixURL
<< prefixPath
557 std::string tmp
= fn
+ "\n";
558 lst
->write (tmp
.c_str(), tmp
.size());
560 if (Script::isAScript (fn
))
561 pkgm
.addScript (Script (canonicalfn
));
564 archive::extract_results extres
;
565 while ((extres
= archive::extract_file (tarstream
, prefixURL
, prefixPath
)) != archive::extract_ok
)
567 bool error_in_this_file
= false;
571 case archive::extract_inuse
: // in use
573 if (!ignoreInUseErrors
)
575 // convert the file name to long UNC form
576 std::string s
= backslash (cygpath ("/" + fn
));
577 WCHAR sname
[s
.size () + 7];
578 mklongpath (sname
, s
.c_str (), s
.size () + 7);
580 // find any process which has that file loaded into it
581 // (note that this doesn't find when the file is un-writeable because the process has
582 // that file opened exclusively)
583 ProcessList processes
= Process::listProcessesWithModuleLoaded (sname
);
586 for (ProcessList::iterator i
= processes
.begin (); i
!= processes
.end (); i
++)
588 if (i
!= processes
.begin ()) plm
+= "\r\n";
590 std::string processName
= i
->getName ();
591 Log (LOG_BABBLE
) << processName
<< endLog
;
595 INT_PTR rc
= (iteration
< 3) ? IDRETRY
: IDCONTINUE
;
596 if (unattended_mode
== attended
)
598 if (!processes
.empty())
600 // Use the IDD_FILE_INUSE dialog to ask the user if we should try to kill the
601 // listed processes, or just ignore the problem and schedule the file to be
602 // replaced after a reboot
603 FileInuseDlgData dlg_data
;
604 std::string msg
= "Unable to extract /" + fn
;
605 dlg_data
.msg
= msg
.c_str ();
606 dlg_data
.processlist
= plm
.c_str ();
607 dlg_data
.iteration
= iteration
;
609 rc
= DialogBoxParam(hinstance
, MAKEINTRESOURCE (IDD_FILE_INUSE
), owner
, FileInuseDlgProc
, (LPARAM
)&dlg_data
);
613 // We couldn't enumerate any processes which have this file loaded into it
614 // either the cause of the error is something else, or something (e.g security
615 // policy) prevents us from listing those processes.
616 // All we can offer the user is a generic "retry or ignore" choice and a chance
617 // to fix the problem themselves
618 char msg
[fn
.size() + 300];
620 "Unable to extract /%s\r\n\r\n"
621 "The file is in use or some other error occurred.\r\n\r\n"
622 "Please stop all Cygwin processes and select \"Retry\", or "
623 "select \"Continue\" to go on anyway (the file will be updated after a reboot).\r\n",
626 rc
= MessageBox (owner
, msg
, "Error writing file",
627 MB_RETRYCONTINUE
| MB_ICONWARNING
| MB_TASKMODAL
);
634 // manual intervention may have fixed the problem, retry
637 if (!processes
.empty())
639 // try to stop all the processes
640 for (ProcessList::iterator i
= processes
.begin (); i
!= processes
.end (); i
++)
645 // wait up to 15 seconds for processes to stop
646 for (unsigned int i
= 0; i
< 15; i
++)
648 processes
= Process::listProcessesWithModuleLoaded (sname
);
649 if (processes
.size () == 0)
655 // else, manual intervention may have fixed the problem
661 // ignore this in-use error, and any subsequent in-use errors for other files in the same package
662 ignoreInUseErrors
= true;
667 // fall through to previous functionality
670 if (NoReplaceOnReboot
)
673 error_in_this_file
= true;
674 Log (LOG_PLAIN
) << "Not replacing in-use file " << prefixURL
675 << prefixPath
<< fn
<< endLog
;
679 error_in_this_file
= extract_replace_on_reboot(tarstream
, prefixURL
, prefixPath
, fn
);
683 case archive::extract_other
: // extract failed
685 if (!ignoreExtractErrors
)
687 char msg
[fn
.size() + 300];
689 "Unable to extract /%s -- corrupt package?\r\n",
692 // XXX: We should offer the option to retry,
693 // continue without extracting this particular archive,
694 // or ignore all extraction errors.
695 // Unfortunately, we don't currently know how to rewind
696 // the archive, so we can't retry at present,
697 // and ignore all errors is mis-implemented at present
698 // to only apply to errors arising from a single archive,
699 // so we degenerate to the continue option.
700 mbox (owner
, msg
, "File extraction error",
701 MB_OK
| MB_ICONWARNING
| MB_TASKMODAL
);
704 error_in_this_file
= true;
707 case archive::extract_ok
:
711 // We're done with this file
713 // if an error occured ...
714 if (error_in_this_file
)
716 // skip to next file in archive
717 tarstream
->skip_file();
718 // don't mark this package as successfully installed
719 error_in_this_package
= true;
724 progress (pkgfile
->tell ());
732 total_bytes_sofar
+= package_bytes
;
735 int df
= diskfull (get_root_dir ().c_str ());
736 Progress
.SetBar3 (df
);
738 if (ver
.Type () == package_binary
&& !error_in_this_package
)
739 pkgm
.installed
= ver
;
743 check_for_old_cygwin (HWND owner
)
745 /* Paths within system dir expected to be always < MAX_PATH. */
746 char buf
[MAX_PATH
+ sizeof ("\\cygwin1.dll")];
747 if (!GetSystemDirectory (buf
, sizeof (buf
)))
749 strcat (buf
, "\\cygwin1.dll");
750 if (_access (buf
, 0) != 0)
753 char msg
[sizeof (buf
) + 132];
755 "An old version of cygwin1.dll was found here:\r\n%s\r\nDelete?",
758 (owner
, msg
, "What's that doing there?",
759 MB_YESNO
| MB_ICONQUESTION
| MB_TASKMODAL
))
762 if (!DeleteFile (buf
))
764 sprintf (msg
, "Couldn't delete file %s.\r\n"
765 "Is the DLL in use by another application?\r\n"
766 "You should delete the old version of cygwin1.dll\r\n"
767 "at your earliest convenience.", buf
);
768 mbox (owner
, buf
, "Couldn't delete file",
769 MB_OK
| MB_ICONEXCLAMATION
| MB_TASKMODAL
);
780 do_install_thread (HINSTANCE h
, HWND owner
)
784 num_installs
= 0, num_uninstalls
= 0;
785 rebootneeded
= false;
787 io_stream::mkpath_p (PATH_TO_DIR
,
788 std::string("file://") + std::string(get_root_dir()),
791 for (i
= 0; Installer::StandardDirs
[i
].name
; i
++)
793 std::string p
= cygpath (Installer::StandardDirs
[i
].name
);
795 io_stream::mkpath_p (PATH_TO_DIR
, "file://" + p
,
796 Installer::StandardDirs
[i
].mode
);
799 /* Create /var/run/utmp */
800 io_stream
*utmp
= io_stream::open ("cygfile:///var/run/utmp", "wb", 0666);
803 Installer myInstaller
;
804 myInstaller
.initDialog();
807 total_bytes_sofar
= 0;
809 int df
= diskfull (get_root_dir ().c_str());
810 Progress
.SetBar3 (df
);
812 /* Writes Cygwin/setup/rootdir registry value */
813 create_install_root ();
815 std::vector
<packageversion
> install_q
, uninstall_q
, sourceinstall_q
;
818 const SolverTransactionList
&t
= db
.solution
.transactions();
820 /* Calculate the amount of data to md5sum */
821 Progress
.SetText1(IDS_PROGRESS_CALCULATING
);
822 long long int md5sum_total_bytes
= 0;
823 for (SolverTransactionList::const_iterator i
= t
.begin (); i
!= t
.end (); ++i
)
825 packageversion version
= i
->version
;
827 if (i
->type
== SolverTransaction::transInstall
)
829 md5sum_total_bytes
+= version
.source()->size
;
833 /* md5sum the packages, build lists of packages to install and uninstall
834 and calculate the total amount of data to install.
835 The hash checking is relevant only for local installs. For a
836 net install, the hashes will have already been verified at download
837 time, and all calls to check_hash() below should instantly return. */
838 long long int md5sum_total_bytes_sofar
= 0;
839 for (SolverTransactionList::const_iterator i
= t
.begin (); i
!= t
.end (); ++i
)
841 packageversion version
= i
->version
;
843 if (i
->type
== SolverTransaction::transInstall
)
847 (*version
.source ()).check_hash ();
851 // We used to give the user a yes/no option to skip this
852 // package (with "no" meaning install it even though the
853 // archive is corrupt), but both options could damage the
854 // user's system. In the absence of a safe way to recover, we
856 if (e
->errNo() == APPERR_CORRUPT_PACKAGE
)
857 fatal (owner
, IDS_CORRUPT_PACKAGE
, version
.Name().c_str());
858 // Unexpected exception.
862 md5sum_total_bytes_sofar
+= version
.source()->size
;
863 total_bytes
+= version
.source()->size
;
865 // source packages are kept in a separate queue as they are installed
866 // differently: root is /usr/src, install isn't recorded, etc.
867 if (version
.Type() == package_source
)
868 sourceinstall_q
.push_back (version
);
870 install_q
.push_back (version
);
874 /* Uninstall, upgrade or reinstall */
875 if (i
->type
== SolverTransaction::transErase
)
877 uninstall_q
.push_back (version
);
880 if (md5sum_total_bytes
> 0)
881 Progress
.SetBar2 (md5sum_total_bytes_sofar
, md5sum_total_bytes
);
884 /* start with uninstalls - remove files that new packages may replace */
886 for (std::vector
<packageversion
>::iterator i
= uninstall_q
.begin ();
887 i
!= uninstall_q
.end (); ++i
)
889 packagemeta
*pkgm
= db
.findBinary (PackageSpecification(i
->Name()));
891 myInstaller
.preremoveOne (*pkgm
);
892 Progress
.SetBar2(std::distance(uninstall_q
.begin(), i
) + 1, uninstall_q
.size());
896 for (std::vector
<packageversion
>::iterator i
= uninstall_q
.begin ();
897 i
!= uninstall_q
.end (); ++i
)
899 packagemeta
*pkgm
= db
.findBinary (PackageSpecification(i
->Name()));
901 myInstaller
.uninstallOne (*pkgm
);
902 Progress
.SetBar2(std::distance(uninstall_q
.begin(), i
) + 1, uninstall_q
.size());
905 for (std::vector
<packageversion
>::iterator i
= install_q
.begin ();
906 i
!= install_q
.end (); ++i
)
908 packageversion
& pkg
= *i
;
909 packagemeta
*pkgm
= db
.findBinary (PackageSpecification(i
->Name()));
912 myInstaller
.installOne (*pkgm
, pkg
, *pkg
.source(),
913 "cygfile://", "/", owner
);
915 catch (std::exception
*e
)
917 if (yesno (owner
, IDS_INSTALL_ERROR
, e
->what()) != IDYES
)
920 << "User cancelled setup after install error" << endLog
;
927 for (std::vector
<packageversion
>::iterator i
= sourceinstall_q
.begin ();
928 i
!= sourceinstall_q
.end (); ++i
)
930 packagemeta
*pkgm
= db
.findSource (PackageSpecification(i
->Name()));
931 packageversion
& pkg
= *i
;
932 myInstaller
.installOne (*pkgm
, pkg
, *pkg
.source(),
933 "cygfile://", "/usr/src/", owner
);
937 note (owner
, IDS_REBOOT_REQUIRED
);
940 if ((temperr
= db
.flush ()))
942 const char *err
= strerror (temperr
);
944 err
= "(unknown error)";
945 fatal (owner
, IDS_ERR_OPEN_WRITE
, "Package Database",
949 if (!myInstaller
.errors
)
950 check_for_old_cygwin (owner
);
951 if (num_installs
== 0 && num_uninstalls
== 0)
953 if (!unattended_mode
)
954 Logger ().setExitMsg (IDS_NOTHING_INSTALLED
);
957 if (num_installs
== 0)
959 if (!unattended_mode
)
960 Logger ().setExitMsg (IDS_UNINSTALL_COMPLETE
);
964 if (myInstaller
.errors
)
965 Logger ().setExitMsg (IDS_INSTALL_INCOMPLETE
);
966 else if (!unattended_mode
)
967 Logger ().setExitMsg (IDS_INSTALL_COMPLETE
);
970 Logger ().setExitMsg (IDS_REBOOT_REQUIRED
);
974 do_install_reflector (void *p
)
977 context
= (HANDLE
*) p
;
981 do_install_thread ((HINSTANCE
) context
[0], (HWND
) context
[1]);
983 // Tell the progress page that we're done downloading
984 Progress
.PostMessageNow (WM_APP_INSTALL_THREAD_COMPLETE
);
986 TOPLEVEL_CATCH((HWND
) context
[1], "install");
991 static HANDLE context
[2];
994 do_install (HINSTANCE h
, HWND owner
)
1000 CreateThread (NULL
, 0, do_install_reflector
, context
, 0, &threadID
);