changeset 2627:3b82970e37a4

This patch adds a thread to the command-channel process checking that its parent agent process is still running. On Windows, the death of a parent doesn't kill its child processes. Once the parent process no long exists, the new thread will exit the current process. The process watcher thread does not start on Linux.
author Simon Tooke <stooke@redhat.com>
date Thu, 11 May 2017 01:03:42 -0400
parents 9d83a097c50c
children 044bca8442bc
files agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerMain.java agent/command/pom.xml agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/CommandChannelDelegate.java agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/CommandChannelDelegateTest.java common/portability/Makefile common/portability/src/main/java/com/redhat/thermostat/common/portability/HostName.java common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableProcess.java common/portability/src/main/java/com/redhat/thermostat/common/portability/ProcessWaiterMain.java common/portability/src/main/java/com/redhat/thermostat/common/portability/ProcessWatcher.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/PosixHelperImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableHostImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableProcessImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSProcessImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsHelperImpl.java common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsPortableProcessImpl.java common/portability/src/main/native/HostName.c common/portability/src/main/native/PosixHelperImpl.c common/portability/src/main/native/WindowsHelperImpl.c distribution/scripts/thermostat-command-channel distribution/windows/scripts/thermostat-command-channel.cmd integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/StorageTest.java
diffstat 21 files changed, 400 insertions(+), 100 deletions(-) [+]
line wrap: on
line diff
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerMain.java	Fri Apr 14 11:23:45 2017 -0400
+++ b/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerMain.java	Thu May 11 01:03:42 2017 -0400
@@ -43,6 +43,7 @@
 import com.redhat.thermostat.agent.ipc.client.ClientIPCService;
 import com.redhat.thermostat.agent.ipc.client.ClientIPCServiceFactory;
 import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel;
+import com.redhat.thermostat.common.portability.ProcessWatcher;
 import com.redhat.thermostat.shared.config.NativeLibraryResolver;
 import com.redhat.thermostat.shared.config.OS;
 import com.redhat.thermostat.shared.config.SSLConfiguration;
@@ -52,7 +53,11 @@
     
     static final String IPC_SERVER_NAME = "command-channel";
     static final String CONFIG_FILE_PROP = "ipcConfigFile";
-    
+
+    private static final int HOSTNAME_ARG_POS = 0;
+    private static final int HOSTPORT_ARG_POS = 1;
+    private static final int PARENT_PID_ARG_POS = 2;
+
     private static SSLConfigurationParser sslConfParser = new SSLConfigurationParser();
     private static ServerCreator serverCreator = new ServerCreator();
     private static ShutdownHookHandler shutdownHandler = new ShutdownHookHandler();
