Revision: 118303 https://trac.macports.org/changeset/118303 Author: cal@macports.org Date: 2014-03-29 12:20:00 -0700 (Sat, 29 Mar 2014) Log Message: ----------- base: prevent further race conditions with SIGINT and SIGTERM during activation and deactivation where possible Modified Paths: -------------- trunk/base/src/registry2.0/portimage.tcl Modified: trunk/base/src/registry2.0/portimage.tcl =================================================================== --- trunk/base/src/registry2.0/portimage.tcl 2014-03-29 16:41:17 UTC (rev 118302) +++ trunk/base/src/registry2.0/portimage.tcl 2014-03-29 19:20:00 UTC (rev 118303) @@ -134,7 +134,6 @@ ui_msg "$UI_PREFIX [format [msgcat::mc "Activating %s @%s"] $name $specifier]" _activate_contents $requested - $requested state installed } # takes a composite version spec rather than separate version,revision,variants @@ -201,7 +200,6 @@ } _deactivate_contents $requested [$requested files] $force - $requested state imaged } proc _check_registry {name version revision variants} { @@ -541,6 +539,11 @@ lappend rollback_filelist $file } } + + # Recording that the port has been activated should be done + # here so that this information cannot be inconsistent with the + # state of the files on disk. + $port state installed } catch {{POSIX SIG SIGINT} eCode eMessage} { # Pressing ^C will (often?) print "^C" to the terminal; send # a linebreak so our message appears after that. @@ -563,30 +566,40 @@ } } catch {*} { # This code must run to completion, or the installation might be left - # in an inconsistent state - signal block {TERM INT} + # in an inconsistent state. We store the old signal handling state, + # block the critical signals and restore to the previous state instead + # of unblocking. + # Note that this still contains a race condition: A user could press ^C + # fast enough so that the second error arrives before the error is + # caught, re-thrown and re-caught here. As far as I can see, there's no + # easy way around this problem. + set osignals [signal get {TERM INT}] + try { + # Block signals to avoid inconsistiencies. + signal block {TERM INT} - # roll back activation of this port - if {[info exists deactivate_this]} { - _deactivate_contents $port $rollback_filelist yes yes - } - # if any errors occurred, move backed-up files back to their original - # locations, then rethrow the error. Transaction rollback will take care - # of this in the registry. - foreach file $backups { - ::file rename -force -- "${file}${baksuffix}" $file - } - # reactivate deactivated ports - foreach entry [array names todeactivate] { - if {[$entry state] eq "imaged" && ($noexec || ![registry::run_target $entry activate ""])} { - activate [$entry name] [$entry version] [$entry revision] [$entry variants] [list ports_activate_no-exec $noexec] + # roll back activation of this port + if {[info exists deactivate_this]} { + _deactivate_contents $port $rollback_filelist yes yes } + # if any errors occurred, move backed-up files back to their original + # locations, then rethrow the error. Transaction rollback will take care + # of this in the registry. + foreach file $backups { + ::file rename -force -- "${file}${baksuffix}" $file + } + # reactivate deactivated ports + foreach entry [array names todeactivate] { + if {[$entry state] eq "imaged" && ($noexec || ![registry::run_target $entry activate ""])} { + activate [$entry name] [$entry version] [$entry revision] [$entry variants] [list ports_activate_no-exec $noexec] + } + } + } finally { + # We've completed all critical operations, re-enable the TERM and + # INT signals. + signal set $osignals } - # We've completed all critical operations, re-enable the TERM and INT - # signals. - signal unblock {TERM INT} - # remove temp image dir ::file delete -force $extracted_dir throw @@ -660,18 +673,38 @@ # are after their elements. set files [lsort -decreasing -unique $files] - # Remove all elements. - if {!$rollback} { - registry::write { - $port deactivate $imagefiles + # Avoid interruptions while removing the files and updating the database to + # prevent inconsistencies from forming between filesystem and database. + set osignals [signal get {TERM INT}] + + try { + # Block the TERM and INT signals to avoid being interrupted. Note that + # they might already be block at this point because + # _deactivate_contents might be called during rollback of + # _activate_contents, but because we're storing the old signal state + # and returning to that instead of unblocking it doesn't matter. + signal block {TERM INT} + + # Remove all elements. + if {!$rollback} { + registry::write { + $port deactivate $imagefiles + foreach file $files { + _deactivate_file $file + } + + # Update the port's state in the same transaction as the file + # delete operations. + $port state imaged + } + } else { foreach file $files { _deactivate_file $file } } - } else { - foreach file $files { - _deactivate_file $file - } + } finally { + # restore the signal block state + signal set $osignals } }
participants (1)
-
cal@macports.org