<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[126893] branches/release_2_3/base</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="https://trac.macports.org/changeset/126893">126893</a></dd>
<dt>Author</dt> <dd>cal@macports.org</dd>
<dt>Date</dt> <dd>2014-10-16 15:17:14 -0700 (Thu, 16 Oct 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>merge <a href="https://trac.macports.org/changeset/124216">r124216</a> from trunk: trace mode: track unknown files in $prefix separately, refactor port1.0/porttrace.tcl

 - Separate sandbox violations in violations (file access denied) and unknowns
   (file access granted but tracked, because the file is in $prefix but not
   installed by a port).
 - Add error handling to Tcl upcalls in pextlib1.0/tracelib.c,
   sandbox_violation()
 - Refactor port1.0/porttrace.tcl and use namespace-local variables instead of
   globals, remove unused methods</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#branchesrelease_2_3basesrcdarwintracelib10darwintracec">branches/release_2_3/base/src/darwintracelib1.0/darwintrace.c</a></li>
<li><a href="#branchesrelease_2_3basesrcpextlib10tracelibc">branches/release_2_3/base/src/pextlib1.0/tracelib.c</a></li>
<li><a href="#branchesrelease_2_3basesrcport10porttracetcl">branches/release_2_3/base/src/port1.0/porttrace.tcl</a></li>
</ul>

<h3>Property Changed</h3>
<ul>
<li><a href="#branchesrelease_2_3base">branches/release_2_3/base/</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="branchesrelease_2_3base"></a>
<div class="propset"><h4>Property changes: branches/release_2_3/base</h4>
<pre class="diff"><span>
</span></pre></div>
<a id="svnmergeinfo"></a>
<div class="modfile"><h4>Modified: svn:mergeinfo</h4></div>
<span class="cx">/branches/gsoc09-logging/base:51231-60371
</span><span class="cx">/branches/gsoc11-rev-upgrade/base:78828-88375
</span><span class="cx">/branches/gsoc11-statistics/base:79520,79666
</span><span class="cx">/branches/gsoc13-tests:106692-111324
</span><span class="cx">/branches/universal-sanity/base:51872-52323
</span><span class="cx">/branches/variant-descs-14482/base:34469-34855,34900-37508,37511-37512,41040-41463,42575-42626,42640-42659
</span><span class="cx">/trunk/base:118038-118039,118056,118085,118161,118559,118562-118569,118598-118599,118602-118603,118606-118607,118640,118735,119034,119169,119171,119175,119204,119297,119987,119992,120036,120038,120059-120060,120064,120067-120069,120074,120076,120127,120132,120142,120345,120382,120637,121311,121364,121451,121485,123652,124145-124146,124150,125578,125621,125859,126866,126868
</span><span class="cx">/users/perry/base-bugs_and_notes:45682-46060
</span><span class="cx">/users/perry/base-select:44044-44692
</span><span class="cx">   + /branches/gsoc08-privileges/base:37343-46937
</span><span class="cx">/branches/gsoc09-logging/base:51231-60371
</span><span class="cx">/branches/gsoc11-rev-upgrade/base:78828-88375
</span><span class="cx">/branches/gsoc11-statistics/base:79520,79666
</span><span class="cx">/branches/gsoc13-tests:106692-111324
</span><span class="cx">/branches/universal-sanity/base:51872-52323
</span><span class="cx">/branches/variant-descs-14482/base:34469-34855,34900-37508,37511-37512,41040-41463,42575-42626,42640-42659
</span><span class="cx">/trunk/base:118038-118039,118056,118085,118161,118559,118562-118569,118598-118599,118602-118603,118606-118607,118640,118735,119034,119169,119171,119175,119204,119297,119987,119992,120036,120038,120059-120060,120064,120067-120069,120074,120076,120127,120132,120142,120345,120382,120637,121311,121364,121451,121485,123652,124145-124146,124150,124216,125578,125621,125859,126866,126868
</span><span class="cx">/users/perry/base-bugs_and_notes:45682-46060
</span><span class="cx">/users/perry/base-select:44044-44692
</span><a id="branchesrelease_2_3basesrcdarwintracelib10darwintracec"></a>
<div class="modfile"><h4>Modified: branches/release_2_3/base/src/darwintracelib1.0/darwintrace.c (126892 => 126893)</h4>
<pre class="diff"><span>
<span class="info">--- branches/release_2_3/base/src/darwintracelib1.0/darwintrace.c        2014-10-16 22:12:04 UTC (rev 126892)
+++ branches/release_2_3/base/src/darwintracelib1.0/darwintrace.c        2014-10-16 22:17:14 UTC (rev 126893)
</span><span class="lines">@@ -657,7 +657,7 @@
</span><span class="cx">                                                         // access anyway, but report a sandbox violation.
</span><span class="cx">                                                         // TODO find a better solution
</span><span class="cx">                                                         if ((flags &amp; DT_REPORT) &gt; 0) {
</span><del>-                                                                __darwintrace_log_op(&quot;sandbox_violation&quot;, path);
</del><ins>+                                                                __darwintrace_log_op(&quot;sandbox_unknown&quot;, path);
</ins><span class="cx">                                                         }
</span><span class="cx">                                                         return true;
</span><span class="cx">                                                 case 0:
</span></span></pre></div>
<a id="branchesrelease_2_3basesrcpextlib10tracelibc"></a>
<div class="modfile"><h4>Modified: branches/release_2_3/base/src/pextlib1.0/tracelib.c (126892 => 126893)</h4>
<pre class="diff"><span>
<span class="info">--- branches/release_2_3/base/src/pextlib1.0/tracelib.c        2014-10-16 22:12:04 UTC (rev 126892)
+++ branches/release_2_3/base/src/pextlib1.0/tracelib.c        2014-10-16 22:17:14 UTC (rev 126893)
</span><span class="lines">@@ -99,7 +99,13 @@
</span><span class="cx"> 
</span><span class="cx"> static void send_file_map(int sock);
</span><span class="cx"> static void dep_check(int sock, char *path);
</span><del>-static void sandbox_violation(int sock, const char *path);
</del><ins>+
+typedef enum {
+    SANDBOX_UNKNOWN,
+    SANDBOX_VIOLATION
+} sandbox_violation_t;
+static void sandbox_violation(int sock, const char *path, sandbox_violation_t type);
+
</ins><span class="cx"> static void ui_warn(const char *format, ...) __printflike(1, 2);
</span><span class="cx"> #if 0
</span><span class="cx"> static void ui_info(const char *format, ...) __printflike(1, 2);
</span><span class="lines">@@ -108,7 +114,6 @@
</span><span class="cx"> 
</span><span class="cx"> #define MAX_SOCKETS (1024)
</span><span class="cx"> #define BUFSIZE     (4096)
</span><del>-#define CANARY      (0xdeadbeef)
</del><span class="cx"> 
</span><span class="cx"> /**
</span><span class="cx">  * send a buffer \c buf with the given length \c size to the socket \c sock, by
</span><span class="lines">@@ -335,8 +340,10 @@
</span><span class="cx"> 
</span><span class="cx">     if (strcmp(buf, &quot;filemap&quot;) == 0) {
</span><span class="cx">         send_file_map(sock);
</span><ins>+    } else if (strcmp(buf, &quot;sandbox_unknown&quot;) == 0) {
+        sandbox_violation(sock, f, SANDBOX_UNKNOWN);
</ins><span class="cx">     } else if (strcmp(buf, &quot;sandbox_violation&quot;) == 0) {
</span><del>-        sandbox_violation(sock, f);
</del><ins>+        sandbox_violation(sock, f, SANDBOX_VIOLATION);
</ins><span class="cx">     } else if (strcmp(buf, &quot;dep_check&quot;) == 0) {
</span><span class="cx">         dep_check(sock, f);
</span><span class="cx">     } else {
</span><span class="lines">@@ -369,9 +376,22 @@
</span><span class="cx">  * \param[in] sock socket reporting the violation; unused.
</span><span class="cx">  * \param[in] path the offending path to be passed to the callback
</span><span class="cx">  */
</span><del>-static void sandbox_violation(int sock UNUSED, const char *path) {
</del><ins>+static void sandbox_violation(int sock UNUSED, const char *path, sandbox_violation_t type) {
</ins><span class="cx">     Tcl_SetVar(interp, &quot;path&quot;, path, 0);
</span><del>-    Tcl_Eval(interp, &quot;slave_add_sandbox_violation $path&quot;);
</del><ins>+    int retVal = TCL_OK;
+    switch (type) {
+        case SANDBOX_VIOLATION:
+            retVal = Tcl_Eval(interp, &quot;slave_add_sandbox_violation $path&quot;);
+            break;
+        case SANDBOX_UNKNOWN:
+            retVal = Tcl_Eval(interp, &quot;slave_add_sandbox_unknown $path&quot;);
+            break;
+    }
+
+    if (retVal != TCL_OK) {
+        fprintf(stderr, &quot;Error evaluating Tcl statement to add sandbox violation: %s\n&quot;, Tcl_GetStringResult(interp));
+    }
+
</ins><span class="cx">     Tcl_UnsetVar(interp, &quot;path&quot;, 0);
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="branchesrelease_2_3basesrcport10porttracetcl"></a>
<div class="modfile"><h4>Modified: branches/release_2_3/base/src/port1.0/porttrace.tcl (126892 => 126893)</h4>
<pre class="diff"><span>
<span class="info">--- branches/release_2_3/base/src/port1.0/porttrace.tcl        2014-10-16 22:12:04 UTC (rev 126892)
+++ branches/release_2_3/base/src/port1.0/porttrace.tcl        2014-10-16 22:17:14 UTC (rev 126893)
</span><span class="lines">@@ -37,6 +37,25 @@
</span><span class="cx"> package require portutil 1.0
</span><span class="cx"> 
</span><span class="cx"> namespace eval porttrace {
</span><ins>+        ##
+        # The fifo currently used as server socket to establish communication
+        # between traced processes and the server-side of trace mode.
+        variable fifo
+
+        ##
+        # The Tcl thread that runs the server side of trace mode and deals with
+        # requests from traced processes.
+        variable thread
+
+        ##
+        # A list of files to which access was denied by trace mode.
+        variable sandbox_violation_list [list]
+
+        ##
+        # A list of files inside the MacPorts prefix but unknown to MacPorts that
+        # were used by the current trace session.
+        variable sandbox_unknown_list [list]
+
</ins><span class="cx">     proc appendEntry {sandbox path action} {
</span><span class="cx">         upvar 2 $sandbox sndbxlst
</span><span class="cx"> 
</span><span class="lines">@@ -85,382 +104,388 @@
</span><span class="cx">     proc ask {sandbox path} {
</span><span class="cx">         appendEntry $sandbox $path &quot;?&quot;
</span><span class="cx">     }
</span><del>-}
</del><span class="cx"> 
</span><del>-proc porttrace::trace_start {workpath} {
-    global prefix os.platform developer_dir macportsuser
-    if {${os.platform} == &quot;darwin&quot;} {
-        if {[catch {package require Thread} error]} {
-            ui_warn &quot;trace requires Tcl Thread package ($error)&quot;
-        } else {
-            global env trace_fifo trace_sandboxbounds portpath distpath altprefix
-            # Create a fifo.
-            # path in unix socket limited to 109 chars
-            # # set trace_fifo &quot;$workpath/trace_fifo&quot;
-            set trace_fifo &quot;/tmp/macports_trace_[pid]-[expr {int(rand()*1000)}]&quot;
-            file delete -force $trace_fifo
</del><ins>+        ##
+        # Start a trace mode session with the given $workpath. Creates a thread to
+        # handle requests from traced processes and sets up the sandbox bounds. You
+        # must call trace_stop once for each call to trace_start after you're done
+        # tracing processes.
+        #
+        # @param workpath The $workpath of the current installation
+        proc trace_start {workpath} {
+                global \
+                        altprefix developer_dir distpath env macportsuser os.platform \
+                        portpath prefix
</ins><span class="cx"> 
</span><del>-            # Create the thread/process.
-            create_slave $workpath $trace_fifo
</del><ins>+                variable fifo
</ins><span class="cx"> 
</span><del>-            # Launch darwintrace.dylib.
</del><ins>+                if {[catch {package require Thread} error]} {
+                        ui_warn &quot;Trace mode requires Tcl Thread package ($error)&quot;
+                        return 0
+                }
</ins><span class="cx"> 
</span><del>-            set tracelib_path [file join ${portutil::autoconf::tcl_package_path} darwintrace1.0 darwintrace.dylib]
</del><ins>+                # Select a name for the socket to be used to communicate with the
+                # processes being traced. Note that Unix sockets are limited to 109
+                # characters and that the the macports user must be able to connect to
+                # the socket (and in case of non-root installations, the current user,
+                # too). We're not prefixing the path in /tmp with a separate
+                # macports-specific directory, because the might not be writable by all
+                # users.
+                set fifo &quot;/tmp/macports-trace-[pid]-[expr {int(rand() * 10000)}]&quot;
</ins><span class="cx"> 
</span><del>-            if {[info exists env(DYLD_INSERT_LIBRARIES)] &amp;&amp; [string length &quot;$env(DYLD_INSERT_LIBRARIES)&quot;] &gt; 0} {
-                set env(DYLD_INSERT_LIBRARIES) &quot;${env(DYLD_INSERT_LIBRARIES)}:${tracelib_path}&quot;
-            } else {
-                set env(DYLD_INSERT_LIBRARIES) ${tracelib_path}
-            }
-            set env(DARWINTRACE_LOG) &quot;$trace_fifo&quot;
</del><ins>+                # Make sure the socket doesn't exist yet (this would cause errors
+                # later)
+                file delete -force $fifo
</ins><span class="cx"> 
</span><del>-            # The sandbox is limited to:
-            set trace_sandbox [list]
</del><ins>+                # Create the server-side of the trace socket; this will handle requests
+                # from the traced processed.
+                create_slave $workpath $fifo
</ins><span class="cx"> 
</span><del>-            # Allow work-, port-, and distpath
-            allow trace_sandbox $workpath
-            allow trace_sandbox $portpath
-            allow trace_sandbox $distpath
</del><ins>+                # Launch darwintrace.dylib.
+                set tracelib [file join ${portutil::autoconf::tcl_package_path} darwintrace1.0 darwintrace.dylib]
</ins><span class="cx"> 
</span><del>-            # Allow standard system directories
-            allow trace_sandbox &quot;/bin&quot;
-            allow trace_sandbox &quot;/sbin&quot;
-            allow trace_sandbox &quot;/dev&quot;
-            allow trace_sandbox &quot;/usr/bin&quot;
-            allow trace_sandbox &quot;/usr/sbin&quot;
-            allow trace_sandbox &quot;/usr/include&quot;
-            allow trace_sandbox &quot;/usr/lib&quot;
-            allow trace_sandbox &quot;/usr/libexec&quot;
-            allow trace_sandbox &quot;/usr/share&quot;
-            allow trace_sandbox &quot;/System/Library&quot;
-            # Deny /Library/Frameworks, third parties install there
-            deny  trace_sandbox &quot;/Library/Frameworks&quot;
-            # But allow the rest of /Library
-            allow trace_sandbox &quot;/Library&quot;
</del><ins>+                # Add darwintrace.dylib as last entry in DYLD_INSERT_LIBRARIES
+                if {[info exists env(DYLD_INSERT_LIBRARIES)] &amp;&amp; [string length $env(DYLD_INSERT_LIBRARIES)] &gt; 0} {
+                        set env(DYLD_INSERT_LIBRARIES) &quot;${env(DYLD_INSERT_LIBRARIES)}:${tracelib}&quot;
+                } else {
+                        set env(DYLD_INSERT_LIBRARIES) ${tracelib}
+                }
+                # Tell traced processes where to find their communication socket back
+                # to this code.
+                set env(DARWINTRACE_LOG) $fifo
</ins><span class="cx"> 
</span><del>-            # Allow a few configuration files
-            allow trace_sandbox &quot;/etc/passwd&quot;
-            allow trace_sandbox &quot;/etc/groups&quot;
-            allow trace_sandbox &quot;/etc/localtime&quot;
</del><ins>+                # The sandbox is limited to:
+                set trace_sandbox [list]
</ins><span class="cx"> 
</span><del>-            # Allow temporary locations
-            allow trace_sandbox &quot;/tmp&quot;
-            allow trace_sandbox &quot;/var/tmp&quot;
-            allow trace_sandbox &quot;/var/folders&quot;
-            allow trace_sandbox &quot;/var/empty&quot;
-            allow trace_sandbox &quot;/var/run&quot;
-            if {[info exists env(TMPDIR)]} {
-                set tmpdir [string trim $env(TMPDIR)]
-                if {$tmpdir ne &quot;&quot;} {
-                    allow trace_sandbox $tmpdir
-                }
-            }
</del><ins>+                # Allow work-, port-, and distpath
+                allow trace_sandbox $workpath
+                allow trace_sandbox $portpath
+                allow trace_sandbox $distpath
</ins><span class="cx"> 
</span><del>-            # Allow access to some Xcode specifics
-            allow trace_sandbox &quot;/var/db/xcode_select_link&quot;
-            allow trace_sandbox &quot;/var/db/mds&quot;
-            allow trace_sandbox [file normalize ~${macportsuser}/Library/Preferences/com.apple.dt.Xcode.plist]
-            allow trace_sandbox &quot;$env(HOME)/Library/Preferences/com.apple.dt.Xcode.plist&quot;
</del><ins>+                # Allow standard system directories
+                allow trace_sandbox &quot;/bin&quot;
+                allow trace_sandbox &quot;/sbin&quot;
+                allow trace_sandbox &quot;/dev&quot;
+                allow trace_sandbox &quot;/usr/bin&quot;
+                allow trace_sandbox &quot;/usr/sbin&quot;
+                allow trace_sandbox &quot;/usr/include&quot;
+                allow trace_sandbox &quot;/usr/lib&quot;
+                allow trace_sandbox &quot;/usr/libexec&quot;
+                allow trace_sandbox &quot;/usr/share&quot;
+                allow trace_sandbox &quot;/System/Library&quot;
+                # Deny /Library/Frameworks, third parties install there
+                deny  trace_sandbox &quot;/Library/Frameworks&quot;
+                # But allow the rest of /Library
+                allow trace_sandbox &quot;/Library&quot;
</ins><span class="cx"> 
</span><del>-            # Allow access to developer_dir; however, if it ends with /Contents/Developer, strip
-            # that. If it doesn't leave that in place to avoid allowing access to &quot;/&quot;!
-            set ddsplit [file split [file normalize [file join ${developer_dir} &quot;..&quot; &quot;..&quot;]]]
-            if {[llength $ddsplit] &gt; 2 &amp;&amp; [lindex $ddsplit end-1] eq &quot;Contents&quot; &amp;&amp; [lindex $ddsplit end] eq &quot;Developer&quot;} {
-                set ddsplit [lrange $ddsplit 0 end-2]
-            }
-            allow trace_sandbox [file join {*}$ddsplit]
</del><ins>+                # Allow a few configuration files
+                allow trace_sandbox &quot;/etc/passwd&quot;
+                allow trace_sandbox &quot;/etc/groups&quot;
+                allow trace_sandbox &quot;/etc/localtime&quot;
</ins><span class="cx"> 
</span><del>-            # Allow launchd.db access to avoid failing on port-load(1)/port-unload(1)/port-reload(1)
-            allow trace_sandbox &quot;/var/db/launchd.db&quot;
</del><ins>+                # Allow temporary locations
+                allow trace_sandbox &quot;/tmp&quot;
+                allow trace_sandbox &quot;/var/tmp&quot;
+                allow trace_sandbox &quot;/var/folders&quot;
+                allow trace_sandbox &quot;/var/empty&quot;
+                allow trace_sandbox &quot;/var/run&quot;
+                if {[info exists env(TMPDIR)]} {
+                        set tmpdir [string trim $env(TMPDIR)]
+                        if {$tmpdir ne &quot;&quot;} {
+                                allow trace_sandbox $tmpdir
+                        }
+                }
</ins><span class="cx"> 
</span><del>-            # Deal with ccache
-            allow trace_sandbox &quot;$env(HOME)/.ccache&quot;
-            if {[info exists env(CCACHE_DIR)]} {
-                set ccachedir [string trim $env(CCACHE_DIR)]
-                if {$ccachedir ne &quot;&quot;} {
-                    allow trace_sandbox $ccachedir
-                }
-            }
</del><ins>+                # Allow access to some Xcode specifics
+                allow trace_sandbox &quot;/var/db/xcode_select_link&quot;
+                allow trace_sandbox &quot;/var/db/mds&quot;
+                allow trace_sandbox [file normalize ~${macportsuser}/Library/Preferences/com.apple.dt.Xcode.plist]
+                allow trace_sandbox &quot;$env(HOME)/Library/Preferences/com.apple.dt.Xcode.plist&quot;
</ins><span class="cx"> 
</span><del>-            # Defer back to MacPorts for dependency checks inside $prefix. This must be at the end,
-            # or it'll be used instead of more specific rules.
-            ask trace_sandbox $prefix
</del><ins>+                # Allow access to developer_dir; however, if it ends with /Contents/Developer, strip
+                # that. If it doesn't leave that in place to avoid allowing access to &quot;/&quot;!
+                set ddsplit [file split [file normalize [file join ${developer_dir} &quot;..&quot; &quot;..&quot;]]]
+                if {[llength $ddsplit] &gt; 2 &amp;&amp; [lindex $ddsplit end-1] eq &quot;Contents&quot; &amp;&amp; [lindex $ddsplit end] eq &quot;Developer&quot;} {
+                        set ddsplit [lrange $ddsplit 0 end-2]
+                }
+                allow trace_sandbox [file join {*}$ddsplit]
</ins><span class="cx"> 
</span><del>-            ui_debug &quot;Tracelib Sandbox is:&quot;
-            foreach sandbox $trace_sandbox {
-                ui_debug &quot;\t$sandbox&quot;
-            }
-            set trace_sandboxbounds [join $trace_sandbox :]
-            tracelib setsandbox $trace_sandboxbounds
-        }
-    }
-}
</del><ins>+                # Allow launchd.db access to avoid failing on port-load(1)/port-unload(1)/port-reload(1)
+                allow trace_sandbox &quot;/var/db/launchd.db&quot;
</ins><span class="cx"> 
</span><del>-# Enable the fence.
-# Only done for targets that should only happen in the sandbox.
-proc porttrace::trace_enable_fence {} {
-    tracelib enablefence
-}
</del><ins>+                # Deal with ccache
+                allow trace_sandbox &quot;$env(HOME)/.ccache&quot;
+                if {[info exists env(CCACHE_DIR)]} {
+                        set ccachedir [string trim $env(CCACHE_DIR)]
+                        if {$ccachedir ne &quot;&quot;} {
+                                allow trace_sandbox $ccachedir
+                        }
+                }
</ins><span class="cx"> 
</span><del>-# Check the list of ports.
-# Output a warning for every port the trace revealed a dependency on
-# that isn't included in portslist
-# This method must be called after trace_start
-proc porttrace::trace_check_deps {target portslist} {
-    # Get the list of ports.
-    set ports [slave_send porttrace::slave_get_ports]
</del><ins>+                # Defer back to MacPorts for dependency checks inside $prefix. This must be at the end,
+                # or it'll be used instead of more specific rules.
+                ask trace_sandbox $prefix
</ins><span class="cx"> 
</span><del>-    # Compare with portslist
-    set portslist [lsort $portslist]
-    foreach port $ports {
-        if {[lsearch -sorted -exact $portslist $port] == -1} {
-            ui_warn &quot;Target $target has an undeclared dependency on $port&quot;
-        }
</del><ins>+                ui_debug &quot;Tracelib Sandbox is:&quot;
+                foreach trace_entry $trace_sandbox {
+                        ui_debug &quot;\t$trace_entry&quot;
+                }
+
+                tracelib setsandbox [join $trace_sandbox :]
</ins><span class="cx">     }
</span><del>-    foreach port $portslist {
-        if {[lsearch -sorted -exact $ports $port] == -1} {
-            ui_debug &quot;Target $target has no traceable dependency on $port&quot;
-        }
-    }
-}
</del><span class="cx"> 
</span><del>-# Check that no violation happened.
-# Output a warning for every sandbox violation the trace revealed.
-# This method must be called after trace_start
-proc porttrace::trace_check_violations {} {
-    # Get the list of violations.
-    set violations [slave_send porttrace::slave_get_sandbox_violations]
</del><ins>+        ##
+        # Stop the running trace session and clean up the trace helper thread and
+        # the communication socket. Just must call this once for each call to
+        # trace_start.
+        proc trace_stop {} {
+                global \
+                        env \
+                        macosx_version
</ins><span class="cx"> 
</span><del>-    set existingFiles [list]
-    set missingFiles  [list]
-    foreach violation [lsort -unique $violations] {
-        if {![catch {file lstat $violation _}]} {
-            lappend existingFiles $violation
-        } else {
-            lappend missingFiles $violation
-        }
-    }
</del><ins>+                variable fifo
</ins><span class="cx"> 
</span><del>-    set existingFilesLen [llength $existingFiles]
-    if {$existingFilesLen &gt; 0} {
-        if {$existingFilesLen &gt; 1} {
-            ui_warn &quot;The following existing files were hidden from the build system by trace mode:&quot;
-        } else {
-            ui_warn &quot;The following existing file was hidden from the build system by trace mode:&quot;
-        }
-        foreach violation $existingFiles {
-            ui_msg &quot;  $violation&quot;
-        }
-    }
</del><ins>+                foreach var {DYLD_INSERT_LIBRARIES DARWINTRACE_LOG} {
+                        array unset env $var
+                        if {$macosx_version eq &quot;10.5&quot;} {
+                                unsetenv $var
+                        }
+                }
</ins><span class="cx"> 
</span><del>-    set missingFilesLen [llength $missingFiles]
-    if {$missingFilesLen &gt; 0} {
-        if {$missingFilesLen &gt; 1} {
-            ui_info &quot;The following files would have been hidden from the build system by trace mode if they existed:&quot;
-        } else {
-            ui_info &quot;The following file would have been hidden from the build system by trace mode if it existed:&quot;
-        }
-        foreach violation $missingFiles {
-            ui_info &quot;  $violation&quot;
-        }
-    }
-}
</del><ins>+                # Kill socket
+                tracelib clean
+                # Delete the socket file
+                file delete -force $fifo
</ins><span class="cx"> 
</span><del>-# Stop the trace and return the list of ports the port depends on.
-# This method must be called after trace_start
-proc porttrace::trace_stop {} {
-    global os.platform
-    if {${os.platform} == &quot;darwin&quot;} {
-        global env trace_fifo macosx_version
-        foreach var {DYLD_INSERT_LIBRARIES DARWINTRACE_LOG} {
-            array unset env $var
-            if {$macosx_version eq &quot;10.5&quot;} {
-                unsetenv $var
-            }
-        }
</del><ins>+                # Delete the slave.
+                delete_slave
+        }
</ins><span class="cx"> 
</span><del>-        #kill socket
-        tracelib clean
</del><ins>+        ##
+        # Enable the sandbox. This is only called for targets that should be run
+        # inside the sandbox.
+        proc trace_enable_fence {} {
+                tracelib enablefence
+        }
</ins><span class="cx"> 
</span><del>-        # Clean up.
-        slave_send porttrace::slave_stop
</del><ins>+        ##
+        # Print a list of sandbox violations, separated into a list of files that
+        # actually exist and were hidden, and a list of files that would have been
+        # hidden, if they existed.
+        #
+        # Also print a list of files inside the MacPorts prefix that were not
+        # installed by a port and thus not hidden, but might still cause
+        # non-repeatable builds.
+        #
+        # This method must not be called before trace_start or after trace_stop.
+        proc trace_check_violations {} {
+                # Get the list of violations and print it; separate the list into existing
+                # and non-existent files to cut down the noise.
+                set violations [slave_send porttrace::slave_get_sandbox_violations]
</ins><span class="cx"> 
</span><del>-        # Delete the slave.
-        delete_slave
</del><ins>+                set existingFiles [list]
+                set missingFiles  [list]
+                foreach violation [lsort -unique $violations] {
+                        if {![catch {file lstat $violation _}]} {
+                                lappend existingFiles $violation
+                        } else {
+                                lappend missingFiles $violation
+                        }
+                }
</ins><span class="cx"> 
</span><del>-        file delete -force $trace_fifo
-    }
-}
</del><ins>+                set existingFilesLen [llength $existingFiles]
+                if {$existingFilesLen &gt; 0} {
+                        if {$existingFilesLen &gt; 1} {
+                                ui_warn &quot;The following existing files were hidden from the build system by trace mode:&quot;
+                        } else {
+                                ui_warn &quot;The following existing file was hidden from the build system by trace mode:&quot;
+                        }
+                        foreach violation $existingFiles {
+                                ui_msg &quot;  $violation&quot;
+                        }
+                }
</ins><span class="cx"> 
</span><del>-# Private
-# Create the slave thread.
-proc porttrace::create_slave {workpath trace_fifo} {
-    global trace_thread prefix developer_dir registry.path
-    # Create the thread.
-    set trace_thread [macports_create_thread]
</del><ins>+                set missingFilesLen [llength $missingFiles]
+                if {$missingFilesLen &gt; 0} {
+                        if {$missingFilesLen &gt; 1} {
+                                ui_info &quot;The following files would have been hidden from the build system by trace mode if they existed:&quot;
+                        } else {
+                                ui_info &quot;The following file would have been hidden from the build system by trace mode if it existed:&quot;
+                        }
+                        foreach violation $missingFiles {
+                                ui_info &quot;  $violation&quot;
+                        }
+                }
</ins><span class="cx"> 
</span><del>-    # The slave thred needs this file and macports 1.0
-    thread::send $trace_thread &quot;package require porttrace 1.0&quot;
-    thread::send $trace_thread &quot;package require macports 1.0&quot;
-    # slave needs ui_{info,warn,debug,error}...
-    # make sure to sync this with ../pextlib1.0/tracelib.c!
-    thread::send $trace_thread &quot;macports::ui_init debug&quot;
-    thread::send $trace_thread &quot;macports::ui_init info&quot;
-    thread::send $trace_thread &quot;macports::ui_init warn&quot;
-    thread::send $trace_thread &quot;macports::ui_init error&quot;
-    # and these variables
-    thread::send $trace_thread &quot;set prefix \&quot;$prefix\&quot;; set developer_dir \&quot;$developer_dir\&quot;&quot;
-    # The slave thread requires the registry package.
-    thread::send $trace_thread &quot;package require registry 1.0&quot;
-    # and an open registry
-    thread::send $trace_thread &quot;registry::open [file join ${registry.path} registry registry.db]&quot;
</del><ins>+                set unknowns [slave_send porttrace::slave_get_sandbox_unknowns]
+                set existingUnknowns [list]
+                foreach unknown [lsort -unique $unknowns] {
+                        if {![catch {file lstat $unknown _}]} {
+                                lappend existingUnknowns $unknown
+                        }
+                        # We don't care about files that don't exist inside MacPorts' prefix
+                }
</ins><span class="cx"> 
</span><del>-    # Initialize the slave
-    thread::send $trace_thread &quot;porttrace::slave_init $trace_fifo $workpath&quot;
</del><ins>+                set existingUnknownsLen [llength $existingUnknowns]
+                if {$existingUnknownsLen &gt; 0} {
+                        if {$existingUnknownsLen &gt; 1} {
+                                ui_warn &quot;The following files inside the MacPorts prefix not installed by a port were accessed:&quot;
+                        } else {
+                                ui_warn &quot;The following file inside the MacPorts prefix not installed by a port was accessed:&quot;
+                        }
+                        foreach unknown $existingUnknowns {
+                                ui_msg &quot;  $unknown&quot;
+                        }
+                }
+        }
</ins><span class="cx"> 
</span><del>-    # Run slave asynchronously
-    thread::send -async $trace_thread &quot;porttrace::slave_run&quot;
-}
</del><ins>+        ##
+        # Create a thread that will contain the server-side of a macports trace
+        # mode setup. This part of the code (most of it actually implemented in
+        # pextlib1.0/tracelib.c) will create a Unix socket that all traced
+        # processes will initially connect to to get the sandbox bounds. It will
+        # also handle requests for dependency checks from traced processes and
+        # provide the appropriate answers to the client and track sandbox
+        # violations.
+        #
+        # You must call delete_slave to clean up the data structures associated
+        # with this slave thread.
+        #
+        # @param workpath The workpath of this installation
+        # @param fifo The Unix socket name to be created
+        proc create_slave {workpath fifo} {
+                global prefix developer_dir registry.path
+                variable thread
</ins><span class="cx"> 
</span><del>-# Private
-# Send a command to the thread without waiting for the result.
-proc porttrace::slave_send_async {command} {
-    global trace_thread
</del><ins>+                # Create the thread.
+                set thread [macports_create_thread]
</ins><span class="cx"> 
</span><del>-    thread::send -async $trace_thread &quot;$command&quot;
-}
</del><ins>+                # The slave thred needs this file and macports 1.0
+                thread::send $thread &quot;package require porttrace 1.0&quot;
+                thread::send $thread &quot;package require macports 1.0&quot;
</ins><span class="cx"> 
</span><del>-# Private
-# Send a command to the thread.
-proc porttrace::slave_send {command} {
-    global trace_thread
</del><ins>+                # slave needs ui_{info,warn,debug,error}...
+                # make sure to sync this with ../pextlib1.0/tracelib.c!
+                thread::send $thread &quot;macports::ui_init debug&quot;
+                thread::send $thread &quot;macports::ui_init info&quot;
+                thread::send $thread &quot;macports::ui_init warn&quot;
+                thread::send $thread &quot;macports::ui_init error&quot;
</ins><span class="cx"> 
</span><del>-    # ui_warn &quot;slave send $command ?&quot;
</del><ins>+                # and these variables
+                thread::send $thread &quot;set prefix \&quot;$prefix\&quot;; set developer_dir \&quot;$developer_dir\&quot;&quot;
+                # The slave thread requires the registry package.
+                thread::send $thread &quot;package require registry 1.0&quot;
+                # and an open registry
+                thread::send $thread &quot;registry::open [file join ${registry.path} registry registry.db]&quot;
</ins><span class="cx"> 
</span><del>-    thread::send $trace_thread &quot;$command&quot; result
-    return $result
-}
</del><ins>+                # Initialize the slave
+                thread::send $thread &quot;porttrace::slave_init $fifo $workpath&quot;
</ins><span class="cx"> 
</span><del>-# Private
-# Destroy the thread.
-proc porttrace::delete_slave {} {
-    global trace_thread
</del><ins>+                # Run slave asynchronously
+                thread::send -async $thread &quot;porttrace::slave_run&quot;
+        }
</ins><span class="cx"> 
</span><del>-    # Destroy the thread.
-    thread::release $trace_thread
-}
</del><ins>+        ##
+        # Initialize the slave thread. This is the first user code called in the
+        # thread after creating it and setting it up.
+        #
+        # @param fifo The path of the Unix socket that should be created by
+        #             tracelib
+        # @param p_workpath The workpath of the current installation
+        proc slave_init {fifo p_workpath} {
+                variable sandbox_violation_list
+                variable sandbox_unknown_list
</ins><span class="cx"> 
</span><del>-# Private.
-# Slave method to read a line from the trace.
-proc porttrace::slave_read_line {chan} {
-    global ports_list trace_filemap sandbox_violation_list workpath env
</del><ins>+                # Save the workpath.
+                set workpath $p_workpath
</ins><span class="cx"> 
</span><del>-    while 1 {
-        # We should never get EOF, actually.
-        if {[eof $chan]} {
-            break
-        }
</del><ins>+                # Initialize the sandbox violation lists
+                set sandbox_violation_list {}
+                set sandbox_unknown_list {}
</ins><span class="cx"> 
</span><del>-        # The line is of the form: verb\tpath
-        # Get the path by chopping it.
-        set theline [gets $chan]
</del><ins>+                # Create the socket
+                tracelib setname $fifo
+                tracelib opensocket
+        }
</ins><span class="cx"> 
</span><del>-        if {[fblocked $chan]} {
-            # Exit the loop.
-            break
-        }
</del><ins>+        ##
+        # Actually start the server component that will deal with requests from
+        # trace mode clients. This will occupy the thread until a different thread
+        # calls tracelib closesocket or tracelib clean.
+        proc slave_run {} {
+                tracelib run
+        }
</ins><span class="cx"> 
</span><del>-        set line_length [string length $theline]
</del><ins>+        ##
+        # Destroy the slave thread. You must call this once for each call to
+        # create_slave.
+        proc delete_slave {} {
+                variable thread
</ins><span class="cx"> 
</span><del>-        # Skip empty lines.
-        if {$line_length &gt; 0} {
-            set path_start [expr {[string first &quot;\t&quot; $theline] + 1}]
-            set op [string range $theline 0 [expr {$path_start - 2}]]
-            set path [string range $theline $path_start [expr {$line_length - 1}]]
</del><ins>+                # Destroy the thread.
+                thread::release $thread
+        }
</ins><span class="cx"> 
</span><del>-            # open/execve
-            if {$op eq &quot;open&quot; || $op eq &quot;execve&quot;} {
-                # Only work on files.
-                if {[file isfile $path]} {
-                    # Did we process the file yet?
-                    if {![filemap exists trace_filemap $path]} {
-                        # Obtain information about this file.
-                        set port [registry::file_registered $path]
-                        if { $port != 0 } {
-                            # Add the port to the list.
-                            if {[lsearch -sorted -exact $ports_list $port] == -1} {
-                                lappend ports_list $port
-                                set ports_list [lsort $ports_list]
-                                # Maybe fill trace_filemap for efficiency?
-                            }
-                        }
</del><ins>+        ##
+        # Send a command to the trace thread created by create_slave, wait for its
+        # completion and return its result. The behavior of this proc is undefined
+        # when called before create_slave or after delete_slave.
+        #
+        # @param command The Tcl command to be executed in the trace thread
+        # @return The return value of the Tcl command, executed in the trace thread
+        proc slave_send {command} {
+                variable thread
</ins><span class="cx"> 
</span><del>-                        # Add the file to the tree with port information.
-                        # Ignore errors. Errors can occur if a directory was
-                        # created where a file once lived.
-                        # This doesn't affect existing ports and we just
-                        # add this information to speed up port detection.
-                        catch {filemap set trace_filemap $path $port}
-                    }
-                }
-            } elseif {$op eq &quot;sandbox_violation&quot;} {
-                lappend sandbox_violation_list $path
-            }
-        }
-    }
-}
</del><ins>+                thread::send $thread &quot;$command&quot; result
+                return $result
+        }
</ins><span class="cx"> 
</span><del>-# Private.
-# Slave init method.
-proc porttrace::slave_init {fifo p_workpath} {
-    global ports_list trace_filemap sandbox_violation_list
-    # Save the workpath.
-    set workpath $p_workpath
-    # Create a virtual filemap.
-    filemap create trace_filemap
-    set ports_list {}
-    set sandbox_violation_list {}
-    tracelib setname $fifo
</del><ins>+        ##
+        # Return a list of sandbox violations stored in the trace server thread.
+        #
+        # @return List of files that the traced processed tried to access but were
+        #         outside the sandbox bounds.
+        proc slave_get_sandbox_violations {} {
+                variable sandbox_violation_list
</ins><span class="cx"> 
</span><del>-    if {[catch {tracelib opensocket} err]} {
-        global errorInfo
-        ui_warn &quot;Error in tracelib: $err&quot;
-        ui_debug &quot;Backtrace: $errorInfo&quot;
-    }
-}
</del><ins>+                return $sandbox_violation_list
+        }
</ins><span class="cx"> 
</span><del>-proc porttrace::slave_run {} {
-    if {[catch {tracelib run} err]} {
-        global errorInfo
-        ui_warn &quot;Error in tracelib: $err&quot;
-        ui_debug &quot;Backtrace: $errorInfo&quot;
-    }
-}
</del><ins>+        ##
+        # Add a sandbox violation. This is called directly from
+        # pextlib1.0/tracelib.c. You won't find calls to this method in Tcl code.
+        #
+        # @param path The path of the file that a traced process tried to access
+        #             but violated the sandbox bounds.
+        proc slave_add_sandbox_violation {path} {
+                variable sandbox_violation_list
</ins><span class="cx"> 
</span><del>-# Private.
-# Slave cleanup method.
-proc porttrace::slave_stop {} {
-    global trace_filemap
-    # Close the virtual filemap.
-    filemap close trace_filemap
-    # Close the pipe (both ends).
-}
</del><ins>+                lappend sandbox_violation_list $path
+        }
</ins><span class="cx"> 
</span><del>-# Private.
-# Slave ports export method.
-proc porttrace::slave_get_ports {} {
-    global ports_list
-    return $ports_list
-}
</del><ins>+        ##
+        # Return a list of files accessed inside the MacPorts prefix but not
+        # registered to any port.
+        #
+        # @return List of files that the traced processed tried to access but
+        #         couldn't be matched to a port by MacPorts.
+        proc slave_get_sandbox_unknowns {} {
+                variable sandbox_unknown_list
</ins><span class="cx"> 
</span><del>-# Private.
-# Slave sandbox violations export method.
-proc porttrace::slave_get_sandbox_violations {} {
-    global sandbox_violation_list
-    return $sandbox_violation_list
-}
</del><ins>+                return $sandbox_unknown_list
+        }
</ins><span class="cx"> 
</span><del>-proc porttrace::slave_add_sandbox_violation {path} {
-    global sandbox_violation_list
-    lappend sandbox_violation_list $path
</del><ins>+        ##
+        # Track an access to a file within the MacPorts prefix that MacPorts
+        # doesn't know about. This is called directly from pextlib1.0/tracelib.c.
+        # You won't find calls to this method in Tcl code.
+        #
+        # @param path The path of the file that a traced process tried to access
+        #             inside the MacPorts prefix, but MacPorts couldn't match to
+        #             a port.
+        proc slave_add_sandbox_unknown {path} {
+                variable sandbox_unknown_list
+
+                lappend sandbox_unknown_list $path
+        }
</ins><span class="cx"> }
</span></span></pre>
</div>
</div>

</body>
</html>