@@ -62,13 +67,13 @@
 
     // TODO Add some keep alive check
     public static void main(String[] args) throws IOException {
-        if (args.length != 2) {
-            throw new IOException("usage: thermostat-command-channel <hostname> <port>");
+        if (args.length != 2 && args.length != 3) {
+            throw new IOException("usage: thermostat-command-channel <hostname> <port> [<parent pid>]");
         }
-        String hostname = args[0];
+        String hostname = args[HOSTNAME_ARG_POS];
         Integer port;
         try {
-            port = Integer.valueOf(args[1]);
+            port = Integer.valueOf(args[HOSTPORT_ARG_POS]);
         } catch (NumberFormatException e) {
             throw new IOException("Port number must be a valid integer");
         }
@@ -89,7 +94,22 @@
         }
         // Connect to IPC server
         IPCMessageChannel channel = ipcService.connectToServer(IPC_SERVER_NAME);
-        
+
+
+        // if there's a parent pid, watch for it to exit and then shutdown.
+        final int parentPid = (args.length == 3) ? Integer.parseInt(args[PARENT_PID_ARG_POS]) : 0;
+        final int SLEEP_TIME_MS = 5000; // 5 seconds between checks
+        if (parentPid > 0) {
+            final ProcessWatcher watcher = new ProcessWatcher(parentPid, SLEEP_TIME_MS) {
+                @Override
+                public void onProcessExit() {
+                    // tell myself to exit
+                    System.exit(1);
+                }
+            };
+            watcher.start();
+        }
+
         try {
             // Notify server has started
             sendMessage(channel, CommandChannelConstants.SERVER_STARTED_TOKEN);
--- a/agent/command/pom.xml	Fri Apr 14 11:23:45 2017 -0400
+++ b/agent/command/pom.xml	Thu May 11 01:03:42 2017 -0400
@@ -131,6 +131,43 @@
           </instructions>
         </configuration>
       </plugin>
+
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-resources-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>copy</id>
+            <phase>generate-resources</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <overwrite>true</overwrite>
+              <outputDirectory>${project.build.directory}</outputDirectory>
+              <resources>
+                <resource>
+                  <directory>../../common/portability/target</directory>
+                  <includes>
+                    <include>${sharedlib.prefix}thermostat-common-portability${sharedlib.suffix}</include>
+                  </includes>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <systemPropertyVariables>
+            <com.redhat.thermostat.shared.loader.testNativesHome>${project.build.directory}</com.redhat.thermostat.shared.loader.testNativesHome>
+          </systemPropertyVariables>
+        </configuration>
+      </plugin>
     </plugins>
   </build>
 
--- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/CommandChannelDelegate.java	Fri Apr 14 11:23:45 2017 -0400
+++ b/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/CommandChannelDelegate.java	Thu May 11 01:03:42 2017 -0400
@@ -50,6 +50,7 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.redhat.thermostat.common.portability.PortableProcessFactory;
 import org.apache.commons.codec.binary.Base64;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkUtil;
@@ -230,7 +231,7 @@
                 ? new String[]{ binPath.getAbsolutePath() + File.separator + CMD_NAME, hostname,
                                 String.valueOf(port), ipcConfig.getAbsolutePath() }
                 : new String[] { "cmd", "/c", binPath.getAbsolutePath() + File.separator + CMD_NAME + ".cmd", hostname,
-                                String.valueOf(port), ipcConfig.getAbsolutePath() };
+                                String.valueOf(port), ipcConfig.getAbsolutePath(), "" + PortableProcessFactory.getInstance().getCurrentProcessPid()};
 
         ProcessBuilder builder = new ProcessBuilder(processArgs);
         // This has the problem of some messages/Exceptions not
--- a/agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/CommandChannelDelegateTest.java	Fri Apr 14 11:23:45 2017 -0400
+++ b/agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/CommandChannelDelegateTest.java	Thu May 11 01:03:42 2017 -0400
@@ -58,6 +58,7 @@
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
+import com.redhat.thermostat.common.portability.PortableProcessFactory;
 import org.apache.commons.codec.binary.Base64;
 import org.junit.Before;
 import org.junit.Test;
@@ -277,14 +278,16 @@
         };
 
         // in Windows we need to ensure the drive letter appears - by calling getAbsolutePath()
-        String[] winArgs = new String[] {
+        // avoid this call in non-windows to simplify test setup
+        String[] winArgs = OS.IS_WINDOWS ? new String[] {
                 "cmd",
                 "/c",
                 new File("/path/to/thermostat/home/thermostat-command-channel.cmd").getAbsolutePath(),
                 "127.0.0.1",
                 "123",
-                new File("/path/to/ipc/config").getAbsolutePath()
-        };
+                new File("/path/to/ipc/config").getAbsolutePath(),
+                Integer.toString(PortableProcessFactory.getInstance().getCurrentProcessPid())
+        } : null;
 
         final String[] expectedArgs = OS.IS_UNIX ? linuxArgs : winArgs;
         
--- a/common/portability/Makefile	Fri Apr 14 11:23:45 2017 -0400
+++ b/common/portability/Makefile	Thu May 11 01:03:42 2017 -0400
@@ -30,9 +30,9 @@
 
 INCLUDE    = -I $(TARGET_DIR) -I "$(JAVA_HOME)/include/" -I "$(JAVA_HOME)/include/$(JNI_PLATFORM)"
 
