private static native int bgexecInternal()

in geode-logging/src/main/java/org/apache/geode/logging/internal/OSProcess.java [112:337]


  private static native int bgexecInternal(String[] cmdarray, String workdir, String logfile,
      boolean inheritLogfile) throws IOException;

  /**
   * Starts execution of the specified command and arguments in a separate detached process in the
   * specified working directory writing output to the specified log file.
   * <p>
   * If there is a security manager, its <code>checkExec</code> method is called with the first
   * component of the array <code>cmdarray</code> as its argument. This may result in a security
   * exception.
   * <p>
   * Given an array of strings <code>cmdarray</code>, representing the tokens of a command line,
   * this method creates a new process in which to execute the specified command.
   *
   * @param cmdarray array containing the command to call and its arguments.
   * @param workdir the current directory of the created process; null causes working directory to
   *        default to the current directory.
   * @param logfile the file the created process will write stdout and stderr to; null causes a
   *        default log file name to be used.
   * @param inheritLogfile can be set to false if the child process is willing to create its own log
   *        file. Setting to false can help on Windows because it keeps the child process from
   *        inheriting handles from the parent.
   * @param env any extra environment variables as key,value map; these will be in addition to those
   *        inherited from the parent process and will overwrite same keys
   * @return the process id of the created process; -1 on failure
   * @exception SecurityException if the current thread cannot create a subprocess.
   * @see java.lang.SecurityException
   * @see java.lang.SecurityManager#checkExec(java.lang.String)
   */
  public static int bgexec(String[] cmdarray, File workdir, File logfile, boolean inheritLogfile,
      Map<String, String> env) throws IOException {
    String commandShell =
        System.getProperty(GeodeGlossary.GEMFIRE_PREFIX + "commandShell", "bash");
    if (cmdarray.length == 0) {
      throw new java.lang.IndexOutOfBoundsException();
    }
    boolean isWindows = false;
    String os = System.getProperty("os.name");
    if (os != null) {
      if (os.indexOf("Windows") != -1) {
        isWindows = true;
      }
    }
    for (int i = 0; i < cmdarray.length; i++) {
      if (cmdarray[i] == null) {
        throw new NullPointerException();
      }
      if (isWindows) {
        if (i == 0) {
          // do the following before quotes get added.
          File cmd = new File(cmdarray[0]);
          if (!cmd.exists()) {
            cmd = new File(cmdarray[0] + ".exe");
            if (cmd.exists()) {
              cmdarray[0] = cmd.getPath();
            }
          }
        }
        String s = cmdarray[i];
        if (i != 0) {
          if (s.length() == 0) {
            cmdarray[i] = "\"\""; // fix for bug 22207
          } else if ((s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0)) {
            String unquotedS = s;
            if (s.indexOf('\"') != -1) {
              // Note that Windows provides no way to embed a double
              // quote in a double quoted string so need to remove
              // any internal quotes and let the outer quotes
              // preserve the whitespace.
              StringBuilder b = new StringBuilder(s);
              int quoteIdx = s.lastIndexOf('\"');
              while (quoteIdx != -1) {
                b.deleteCharAt(quoteIdx);
                quoteIdx = s.lastIndexOf('\"', quoteIdx - 1);
              }
              unquotedS = b.toString();
            }
            // It has whitespace and its not quoted
            cmdarray[i] = '"' + unquotedS + '"';
          }
        }
      }
    }
    File cmd = new File(cmdarray[0]);
    if (!cmd.exists()) {
      throw new IOException(String.format("the executable %s does not exist",
          cmd.getPath()));
    }
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
      security.checkExec(cmdarray[0]);
    }
    if (workdir != null && !workdir.isDirectory()) {
      String curDir = new File("").getAbsolutePath();
      System.out.println(
          String.format("WARNING: %s is not a directory. Defaulting to current directory %s.",
              workdir, curDir));
      workdir = null;
    }
    if (workdir == null) {
      workdir = new File("").getAbsoluteFile();
    }
    if (logfile == null) {
      logfile = File.createTempFile("bgexec", ".log", workdir);
    }
    if (!logfile.isAbsolute()) {
      // put it in the working directory
      logfile = new File(workdir, logfile.getPath());
    }
    // fix for bug 24575
    if (logfile.exists()) {
      // it already exists so make sure its a file and can be written
      if (!logfile.isFile()) {
        throw new IOException(String.format("The log file %s was not a normal file.",
            logfile.getPath()));
      }
      if (!logfile.canWrite()) {
        throw new IOException(String.format("Need write access for the log file %s.",
            logfile.getPath()));
      }
    } else {
      try {
        logfile.createNewFile();
      } catch (IOException io) {
        throw new IOException(String.format("Could not create log file %s because: %s.",
            logfile.getPath(), io.getMessage()));
      }
    }
    String trace = System.getProperty("org.apache.geode.logging.internal.OSProcess.trace");
    if (trace != null && trace.length() > 0) {
      for (int i = 0; i < cmdarray.length; i++) {
        System.out.println("cmdarray[" + i + "] = " + cmdarray[i]);
      }
      System.out.println("workdir=" + workdir.getPath());
      System.out.println("logfile=" + logfile.getPath());
    }
    int result = 0;

    StringBuilder sb = new StringBuilder();
    Vector<String> cmdVec = new Vector<>();
    // Add shell code to spawn a process silently
    if (isWindows) {
      cmdVec.add("cmd.exe");
      cmdVec.add("/c");
      sb.append("start /b \"\" ");
    } else {
      // to address issue with users that don't have bash shell installed
      if (commandShell.equals("bash")) {
        cmdVec.add("bash");
        cmdVec.add("--norc");
        cmdVec.add("-c");
      } else {
        cmdVec.add(commandShell);
      }
    }
    // Add the actual command
    for (int i = 0; i < cmdarray.length; i++) {
      if (i != 0) {
        sb.append(" ");
      }
      if (cmdarray[i].length() != 0 && cmdarray[i].charAt(0) == '\"') {
        // The token has already been quoted, see bug 40835
        sb.append(cmdarray[i]);
      } else {
        sb.append("\"");
        sb.append(cmdarray[i]);
        sb.append("\"");
      }
    }
    // Add the IO redirction code, this prevents hangs and IO blocking
    sb.append(" >> ");
    sb.append(logfile.getPath());
    sb.append(" 2>&1");
    if (isWindows) {
      sb.append(" <NUL");
    } else {
      sb.append(" </dev/null &");
    }
    cmdVec.add(sb.toString());

    String[] cmdStrings = cmdVec.toArray(new String[0]);
    if (trace != null && trace.length() > 0) {
      for (int i = 0; i < cmdStrings.length; i++) {
        System.out.println("cmdStrings[" + i + "] = " + cmdStrings[i]);
      }
      System.out.println("workdir=" + workdir.getPath());
      System.out.println("logfile=" + logfile.getPath());
    }
    final ProcessBuilder procBuilder = new ProcessBuilder(cmdStrings);
    if (env != null && env.size() > 0) {
      // adjust the environment variables inheriting from parent
      procBuilder.environment().putAll(env);
    }
    procBuilder.directory(workdir);
    final Process process = procBuilder.start();
    try {
      process.getInputStream().close();
    } catch (IOException ignore) {
    }
    try {
      process.getOutputStream().close();
    } catch (IOException ignore) {
    }
    try {
      process.getErrorStream().close();
    } catch (IOException ignore) {
    }
    try {
      // short count = 1000;
      boolean processIsStillRunning = true;
      while (processIsStillRunning) {
        Thread.sleep(10);
        try {
          process.exitValue();
          processIsStillRunning = false;
        } catch (IllegalThreadStateException itse) {
          // Ignore this, we are polling the exitStatus
          // instead of using the blocking Process#waitFor()
        }
      }
    } catch (InterruptedException ie) {
      Thread.currentThread().interrupt();
    }

    return result; // Always 0 for pureJava
  }