Skip to content

Instantly share code, notes, and snippets.

@brettwooldridge
Last active May 8, 2023 07:03
Show Gist options
  • Select an option

  • Save brettwooldridge/5f7413153fe56feb74fcab6a0ee2b6e5 to your computer and use it in GitHub Desktop.

Select an option

Save brettwooldridge/5f7413153fe56feb74fcab6a0ee2b6e5 to your computer and use it in GitHub Desktop.

Revisions

  1. brettwooldridge revised this gist Apr 22, 2018. 1 changed file with 5 additions and 4 deletions.
    9 changes: 5 additions & 4 deletions ProcessTest.java
    Original file line number Diff line number Diff line change
    @@ -70,7 +70,8 @@ public static void main(String[] cargs) throws InterruptedException {
    }

    String[] cmdarray = {"/bin/cat", "/Users/brettw/pgadmin.log"};
    // See java.lang.ProcessImpl:71-83

    // See https://github.com/JetBrains/jdk8u_jdk/blob/master/src/solaris/classes/java/lang/ProcessImpl.java#L71-L83
    byte[][] args = new byte[cmdarray.length-1][];
    int size = args.length; // For added NUL bytes
    for (int i = 0; i < args.length; i++) {
    @@ -85,14 +86,14 @@ public static void main(String[] cargs) throws InterruptedException {
    // No need to write NUL bytes explicitly
    }

    // See java.lang.ProcessImpl:86
    // See https://github.com/JetBrains/jdk8u_jdk/blob/master/src/solaris/classes/java/lang/ProcessImpl.java#L86
    TreeMap<String, String> environment = new TreeMap<>(System.getenv());
    byte[] envBlock = toEnvironmentBlock(environment);

    // See java.lang.ProcessImpl:96
    // See https://github.com/JetBrains/jdk8u_jdk/blob/master/src/solaris/classes/java/lang/ProcessImpl.java#L96
    int[] std_fds = new int[] { -1, -1, -1 };

    // See java.lang.UNIXProcess:247
    // See https://github.com/JetBrains/jdk8u_jdk/blob/master/src/solaris/classes/java/lang/UNIXProcess.java#L247
    // Native source code: https://github.com/JetBrains/jdk8u_jdk/blob/master/src/solaris/native/java/lang/UNIXProcess_md.c#L566
    int pid = jnp.Java_java_lang_UNIXProcess_forkAndExec(
    JNIEnv.CURRENT,
  2. brettwooldridge created this gist Apr 22, 2018.
    150 changes: 150 additions & 0 deletions ProcessTest.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,150 @@
    package com.zaxxer.jna;

    import com.sun.jna.JNIEnv;
    import com.sun.jna.Library;
    import com.sun.jna.Native;
    import com.sun.jna.NativeLibrary;

    import java.util.HashMap;
    import java.util.Map;
    import java.util.TreeMap;
    import java.util.concurrent.TimeUnit;

    public class ProcessTest {
    @SuppressWarnings("unused")
    private enum LaunchMechanism {
    // order IS important!
    FORK,
    POSIX_SPAWN,
    VFORK
    }

    public static class JavaNativeProcess {

    static {
    Map<String, Object> options = new HashMap<>();
    options.put(Library.OPTION_ALLOW_OBJECTS, Boolean.TRUE);

    Native.register(NativeLibrary.getProcess(options));
    }

    public native void Java_java_lang_UNIXProcess_init(JNIEnv jniEnv, Object clazz);

    /**
    * JNIEXPORT jint JNICALL
    * Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
    * jobject process,
    * jint mode,
    * jbyteArray helperpath,
    * jbyteArray prog,
    * jbyteArray argBlock, jint argc,
    * jbyteArray envBlock, jint envc,
    * jbyteArray dir,
    * jintArray std_fds,
    * jboolean redirectErrorStream)
    *
    * @return the PID of the process
    */
    public native int Java_java_lang_UNIXProcess_forkAndExec(
    JNIEnv jniEnv,
    int mode,
    byte[] helperpath,
    byte[] prog,
    byte[] argBlock, int argc,
    byte[] envBlock, int envc,
    byte[] dir,
    int[] fds,
    boolean redirectErrorStream
    );
    }

    public static void main(String[] cargs) throws InterruptedException {

    JavaNativeProcess jnp = new JavaNativeProcess();

    jnp.Java_java_lang_UNIXProcess_init(JNIEnv.CURRENT, ProcessTest.class);

    LaunchMechanism launchMechanism = LaunchMechanism.POSIX_SPAWN;
    if (System.getProperty("os.name").contains("Linux")) {
    launchMechanism = LaunchMechanism.VFORK;
    }

    String[] cmdarray = {"/bin/cat", "/Users/brettw/pgadmin.log"};
    // See java.lang.ProcessImpl:71-83
    byte[][] args = new byte[cmdarray.length-1][];
    int size = args.length; // For added NUL bytes
    for (int i = 0; i < args.length; i++) {
    args[i] = cmdarray[i+1].getBytes();
    size += args[i].length;
    }
    byte[] argBlock = new byte[size];
    int i = 0;
    for (byte[] arg : args) {
    System.arraycopy(arg, 0, argBlock, i, arg.length);
    i += arg.length + 1;
    // No need to write NUL bytes explicitly
    }

    // See java.lang.ProcessImpl:86
    TreeMap<String, String> environment = new TreeMap<>(System.getenv());
    byte[] envBlock = toEnvironmentBlock(environment);

    // See java.lang.ProcessImpl:96
    int[] std_fds = new int[] { -1, -1, -1 };

    // See java.lang.UNIXProcess:247
    // Native source code: https://github.com/JetBrains/jdk8u_jdk/blob/master/src/solaris/native/java/lang/UNIXProcess_md.c#L566
    int pid = jnp.Java_java_lang_UNIXProcess_forkAndExec(
    JNIEnv.CURRENT,
    launchMechanism.ordinal() + 1,
    toCString(System.getProperty("java.home") + "/lib/jspawnhelper"),
    toCString(cmdarray[0]),
    argBlock, args.length,
    envBlock, environment.size(),
    null,
    std_fds,
    false
    );

    System.out.println("Child PID: " + pid);
    TimeUnit.SECONDS.sleep(3);
    }

    private static byte[] toCString(String s) {
    if (s == null)
    return null;
    byte[] bytes = s.getBytes();
    byte[] result = new byte[bytes.length + 1];
    System.arraycopy(bytes, 0,
    result, 0,
    bytes.length);
    result[result.length-1] = (byte)0;
    return result;
    }


    private static byte[] toEnvironmentBlock(TreeMap<String, String> environment) {
    int count = environment.size() * 2;
    for (Map.Entry<String, String> entry : environment.entrySet()) {
    count += entry.getKey().getBytes().length;
    count += entry.getValue().getBytes().length;
    }

    byte[] block = new byte[count];

    int i = 0;
    for (Map.Entry<String, String> entry : environment.entrySet()) {
    byte[] key = entry.getKey ().getBytes();
    byte[] value = entry.getValue().getBytes();
    System.arraycopy(key, 0, block, i, key.length);
    i+=key.length;
    block[i++] = (byte) '=';
    System.arraycopy(value, 0, block, i, value.length);
    i+=value.length + 1;
    // No need to write NUL byte explicitly
    //block[i++] = (byte) '\u0000';
    }

    return block;
    }
    }