-HOSTNAME_SOURCES    = src/main/native/HostName.c
-HOSTNAME_TARGET     = $(TARGET_DIR)/HostName.c
-HOSTNAME_OBJECTS    = $(HOSTNAME_TARGET:.c=.o)
+POSIX_HELPER_SOURCES    = src/main/native/PosixHelperImpl.c
+POSIX_HELPER_TARGET     = $(TARGET_DIR)/PosixHelperImpl.c
+POSIX_HELPER_OBJECTS    = $(POSIX_HELPER_TARGET:.c=.o)
 
 USERNAME_SOURCES    = src/main/native/UserNameUtilImpl.c
 USERNAME_TARGET     = $(TARGET_DIR)/UserNameUtilImpl.c
@@ -53,7 +53,7 @@
 EXECUTABLES          = $(EXECUTABLE)
 
 .PHONY:UserNameUtilImpl
-JNI_LIST = com.redhat.thermostat.common.portability.HostName com.redhat.thermostat.common.portability.internal.linux.UserNameUtilImpl
+JNI_LIST = com.redhat.thermostat.common.portability.internal.PosixHelperImpl com.redhat.thermostat.common.portability.internal.linux.UserNameUtilImpl
 
 ifeq ($(JNI_PLATFORM),win32)
     JNI_LIST     +=  com.redhat.thermostat.common.portability.internal.windows.WindowsHelperImpl
@@ -67,19 +67,18 @@
 $(JNI_LIST):
 	$(JAVAH) -force -classpath $(CLASSPATH) -d $(TARGET_DIR) $(JNI_LIST)
 
-all: $(JNI_LIST) init $(HOSTNAME_SOURCES) $(USERNAME_SOURCES) $(HELPER_SOURCES) $(EXECUTABLES)
+all: $(JNI_LIST) init $(POSIX_HELPER_SOURCES) $(USERNAME_SOURCES) $(HELPER_SOURCES) $(EXECUTABLES)
 
 .PHONY:
 init:
-	$(COPY) $(HOSTNAME_SOURCES) $(HOSTNAME_TARGET)
+	$(COPY) $(POSIX_HELPER_SOURCES) $(POSIX_HELPER_TARGET)
 	$(COPY) $(USERNAME_SOURCES) $(USERNAME_TARGET)
 ifneq ($(strip $(HELPER_SOURCES)),)
 	$(COPY) $(HELPER_SOURCES) $(HELPER_TARGET)
 endif
 
-$(EXECUTABLE): $(HOSTNAME_OBJECTS) $(USERNAME_OBJECTS) $(HELPER_OBJECTS)
-	$(CC) $(MYLDFLAGS) $(LDFLAGS) $(HOSTNAME_OBJECTS) $(USERNAME_OBJECTS) $(HELPER_OBJECTS) $(PLATFORM_LIBS) $(HELPER_LIBS) -o $(TARGET_DIR)/$@
-
+$(EXECUTABLE): $(POSIX_HELPER_OBJECTS) $(USERNAME_OBJECTS) $(HELPER_OBJECTS)
+	$(CC) $(MYLDFLAGS) $(LDFLAGS) $(POSIX_HELPER_OBJECTS) $(USERNAME_OBJECTS) $(HELPER_OBJECTS) $(PLATFORM_LIBS) $(HELPER_LIBS) -o $(TARGET_DIR)/$@
 
 .c.o:
 	$(CC) $(MYCFLAGS) $(CFLAGS) $(INCLUDE) $< -o $@
@@ -91,7 +90,7 @@
 	rm -f $(TARGET_DIR)/$(EXECUTABLE)
 
 clean-obj:
-	rm -f $(HOSTNAME_OBJECTS)
+	rm -f $(POSIX_HELPER_OBJECTS)
 	rm -f $(USERNAME_OBJECTS)
 	rm -f $(HELPER_OBJECTS)
 
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/HostName.java	Fri Apr 14 11:23:45 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/HostName.java	Thu May 11 01:03:42 2017 -0400
@@ -37,16 +37,17 @@
 package com.redhat.thermostat.common.portability;
 
 import com.redhat.thermostat.common.portability.internal.PortableNativeLibraryLoader;
