<!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>[140639] trunk/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/140639">140639</a></dd>
<dt>Author</dt> <dd>cal@macports.org</dd>
<dt>Date</dt> <dd>2015-09-28 14:04:13 -0700 (Mon, 28 Sep 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>base: tracelib: Add process tracking &amp; killing

Some build systems (*cough* xcodebuild *cough*) start long-running daemon
process that do terminate after the build is finished. Because these processes
use file descriptors, they are instrumented by darwintrace and end up in an
inconsistent state when the tracelib server terminates at the end of the build.
These processes then commonly hang around as quasi-zombies and cannot be killed
but using SIGKILL.

Avoid this problem by tracking which process belongs to a incoming socket in
tracelib and sending these process both SIGTERM and SIGKILL after the main loop
terminates.

NOTE: This change implements the data structures and operations required to do
the tracking, but does not actually fill and drain the list of open sockets.
This is because I did some refactoring while writing these changes and the
control flow path that adds and removes entries from the list can not be easily
separated (short of a complete re-implementation against an older version that
will be replaced anyway) from other refactoring.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkbaseconfigureac">trunk/base/configure.ac</a></li>
<li><a href="#trunkbasesrcpextlib10tracelibc">trunk/base/src/pextlib1.0/tracelib.c</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkbaseconfigureac"></a>
<div class="modfile"><h4>Modified: trunk/base/configure.ac (140638 => 140639)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/configure.ac        2015-09-28 20:38:28 UTC (rev 140638)
+++ trunk/base/configure.ac        2015-09-28 21:04:13 UTC (rev 140639)
</span><span class="lines">@@ -246,7 +246,7 @@
</span><span class="cx"> AC_HEADER_STDC
</span><span class="cx"> AC_HEADER_DIRENT
</span><span class="cx"> AC_HEADER_SYS_WAIT
</span><del>-AC_CHECK_HEADERS([crt_externs.h err.h fcntl.h libkern/OSAtomic.h limits.h paths.h pwd.h \
</del><ins>+AC_CHECK_HEADERS([crt_externs.h err.h fcntl.h libkern/OSAtomic.h libproc.h limits.h paths.h pwd.h \
</ins><span class="cx">         readline/history.h readline/readline.h spawn.h sys/cdefs.h sys/event.h sys/fcntl.h sys/file.h \
</span><span class="cx">         sys/paths.h sys/socket.h sys/sysctl.h utime.h])
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkbasesrcpextlib10tracelibc"></a>
<div class="modfile"><h4>Modified: trunk/base/src/pextlib1.0/tracelib.c (140638 => 140639)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/pextlib1.0/tracelib.c        2015-09-28 20:38:28 UTC (rev 140638)
+++ trunk/base/src/pextlib1.0/tracelib.c        2015-09-28 21:04:13 UTC (rev 140639)
</span><span class="lines">@@ -42,7 +42,9 @@
</span><span class="cx"> #include &lt;inttypes.h&gt;
</span><span class="cx"> #include &lt;limits.h&gt;
</span><span class="cx"> #include &lt;pthread.h&gt;
</span><ins>+#include &lt;signal.h&gt;
</ins><span class="cx"> #include &lt;stdarg.h&gt;
</span><ins>+#include &lt;stdbool.h&gt;
</ins><span class="cx"> #include &lt;stdint.h&gt;
</span><span class="cx"> #include &lt;stdio.h&gt;
</span><span class="cx"> #include &lt;stdlib.h&gt;
</span><span class="lines">@@ -62,6 +64,11 @@
</span><span class="cx"> #include &lt;registry2.0/registry.h&gt;
</span><span class="cx"> #include &lt;darwintracelib1.0/sandbox_actions.h&gt;
</span><span class="cx"> 
</span><ins>+#if defined(LOCAL_PEERPID) &amp;&amp; defined(HAVE_LIBPROC_H)
+#include &lt;libproc.h&gt;
+#define HAVE_PEERPID_LIST
+#endif /* defined(LOCAL_PEERPID) &amp;&amp; defined(HAVE_LIBPROC_H) */
+
</ins><span class="cx"> #include &quot;tracelib.h&quot;
</span><span class="cx"> 
</span><span class="cx"> #include &quot;Pextlib.h&quot;
</span><span class="lines">@@ -86,6 +93,14 @@
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+#ifdef HAVE_PEERPID_LIST
+static bool peerpid_list_enqueue(int sock, pid_t pid);
+static pid_t peerpid_list_dequeue(int sock);
+static pid_t peerpid_list_get(int sock, const char **progname);
+static void peerpid_list_walk(bool (*callback)(int sock, pid_t pid, const char *progname));
+#endif /* defined(HAVE_PEERPID_LIST) */
+
+
</ins><span class="cx"> static char *name;
</span><span class="cx"> static char *sandbox;
</span><span class="cx"> static size_t sandboxLength;
</span><span class="lines">@@ -109,6 +124,130 @@
</span><span class="cx"> } sandbox_violation_t;
</span><span class="cx"> static void sandbox_violation(int sock, const char *path, sandbox_violation_t type);
</span><span class="cx"> 
</span><ins>+#ifdef HAVE_PEERPID_LIST
+typedef struct _peerpid {
+    struct _peerpid *ppid_next;
+    char            *ppid_prog;
+    int              ppid_sock;
+    pid_t            ppid_pid;
+} peerpid_entry_t;
+
+static peerpid_entry_t *peer_list = NULL;
+
+/**
+ * Add a new entry to the list of PIDs of peers. Call this once for each
+ * accepted socket with the socket and the peer's PID.
+ *
+ * @param sock The new socket that was opened by the process with the given PID
+ *             and should be added to the list of peers.
+ * @param pid The PID of the new peer.
+ * @return boolean indicating success.
+ */
+static bool peerpid_list_enqueue(int sock, pid_t pid) {
+    char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
+    const char *progname = &quot;&lt;unknown&gt;&quot;;
+
+    peerpid_entry_t *ppid = malloc(sizeof(peerpid_entry_t));
+    if (!ppid) {
+        return false;
+    }
+
+    if (proc_pidpath(pid, pathbuf, sizeof(pathbuf))) {
+        progname = pathbuf;
+    }
+
+    ppid-&gt;ppid_prog = strdup(progname);
+    if (!ppid-&gt;ppid_prog) {
+        free(ppid);
+        return false;
+    }
+    ppid-&gt;ppid_sock = sock;
+    ppid-&gt;ppid_pid = pid;
+    ppid-&gt;ppid_next = peer_list;
+    peer_list = ppid;
+    return true;
+}
+
+/**
+ * Given a socket, dequeue a peer from the current list of peers. Use this when
+ * a socket is closed.
+ *
+ * @param sock The socket that is being closed and should be dequeued.
+ * @return The PID of the socket that has been dequeued, or (pid_t) -1
+ */
+static pid_t peerpid_list_dequeue(int sock) {
+    peerpid_entry_t **ref = &amp;peer_list;
+    while (*ref) {
+        peerpid_entry_t *curr = *ref;
+        if (curr-&gt;ppid_sock == sock) {
+            // dequeue the element
+            *ref = curr-&gt;ppid_next;
+            pid_t pid = curr-&gt;ppid_pid;
+            free(curr-&gt;ppid_prog);
+            free(curr);
+            return pid;
+        }
+
+        ref = &amp;curr-&gt;ppid_next;
+    }
+
+    return (pid_t) -1;
+}
+
+/**
+ * Return the peer PID given a socket.
+ *
+ * @param sock The socket for which the peer PID is needed.
+ * @param progname A pointer that will point to the string that holds the
+ *                 command line corresponding to the PID at the time of
+ *                 enqueuing. Set to NULL if not needed.
+ * @return The peer's PID or (pid_t) -1, if the socket could not be found in the list.
+ */
+static pid_t peerpid_list_get(int sock, const char **progname) {
+    peerpid_entry_t *curr = peer_list;
+    while (curr) {
+        if (curr-&gt;ppid_sock == sock) {
+            if (progname) {
+                *progname = curr-&gt;ppid_prog;
+            }
+            return curr-&gt;ppid_pid;
+        }
+
+        curr = curr-&gt;ppid_next;
+    }
+
+    return (pid_t) -1;
+}
+
+/**
+ * Walk the current list of (socket, peer PID) pairs and call a callback
+ * function for each pair.
+ *
+ * @param func Callback function to call for each tuple of socket, peer PID and
+ *             peer command line. The function should take an integer (the
+ *             socket), a pid_t (the peer's PID) and a const char * (the peer's
+ *             command line) and return a boolean (true, if the element should
+ *             be removed from the list, false otherwise). The callback must
+ *             not modify the list using peerpid_list_enqueue() or
+ *             peerpid_list_dequeue().
+ */
+static void peerpid_list_walk(bool (*callback)(int sock, pid_t pid, const char *progname)) {
+    peerpid_entry_t **ref = &amp;peer_list;
+    while (*ref) {
+        peerpid_entry_t *curr = *ref;
+        if (callback(curr-&gt;ppid_sock, curr-&gt;ppid_pid, curr-&gt;ppid_prog)) {
+            // dequeue the element
+            *ref = curr-&gt;ppid_next;
+            free(curr-&gt;ppid_prog);
+            free(curr);
+            continue;
+        }
+
+        ref = &amp;curr-&gt;ppid_next;
+    }
+}
+#endif /* defined(HAVE_PEERPID_LIST) */
+
</ins><span class="cx"> #define MAX_SOCKETS (1024)
</span><span class="cx"> #define BUFSIZE     (4096)
</span><span class="cx"> 
</span><span class="lines">@@ -310,7 +449,11 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (len &gt; BUFSIZE - 1) {
</span><del>-        fprintf(stderr, &quot;tracelib: transfer too large: %&quot; PRIu32 &quot; bytes sent, but buffer holds %d on socket %d\n&quot;, len, BUFSIZE - 1, sock);
</del><ins>+        pid_t pid = (pid_t) -1;
+#ifdef HAVE_PEERPID_LIST
+        pid = peerpid_list_get(sock, NULL);
+#endif
+        fprintf(stderr, &quot;tracelib: transfer too large: %&quot; PRIu32 &quot; bytes sent, but buffer holds %d on socket %d from pid %ld\n&quot;, len, BUFSIZE - 1, sock, (unsigned long) pid);
</ins><span class="cx">         return 0;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -510,6 +653,32 @@
</span><span class="cx">     return TCL_OK;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#ifdef HAVE_PEERPID_LIST
+/**
+ * Callback to be passed to peerpid_list_walk(). Closes the open sockets and
+ * sends SIGTERM to the associated processes. Leaves the list unmodified.
+ */
+static bool close_and_send_sigterm(int sock UNUSED, pid_t pid, const char *progname) {
+    ui_warn(interp, &quot;Sending SIGTERM to process %ld: %s&quot;, (unsigned long) pid, progname);
+    kill(pid, SIGTERM);
+
+    // keep the elements in the list
+    return false;
+}
+
+/**
+ * Callback to be passed to peerpid_list_walk(). Sends SIGKILL to the processes
+ * and deletes the elements from the list.
+ */
+static bool send_sigkill_and_free(int sock, pid_t pid, const char *progname UNUSED) {
+    close(sock);
+    kill(pid, SIGKILL);
+
+    // remove the elements from the list
+    return true;
+}
+#endif
+
</ins><span class="cx"> /* create this on heap rather than stack, due to its rather large size */
</span><span class="cx"> static struct kevent res_kevents[MAX_SOCKETS];
</span><span class="cx"> 
</span><span class="lines">@@ -718,8 +887,16 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     /* NOTE: We aren't necessarily closing all client sockets here! */
</span><ins>+
+    // Close remainig sockets to avoid dangling processes
</ins><span class="cx">     if (opensockcount &gt; 0) {
</span><del>-        fprintf(stderr, &quot;tracelib: %d open sockets will leak at end of runcmd\n&quot;, opensockcount);
</del><ins>+#ifdef HAVE_PEERPID_LIST
+        ui_warn(interp, &quot;tracelib: %d open sockets leaking at end of runcmd, closing, sending SIGTERM and SIGKILL\n&quot;, opensockcount);
+        peerpid_list_walk(close_and_send_sigterm);
+        peerpid_list_walk(send_sigkill_and_free);
+#else
+        ui_warn(interp, &quot;tracelib: %d open sockets leaking at end of runcmd\n&quot;, opensockcount);
+#endif
</ins><span class="cx">     }
</span><span class="cx">     pthread_mutex_lock(&amp;sock_mutex);
</span><span class="cx">     close(kq);
</span></span></pre>
</div>
</div>

</body>
</html>