changeset 2678:259b1bc3fb64

Add thread to command channel to watch when the agent dies, and then abort the command channel process. This is only running on Windows. Windows child processes do not otherwise die when the parent dies. Reviewed-by: jkang Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-May/023287.html
author Simon Tooke <stooke@redhat.com>
date Tue, 30 May 2017 16:30:17 -0400
parents 36f6b133eb8e
children b88b3c8a79c7
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/PortableHost.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/packaging/fedora/thermostat.spec distribution/scripts/thermostat-command-channel distribution/windows/scripts/thermostat-command-channel.cmd
diffstat 22 files changed, 431 insertions(+), 119 deletions(-) [+]
line wrap: on
line diff
--- a/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerMain.java	Thu May 25 13:40:02 2017 -0400
+++ b/agent/command-server/src/main/java/com/redhat/thermostat/agent/command/server/internal/CommandChannelServerMain.java	Tue May 30 16:30:17 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	Thu May 25 13:40:02 2017 -0400
+++ b/agent/command/pom.xml	Tue May 30 16:30:17 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	Thu May 25 13:40:02 2017 -0400
+++ b/agent/command/src/main/java/com/redhat/thermostat/agent/command/internal/CommandChannelDelegate.java	Tue May 30 16:30:17 2017 -0400
@@ -50,6 +50,7 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.redhat.thermostat.common.portability.PortableProcessFactory;
 import com.redhat.thermostat.agent.command.ConfigurationServer;
 import com.redhat.thermostat.agent.command.ReceiverRegistry;
 import com.redhat.thermostat.agent.command.RequestReceiver;
@@ -219,7 +220,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	Thu May 25 13:40:02 2017 -0400
+++ b/agent/command/src/test/java/com/redhat/thermostat/agent/command/internal/CommandChannelDelegateTest.java	Tue May 30 16:30:17 2017 -0400
@@ -58,6 +58,7 @@
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
+import com.redhat.thermostat.common.portability.PortableProcessFactory;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -271,14 +272,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	Thu May 25 13:40:02 2017 -0400
+++ b/common/portability/Makefile	Tue May 30 16:30:17 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	Thu May 25 13:40:02 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/HostName.java	Tue May 30 16:30:17 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/PortableHost.java	Thu May 25 13:40:02 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableHost.java	Tue May 30 16:30:17 2017 -0400
@@ -54,7 +54,7 @@
 
     PortableMemoryStat getMemoryStat();
 
-    // size of array containing CPU statistics for idle, system and user times
+    // represents size of array with idle, system and user ticks or percentages
     int CPU_TIMES_SIZE = 3;
 
     // returns an array (one row per CPU) of an array of ints (idle, system and user ticks)
--- a/common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableProcess.java	Thu May 25 13:40:02 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/PortableProcess.java	Tue May 30 16:30:17 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	Tue May 30 16:30:17 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	Tue May 30 16:30:17 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	Tue May 30 16:30:17 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	Thu May 25 13:40:02 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableHostImpl.java	Tue May 30 16:30:17 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	Thu May 25 13:40:02 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/linux/LinuxPortableProcessImpl.java	Tue May 30 16:30:17 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	Thu May 25 13:40:02 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/macos/MacOSProcessImpl.java	Tue May 30 16:30:17 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	Thu May 25 13:40:02 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsHelperImpl.java	Tue May 30 16:30:17 2017 -0400
@@ -45,6 +45,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
+
 /**
  * Utility class to access Windows native code
  */