+import com.redhat.thermostat.common.portability.internal.PosixHelperImpl;
 
 /**
  * Finds the current host name without doing a DNS lookup
  */
 public class HostName extends PortableNativeLibraryLoader {
 
+    private static PosixHelperImpl helper = new PosixHelperImpl();
+    
     public static String getLocalHostName() {
-        return getHostName();
+        return helper.getLocalHostName();
     }
-    
-    private static native String getHostName();
 }
 
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableProcess.java	Fri Apr 14 11:23:45 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableProcess.java	Thu May 11 01:03:42 2017 -0400
@@ -59,4 +59,6 @@
     boolean terminateProcess(int pid, boolean wait);
 
     boolean terminateProcess(int pid, int exitcode, int waitMillis);
+
+    int getCurrentProcessPid();
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/ProcessWaiterMain.java	Thu May 11 01:03:42 2017 -0400
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.portability;
+
+public class ProcessWaiterMain {
+
+    private static final boolean verbose = false;
+    private static final int TICKTIME = 1000;
+
+    public static void main(String args[]) {
+        for (String pidStr : args) {
+            final int pid = Integer.parseInt(pidStr);
+            if (pid != 0) {
+                ProcessWatcher watcher = new ProcessWatcher(pid, TICKTIME) {
+                    @Override
+                    public void onProcessExit() {
+                        if (verbose) {
+                            System.err.println("process " + pid + " no longer exists");
+                        }
+                    }
+                };
+                watcher.start();
+                try {
+                    watcher.join();
+                } catch (InterruptedException ignored) {
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/ProcessWatcher.java	Thu May 11 01:03:42 2017 -0400
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.portability;
+
+public abstract class ProcessWatcher extends Thread {
+
+    private final int pidToWatch;
+    private final long sleepTimeMs;
+    private PortableProcess processChecker = PortableProcessFactory.getInstance();
+
+    protected ProcessWatcher(int pid, long sleepTimeMs) {
+        this.pidToWatch = pid;
+        this.sleepTimeMs = sleepTimeMs;
+        setName("process watcher");
+        setDaemon(true);
+    }
+
+    public void run() {
+        boolean parentIsRunning = true;
+        while (parentIsRunning) {
+            parentIsRunning = processChecker.exists(pidToWatch);
+            if (parentIsRunning) {
+                tick();
+                try {
+                    Thread.sleep(sleepTimeMs);
+                } catch (InterruptedException ignored) {
+                }
+            }
+        }
+        onProcessExit();
+    }
+
+    public void tick() {}
+
+    abstract public void onProcessExit();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/PosixHelperImpl.java	Thu May 11 01:03:42 2017 -0400
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.portability.internal;
+
+public class PosixHelperImpl {
+
+    public int getCurrentProcessPid() {
+        return getCurrentProcessID0();
+    }
+
+    public String getLocalHostName() {
+        return getHostName0();
+    }
+
+    private static native int getCurrentProcessID0();
+    private static native String getHostName0();
+}
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableHostImpl.java	Fri Apr 14 11:23:45 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableHostImpl.java	Thu May 11 01:03:42 2017 -0400
@@ -45,9 +45,9 @@
 
 import com.redhat.thermostat.common.Size;
 import com.redhat.thermostat.common.Size.Unit;
-import com.redhat.thermostat.common.portability.HostName;
 import com.redhat.thermostat.common.portability.PortableHost;
 import com.redhat.thermostat.common.portability.PortableMemoryStat;
+import com.redhat.thermostat.common.portability.internal.PosixHelperImpl;
 import com.redhat.thermostat.common.portability.internal.UnimplementedError;
 import com.redhat.thermostat.common.portability.linux.ProcDataSource;
 import com.redhat.thermostat.common.utils.LoggingUtils;
@@ -86,7 +86,6 @@
         }
     }
 
-
     LinuxPortableHostImpl(ProcDataSource dataSource) {
         this.dataSource = dataSource;
     }
@@ -145,7 +144,7 @@
         
         // if fails, try to get hostname without dns lookup
         if (hostname == null) {
-            hostname = HostName.getLocalHostName();
+            hostname = new PosixHelperImpl().getLocalHostName();
         }
         
         // still null, use localhost
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableProcessImpl.java	Fri Apr 14 11:23:45 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableProcessImpl.java	Thu May 11 01:03:42 2017 -0400
@@ -42,6 +42,7 @@
 import com.redhat.thermostat.common.portability.PortableProcessStat;
 import com.redhat.thermostat.common.portability.PortableVmIoStat;
 import com.redhat.thermostat.common.portability.ProcessChecker;
+import com.redhat.thermostat.common.portability.internal.PosixHelperImpl;
 import com.redhat.thermostat.common.portability.internal.UnimplementedError;
 import com.redhat.thermostat.common.portability.internal.linux.vmio.LinuxVmIoStatBuilderImpl;
 import com.redhat.thermostat.common.portability.internal.linux.vmio.ProcIoDataReader;
@@ -54,6 +55,7 @@
     private LinuxPortableProcessStatBuilderImpl procStatHelper;
     private LinuxProcessEnvironmentBuilderImpl procEnvHelper;
     private LinuxVmIoStatBuilderImpl vmioHelper;
+    private PosixHelperImpl posixHelper;
 
     public static LinuxPortableProcessImpl INSTANCE = new LinuxPortableProcessImpl(new SystemClock(), new ProcDataSource());
 
@@ -65,6 +67,7 @@
         procStatHelper = new LinuxPortableProcessStatBuilderImpl(dataSource);
         procEnvHelper = new LinuxProcessEnvironmentBuilderImpl(dataSource);
         vmioHelper = new LinuxVmIoStatBuilderImpl(clock, new ProcIoDataReader(dataSource));
+        posixHelper = new PosixHelperImpl();
     }
 
     @Override
@@ -111,4 +114,9 @@
     public boolean terminateProcess(int pid, int exitcode, int waitMillis) {
         throw new UnimplementedError("terminateProcess()");
     }
+
+    @Override
+    public int getCurrentProcessPid() {
+        return posixHelper.getCurrentProcessPid();
+    }
 }
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSProcessImpl.java	Fri Apr 14 11:23:45 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSProcessImpl.java	Thu May 11 01:03:42 2017 -0400
@@ -40,6 +40,7 @@
 import com.redhat.thermostat.common.portability.PortableProcess;
 import com.redhat.thermostat.common.portability.PortableProcessStat;
 import com.redhat.thermostat.common.portability.PortableVmIoStat;
+import com.redhat.thermostat.common.portability.internal.PosixHelperImpl;
 
 import java.util.Map;
 
@@ -47,6 +48,7 @@
 
     public static final MacOSProcessImpl INSTANCE = new MacOSProcessImpl();
     private static final MacOSHelperImpl helper = MacOSHelperImpl.INSTANCE;
+    private PosixHelperImpl posixHelper = new PosixHelperImpl();
 
     @Override
     public boolean exists(int pid) {
@@ -95,4 +97,9 @@
     public boolean terminateProcess(int pid, int exitcode, int waitMillis) {
         return helper.terminateProcess(pid, exitcode, waitMillis);
     }
+
+    @Override
+    public int getCurrentProcessPid() {
+        return posixHelper.getCurrentProcessPid();
+    }
 }
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsHelperImpl.java	Fri Apr 14 11:23:45 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsHelperImpl.java	Thu May 11 01:03:42 2017 -0400
@@ -147,11 +147,7 @@
     }
 
     public boolean exists(int pid) {
-        final long hnd = getLimitedProcessHandle0(pid);
-        if (hnd != 0) {
-            closeHandle0(hnd);
-        }
-        return hnd != 0;
+        return exists0(pid) != 0;
     }
 
     public String getUserName(int pid) {
@@ -328,6 +324,7 @@
     private static native Object getEnvironment0(long hProcess, int mode); // mode = 0 returns DirectByteBuffer, 1 = String cwd, 2 = String execuatable, 3 = String command line
     private static native boolean getProcessInfo0(int pid, long[] info);
     private static native boolean getProcessIOInfo0(int pid, long[] info);
+    private static native int exists0(int pid);
 
     private static native int getCurrentProcessID0();
     private static native long getCurrentProcessHandle0();
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsPortableProcessImpl.java	Fri Apr 14 11:23:45 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsPortableProcessImpl.java	Thu May 11 01:03:42 2017 -0400
@@ -101,4 +101,9 @@
     public boolean terminateProcess(int pid, int exitcode, int waitMillis) {
         return helper.terminateProcess(pid, exitcode, waitMillis);
     }
+
+    @Override
+    public int getCurrentProcessPid() {
+        return helper.getCurrentProcessPid();
+    }
 }
--- a/common/portability/src/main/native/HostName.c	Fri Apr 14 11:23:45 2017 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/*
- * Copyright 2012-2017 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-#include "com_redhat_thermostat_common_portability_HostName.h"
-
-#include <jni.h>
-#include <unistd.h>
-#include <string.h>
-
-#if !defined(_WIN32)
-# include <netdb.h>
-#else // windows
-# include <winsock2.h>
-#endif
-
-#ifndef NI_MAXHOST
-#define NI_MAXHOST 1025
-#endif /* NI_MAXHOST */
-
-JNIEXPORT jstring JNICALL
-Java_com_redhat_thermostat_common_portability_HostName_getHostName
-  (JNIEnv *env, jclass HostNameClass)
-{
-    char hostname[NI_MAXHOST];
-    memset(hostname, 0, sizeof(hostname));
-
-    if (gethostname(hostname,  sizeof(hostname)) == 0) {
-        return (*env)->NewStringUTF(env, hostname);
-    }
-    return NULL;
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/portability/src/main/native/PosixHelperImpl.c	Thu May 11 01:03:42 2017 -0400
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2012-2017 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+#include "com_redhat_thermostat_common_portability_internal_PosixHelperImpl.h"
+
+#include <jni.h>
+#include <unistd.h>
+#include <string.h>
+
+#if !defined(_WIN32)
+# include <netdb.h>
+#else // windows
+# include <winsock2.h>
+#endif
+
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 1025
+#endif /* NI_MAXHOST */
+
+JNIEXPORT jstring JNICALL
+Java_com_redhat_thermostat_common_portability_internal_PosixHelperImpl_getHostName0
+  (JNIEnv *env, jclass HostNameClass)
+{
+    char hostname[NI_MAXHOST];
+    memset(hostname, 0, sizeof(hostname));
+
+    if (gethostname(hostname,  sizeof(hostname)) == 0) {
+        return (*env)->NewStringUTF(env, hostname);
+    }
+    return NULL;
+}
+
+
+JNIEXPORT jint JNICALL
+Java_com_redhat_thermostat_common_portability_internal_PosixHelperImpl_getCurrentProcessID0
+  (JNIEnv *env, jclass posixHelperClass)
+{
+    return (jint)getpid();
+}
--- a/common/portability/src/main/native/WindowsHelperImpl.c	Fri Apr 14 11:23:45 2017 -0400
+++ b/common/portability/src/main/native/WindowsHelperImpl.c	Thu May 11 01:03:42 2017 -0400
@@ -930,6 +930,30 @@
 
 /*
  * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
+ * Method:    exists0
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_exists0
+  (JNIEnv *env, jclass winHelperClass, jint pid) {
+
+    if (pid == 0)
+        return 1;
+    HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
+    if (hProcess == 0)
+        return 0;
+    LPDWORD ret = 0;
+    int rc = GetExitCodeProcess(hProcess, &ret);
+    CloseHandle(hProcess);
+    if (rc) {
+        return ret == STILL_ACTIVE;
+    }
+    else {
+        return 0;
+    }
+}
+
+/*
+ * Class:     com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl
  * Method:    closeHandle0
  * Signature: (J)V
  */
--- a/distribution/scripts/thermostat-command-channel	Fri Apr 14 11:23:45 2017 -0400
+++ b/distribution/scripts/thermostat-command-channel	Thu May 11 01:03:42 2017 -0400
@@ -61,6 +61,7 @@
 IPC_CLASSPATH="${IPC_CLASSPATH}:${THERMOSTAT_LIBS}/thermostat-shared-config-@project.version@.jar"
 IPC_CLASSPATH="${IPC_CLASSPATH}:${THERMOSTAT_LIBS}/thermostat-agent-command-@project.version@.jar"
 IPC_CLASSPATH="${IPC_CLASSPATH}:${THERMOSTAT_LIBS}/thermostat-common-command-@project.version@.jar"
+IPC_CLASSPATH="${IPC_CLASSPATH}:${THERMOSTAT_LIBS}/thermostat-common-portability-@project.version@.jar"
 IPC_CLASSPATH="${IPC_CLASSPATH}:${THERMOSTAT_LIBS}/thermostat-agent-command-server-@project.version@.jar"
 IPC_CLASSPATH="${IPC_CLASSPATH}:${THERMOSTAT_LIBS}/netty-buffer-@netty.version@.jar"
 IPC_CLASSPATH="${IPC_CLASSPATH}:${THERMOSTAT_LIBS}/netty-common-@netty.version@.jar"
--- a/distribution/windows/scripts/thermostat-command-channel.cmd	Fri Apr 14 11:23:45 2017 -0400
+++ b/distribution/windows/scripts/thermostat-command-channel.cmd	Thu May 11 01:03:42 2017 -0400
@@ -36,13 +36,13 @@
 
 setlocal
 
-if "%3"=="" goto usage
-if not "%4"=="" goto usage
+if "%4"=="" goto usage
+if not "%5"=="" goto usage
 
 goto skipfuncdefs
 
 :usage
-  echo "usage: %~f0 <hostname> <port> <ipcConfigFile>"
+  echo "usage: %~f0 <hostname> <port> <ipcConfigFile> <parentPid>"
   exit /b 1
 
 :skipfuncdefs
@@ -50,6 +50,7 @@
 set HOSTNAME=%1
 set PORT=%2
 set CONFIG_FILE=%3
+set PARENT_PID=%4
 
 :: Source thermostat-ipc-client-common from same directory as this script
 :: Defines IPC_CLASSPATH variable with JARs necessary for the IPC service
@@ -68,6 +69,7 @@
 set IPC_CLASSPATH=%IPC_CLASSPATH%;%THERMOSTAT_LIBS%\thermostat-shared-config-@project.version@.jar
 set IPC_CLASSPATH=%IPC_CLASSPATH%;%THERMOSTAT_LIBS%\thermostat-agent-command-@project.version@.jar
 set IPC_CLASSPATH=%IPC_CLASSPATH%;%THERMOSTAT_LIBS%\thermostat-common-command-@project.version@.jar
+set IPC_CLASSPATH=%IPC_CLASSPATH%;%THERMOSTAT_LIBS%\thermostat-common-portability-@project.version@.jar
 set IPC_CLASSPATH=%IPC_CLASSPATH%;%THERMOSTAT_LIBS%\thermostat-agent-command-server-@project.version@.jar
 set IPC_CLASSPATH=%IPC_CLASSPATH%;%THERMOSTAT_LIBS%\netty-buffer-@netty.version@.jar
 set IPC_CLASSPATH=%IPC_CLASSPATH%;%THERMOSTAT_LIBS%\netty-common-@netty.version@.jar
@@ -87,7 +89,7 @@
 :: Start server
 
 set CONFIG_FILE_ARG=-DipcConfigFile=%CONFIG_FILE%
-%JAVA% %CONFIG_FILE_ARG% %LOGGING_ARGS% -cp %IPC_CLASSPATH% %DEBUG_OPTS% %CMD_CHANNEL_CLASS% %HOSTNAME% %PORT%
+%JAVA% %CONFIG_FILE_ARG% %LOGGING_ARGS% -cp %IPC_CLASSPATH% %DEBUG_OPTS% %CMD_CHANNEL_CLASS% %HOSTNAME% %PORT% %PARENT_PID%
 
 
 
--- a/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/StorageTest.java	Fri Apr 14 11:23:45 2017 -0400
+++ b/integration-tests/itest-run/src/test/java/com/redhat/thermostat/itest/StorageTest.java	Thu May 11 01:03:42 2017 -0400
@@ -81,6 +81,8 @@
         Spawn service = spawnResult.spawn;
 
         try {
+
+
             service.expect("Agent started.");
             // Give agent some time to startup before killing it
             Thread.sleep(2000l);