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 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
);
80 class PerpetualRemoveFindVisitor
: public FindVisitor
83 PerpetualRemoveFindVisitor (std::vector
<Script
> *scripts
, const std::string
& 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
;
92 if (script
.is_p(stratum
))
93 _scripts
->push_back(Script (fn
));
95 virtual ~ PerpetualRemoveFindVisitor () {}
97 PerpetualRemoveFindVisitor (PerpetualRemoveFindVisitor
const &);
98 PerpetualRemoveFindVisitor
& operator= (PerpetualRemoveFindVisitor
const &);
100 std::vector
<Script
> *_scripts
;
101 const std::string stratum
;
107 static std_dirs_t StandardDirs
[];
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
);
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
,
133 Installer::Installer() : errors(0)
138 Installer::initDialog()
140 Progress
.SetText2 ("");
141 Progress
.SetText3 ("");
145 Installer::progress (off_t bytes
)
147 if (package_bytes
> 0)
148 Progress
.SetBar1 (bytes
, package_bytes
);
151 Progress
.SetBar2 (total_bytes_sofar
+ bytes
, total_bytes
);
155 Installer::StandardDirs
[] = {
158 { "/dev/mqueue", 01777 },
159 { "/dev/shm", 01777 },
161 { "/etc/fstab.d", 01777 },
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 },
180 static int num_installs
, num_uninstalls
;
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())
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());
198 Progress
.SetText3 ("");
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
]);
214 Installer::uninstallOne (packagemeta
& pkg
)
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
);
230 char getfilenamebuffer
[CYG_PATH_MAX
];
231 const char *sz
= listdata
->gets (getfilenamebuffer
, sizeof (getfilenamebuffer
));
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.
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
);
260 /* Check for Windows shortcut of same name. */
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
);
273 /* Remove the listing file */
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())
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();
294 /* log failed scheduling of replace-on-reboot of a given file. */
295 /* also increment errors. */
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
;
305 /* log successful scheduling of replace-on-reboot of a given file. */
306 /* also set rebootneeded. */
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
;
318 const char *processlist
;
322 static INT_PTR CALLBACK
323 FileInuseDlgProc (HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
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
)
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
);
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());
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
360 if (HIWORD (wParam
) == BN_CLICKED
)
362 switch (LOWORD (wParam
))
367 EndDialog (hwndDlg
, LOWORD (wParam
));
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
384 create_allow_protected_renames ()
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
));
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
,
406 Log (LOG_PLAIN
) << "Unable to install file " << prefixURL
407 << prefixPath
<< fn
<< ".new" << endLog
;
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
);
427 create_allow_protected_renames ();
428 replaceOnRebootSucceeded (fn
, rebootneeded
);
434 static char all_null
[512];
436 /* install one source at a given prefix. */
438 Installer::installOne (packagemeta
&pkgm
, const packageversion
&ver
,
439 packagesource
&source
,
440 const std::string
& prefixURL
,
441 const std::string
& prefixPath
,
444 if (!source
.Canonical())
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");
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");
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.
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
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
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
;
504 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (),
505 "Invalid or unsupported tar format");
508 delete try_decompress
;
512 else if ((tarstream
= archive::extract (pkgfile
)) == NULL
)
514 /* Not a compressed tarball, not a plain tarball, give up. */
516 note (NULL
, IDS_ERR_OPEN_READ
, source
.Cached (),
517 "Unrecognisable file format");
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";
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
;
536 lst
= new compress_gz (tmp
, "w9");
541 Log (LOG_PLAIN
) << "Warning: gzip unable to write to lst file " +
542 lstfn
+ " - uninstall of this package will leave orphaned files."
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);
563 total_bytes_sofar
+= package_bytes
;
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
;
574 Installer::_installOne (packagemeta
&pkgm
,
575 const std::string
& prefixURL
,
576 const std::string
& prefixPath
,
583 bool error_in_this_package
= false;
584 bool ignoreInUseErrors
= false;
585 bool ignoreExtractErrors
= unattended_mode
;
588 while ((fn
= tarstream
->next_file_name ()).size ())
590 if (symlink_phase
!= (tarstream
->next_file_type () == ARCHIVE_FILE_SYMLINK
))
592 tarstream
->skip_file();
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.
602 tarstream
->skip_file ();
606 Progress
.SetText3 (canonicalfn
.c_str ());
607 Log (LOG_BABBLE
) << "Installing file " << prefixURL
<< prefixPath
611 std::string tmp
= fn
+ "\n";
612 lst
->write (tmp
.c_str(), tmp
.size());
614 if (Script::isAScript (fn
))
615 pkgm
.addScript (Script (canonicalfn
));
618 archive::extract_results extres
;
619 while ((extres
= archive::extract_file (tarstream
, prefixURL
, prefixPath
)) != archive::extract_ok
)
621 bool error_in_this_file
= false;
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
);
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
;
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
);
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
,
681 // manual intervention may have fixed the problem, retry
684 if (!processes
.empty())
686 // try to stop all the processes
687 for (ProcessList::iterator i
= processes
.begin (); i
!= processes
.end (); i
++)
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)
702 // else, manual intervention may have fixed the problem
708 // ignore this in-use error, and any subsequent in-use errors for other files in the same package
709 ignoreInUseErrors
= true;
714 // fall through to previous functionality
717 if (NoReplaceOnReboot
)
720 error_in_this_file
= true;
721 Log (LOG_PLAIN
) << "Not replacing in-use file " << prefixURL
722 << prefixPath
<< fn
<< endLog
;
726 error_in_this_file
= extract_replace_on_reboot(tarstream
, prefixURL
, prefixPath
, fn
);
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
,
747 error_in_this_file
= true;
750 case archive::extract_ok
:
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;
767 progress (pkgfile
->tell ());
771 return error_in_this_package
;
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
)))
781 strcat (buf
, "\\cygwin1.dll");
782 if (_access (buf
, 0) != 0)
785 switch (mbox(owner
, IDS_INSTALL_OLD_CYGWIN
,
786 MB_YESNO
| MB_ICONQUESTION
| MB_TASKMODAL
,
790 if (!DeleteFile (buf
))
792 mbox (owner
, IDS_INSTALL_DELETE_OLD_CYGWIN_FAILED
,
793 MB_OK
| MB_ICONEXCLAMATION
| MB_TASKMODAL
,
805 do_install_thread (HINSTANCE h
, HWND owner
)
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()),
816 for (i
= 0; Installer::StandardDirs
[i
].name
; i
++)
818 std::string p
= cygpath (Installer::StandardDirs
[i
].name
);
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);
828 Installer myInstaller
;
829 myInstaller
.initDialog();
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
;
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 ();
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
882 if (e
->errNo() == APPERR_CORRUPT_PACKAGE
)
883 fatal (owner
, IDS_CORRUPT_PACKAGE
, version
.Name().c_str());
884 // Unexpected exception.
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
);
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 */
912 myInstaller
.preremovePerpetual ("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()));
920 myInstaller
.preremoveOne (*pkgm
);
921 Progress
.SetBar2(std::distance(uninstall_q
.begin(), i
) + 1, uninstall_q
.size());
925 myInstaller
.preremovePerpetual ("z");
928 for (std::vector
<packageversion
>::iterator i
= uninstall_q
.begin ();
929 i
!= uninstall_q
.end (); ++i
)
931 packagemeta
*pkgm
= db
.findBinary (PackageSpecification(i
->Name()));
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()));
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
)
952 << "User cancelled setup after install error" << endLog
;
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
);
969 note (owner
, IDS_REBOOT_REQUIRED
);
972 if ((temperr
= db
.flush ()))
974 const char *err
= strerror (temperr
);
976 err
= "(unknown error)";
977 fatal (owner
, IDS_ERR_OPEN_WRITE
, "Package Database",
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
);
989 if (num_installs
== 0)
991 if (!unattended_mode
)
992 Logger ().setExitMsg (IDS_UNINSTALL_COMPLETE
);
996 if (myInstaller
.errors
)
997 Logger ().setExitMsg (IDS_INSTALL_INCOMPLETE
);
998 else if (!unattended_mode
)
999 Logger ().setExitMsg (IDS_INSTALL_COMPLETE
);
1002 Logger ().setExitMsg (IDS_REBOOT_REQUIRED
);
1006 do_install_reflector (void *p
)
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");
1025 static HANDLE context
[2];
1028 do_install (HINSTANCE h
, HWND owner
)
1034 CreateThread (NULL
, 0, do_install_reflector
, context
, 0, &threadID
);