@@ -146,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) {
@@ -327,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	Thu May 25 13:40:02 2017 -0400
+++ b/common/portability/src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsPortableProcessImpl.java	Tue May 30 16:30:17 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	Thu May 25 13:40:02 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	Tue May 30 16:30:17 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	Thu May 25 13:40:02 2017 -0400
+++ b/common/portability/src/main/native/WindowsHelperImpl.c	Tue May 30 16:30:17 2017 -0400
@@ -497,11 +497,23 @@
  */
 JNIEXPORT jobject JNICALL
 Java_com_redhat_thermostat_common_portability_internal_windows_WindowsHelperImpl_getEnvironment0
-  (JNIEnv *env, jclass winHelperClass, jlong hProcess, jint callmode) {
+  (JNIEnv *env, jclass winHelperClass, jlong lhProcess, jint callmode) {
+
+    HANDLE hProcess = (HANDLE)(lhProcess);
+
+    BOOL isWowProcess = FALSE;
+    IsWow64Process(hProcess, &isWowProcess);
+    if (isWowProcess) {
+#if defined(DEBUG_GETENV)
+        fprintf(stderr, "process pid=%I64d hnd=%ld is 32-bit; getEnvironment() is unsupported\n", (long long)hProcess, (long)GetProcessId(hProcess));
+#endif
+        return NULL;
+    }
+
     PROCESS_BASIC_INFORMATION procBasicInfo = { 0 };
 
     ULONG uReturnLength = 0;
-    NTSTATUS ntStat = QueryInformationProcesss((HANDLE)hProcess,
+    NTSTATUS ntStat = QueryInformationProcesss(hProcess,
         ProcessBasicInformation,
         &procBasicInfo,
         sizeof(procBasicInfo),
@@ -526,8 +538,8 @@
 #endif //DEBUG_GETENV
 
     // read the PEB from the other process (which is assumed to be 64-bit)
-    if (!ReadProcessMemory((HANDLE)hProcess,(LPCVOID)procBasicInfo.PebBaseAddress, &procEnvBlock, sizeof(procEnvBlock), &returnByteCount)) {
-        fprintf(stderr, "Error Reading Process Memory err=%ld", GetLastError());
+    if (!ReadProcessMemory(hProcess,(LPCVOID)procBasicInfo.PebBaseAddress, &procEnvBlock, sizeof(procEnvBlock), &returnByteCount)) {
+        fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d, err=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError());
         return NULL;
     }
 
@@ -548,8 +560,8 @@
     fprintf(stderr, "testing RTL_USER_PROCESS_PARAMETERS memory at 0x%ld\n", (long)pRTLUserInfo);
 #endif //DEBUG_GETENV
 
-    if (!checkReadAccess((HANDLE)hProcess, pRTLUserInfo, &readableSize)) {
-        fprintf(stderr, "Error Reading Process Memory err=%ld", GetLastError());
+    if (!checkReadAccess(hProcess, pRTLUserInfo, &readableSize)) {
+        fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError());
         return NULL;
     }
 
@@ -560,8 +572,8 @@
 
     // Get the first 0x64 bytes of RTL_USER_PROCESS_PARAMETERS strcuture
     RTL_USER_PROCESS_PARAMETERS64 upp = {0};
-    if (!ReadProcessMemory((HANDLE)hProcess, (LPCVOID)pRTLUserInfo, &upp, sizeof(RTL_USER_PROCESS_PARAMETERS64), &returnByteCount)) {
-        fprintf(stderr, "Error Reading Process Memory err=%ld", GetLastError());
+    if (!ReadProcessMemory(hProcess, (LPCVOID)pRTLUserInfo, &upp, sizeof(RTL_USER_PROCESS_PARAMETERS64), &returnByteCount)) {
+        fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError());
         return NULL;
     }
 
@@ -582,8 +594,8 @@
         fprintf(stderr, "reading CurrentWorkingDirectory string at 0x%lx length %ld\n", (long)upp.CurrentWorkingDirectory.Buffer, (long)upp.CurrentWorkingDirectory.Length);
 #endif //DEBUG_GETENV
         WCHAR* sb = malloc(upp.CurrentWorkingDirectory.Length);
-        if (!ReadProcessMemory((HANDLE)hProcess, (LPCVOID)upp.CurrentWorkingDirectory.Buffer, sb, upp.CurrentWorkingDirectory.Length, &returnByteCount)) {
-            fprintf(stderr, "Error Reading Process Memory err=%ld bytes=%ld\n", GetLastError(), (long)returnByteCount);
+        if (!ReadProcessMemory(hProcess, (LPCVOID)upp.CurrentWorkingDirectory.Buffer, sb, upp.CurrentWorkingDirectory.Length, &returnByteCount)) {
+            fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld bytes=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError(), (long)returnByteCount);
             free(sb);
             return NULL;
         }
@@ -598,8 +610,8 @@
         fprintf(stderr, "reading string at 0x%lx length %ld\n", (long)upp.ImagePathName.Buffer, (long)upp.ImagePathName.Length);
 #endif //DEBUG_GETENV
         WCHAR* sb = malloc(upp.ImagePathName.Length);
-        if (!ReadProcessMemory((HANDLE)hProcess, (LPCVOID)upp.ImagePathName.Buffer, sb, upp.ImagePathName.Length, &returnByteCount)) {
-            fprintf(stderr, "Error Reading Process Memory err=%ld bytes=%ld\n", GetLastError(), (long)returnByteCount);
+        if (!ReadProcessMemory(hProcess, (LPCVOID)upp.ImagePathName.Buffer, sb, upp.ImagePathName.Length, &returnByteCount)) {
+            fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld bytes=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError(), (long)returnByteCount);
             free(sb);
             return NULL;
         }
@@ -614,8 +626,8 @@
         fprintf(stderr, "reading string at 0x%lx length %ld\n", (long)upp.CommandLine.Buffer, (long)upp.CommandLine.Length);
 #endif //DEBUG_GETENV
         WCHAR* sb = malloc(upp.CommandLine.Length);
-        if (!ReadProcessMemory((HANDLE)hProcess, (LPCVOID)upp.CommandLine.Buffer, sb, upp.CommandLine.Length, &returnByteCount)) {
-            fprintf(stderr, "Error Reading Process Memory err=%ld bytes=%ld\n", GetLastError(), (long)returnByteCount);
+        if (!ReadProcessMemory(hProcess, (LPCVOID)upp.CommandLine.Buffer, sb, upp.CommandLine.Length, &returnByteCount)) {
+            fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld bytes=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError(), (long)returnByteCount);
             free(sb);
             return NULL;
         }
@@ -630,8 +642,8 @@
 
         // find out how much we can read, and then read it
         // this will read more than just the environment, but there's not a lot we can do cleanly
-        if (!checkReadAccess((HANDLE)hProcess, strPtr, &readableSize)) {
-            fprintf(stderr, "Error Reading Process Memory err=%ld siz=0x%lx", GetLastError(), (long)readableSize);
+        if (!checkReadAccess(hProcess, strPtr, &readableSize)) {
+            fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld siz=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError(), (long)readableSize);
             return NULL;
         }
         // constrain readableSize to some maximum
@@ -644,8 +656,8 @@
         fprintf(stderr, "reading string at 0x%lx length %ld\n", (long)strPtr, (long)readableSize);
 #endif //DEBUG_GETENV
         WCHAR* sb = malloc(readableSize);
-        if (!ReadProcessMemory((HANDLE)hProcess, (LPCVOID)strPtr, sb, readableSize, &returnByteCount)) {
-            fprintf(stderr, "Error Reading Process Memory err=%ld bytes=%ld\n", GetLastError(), (long)returnByteCount);
+        if (!ReadProcessMemory(hProcess, (LPCVOID)strPtr, sb, readableSize, &returnByteCount)) {
+            fprintf(stderr, "Error reading process memory hnd=%I64d pid=%ld line=%d err=%ld readable=%ld retbytes=%ld\n", (long long)hProcess, (long)GetProcessId(hProcess), __LINE__, GetLastError(), (long)readableSize, (long)returnByteCount);
             free(sb);
             return NULL;
         }
@@ -930,6 +942,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/packaging/fedora/thermostat.spec	Thu May 25 13:40:02 2017 -0400
+++ b/distribution/packaging/fedora/thermostat.spec	Tue May 30 16:30:17 2017 -0400
@@ -673,6 +673,7 @@
          src/main/java/com/redhat/thermostat/common/portability/HostName.java \
          src/main/java/com/redhat/thermostat/common/portability/UserNameUtil.java \
          src/main/java/com/redhat/thermostat/common/portability/UserNameLookupException.java \
+         src/main/java/com/redhat/thermostat/common/portability/internal/PosixHelperImpl.java \
          src/main/java/com/redhat/thermostat/common/portability/internal/PortableNativeLibraryLoader.java \
          src/main/java/com/redhat/thermostat/common/portability/internal/linux/UserNameUtilImpl.java \
          src/main/java/com/redhat/thermostat/common/portability/internal/windows/WindowsHelperImpl.java
--- a/distribution/scripts/thermostat-command-channel	Thu May 25 13:40:02 2017 -0400
+++ b/distribution/scripts/thermostat-command-channel	Tue May 30 16:30:17 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	Thu May 25 13:40:02 2017 -0400
+++ b/distribution/windows/scripts/thermostat-command-channel.cmd	Tue May 30 16:30:17 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%