/* * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved * * @author Scott Ferguson */ #ifdef WIN32 #ifndef _WINSOCKAPI_ #define _WINSOCKAPI_ #endif #include #else #include #include #include #include #include #include #include #include #endif #include #include #include #include #include #include #include #include /* probably system-dependent */ #include static char * get_utf8(JNIEnv *env, jstring jaddr, char *buf, int buflen) { const char *temp_string = 0; temp_string = (*env)->GetStringUTFChars(env, jaddr, 0); if (temp_string) { strncpy(buf, temp_string, buflen); buf[buflen - 1] = 0; (*env)->ReleaseStringUTFChars(env, jaddr, temp_string); } return buf; } JNIEXPORT jboolean JNICALL Java_com_caucho_bootjni_JniProcess_isNativeBootAvailable(JNIEnv *env, jobject obj) { #ifdef WIN32 return 0; #else return 1; #endif } JNIEXPORT jboolean JNICALL Java_com_caucho_bootjni_JniProcess_clearSaveOnExec(JNIEnv *env, jobject obj) { #ifdef WIN32 return 0; #else { int fd; for (fd = 3; fd < 4096; fd++) { int arg = fcntl(fd, F_GETFD, 0); if (arg >= 0) { arg |= FD_CLOEXEC; fcntl(fd, F_SETFD, arg); } } return 1; } #endif } JNIEXPORT jint JNICALL Java_com_caucho_bootjni_JniProcess_getFdMax(JNIEnv *env, jobject obj) { #ifdef WIN32 return -1; #else struct rlimit rlimit; if (getrlimit(RLIMIT_NOFILE, &rlimit) != 0) return -1; return rlimit.rlim_cur; #endif } #ifdef WIN32 JNIEXPORT jint JNICALL Java_com_caucho_bootjni_JniProcess_setFdMax(JNIEnv *env, jobject obj) { return -1; } #else JNIEXPORT jint JNICALL Java_com_caucho_bootjni_JniProcess_setFdMax(JNIEnv *env, jobject obj) { struct rlimit orig_rlimit; struct rlimit rlimit; struct rlimit set_rlimit; set_rlimit.rlim_cur = set_rlimit.rlim_max = 128 * 1024; if (setrlimit(RLIMIT_NOFILE, &set_rlimit) != 0) { set_rlimit.rlim_cur = set_rlimit.rlim_max = RLIM_INFINITY; setrlimit(RLIMIT_NOFILE, &set_rlimit); } if (getrlimit(RLIMIT_NOFILE, &rlimit) == 0) return rlimit.rlim_cur; else return -1; } #endif jboolean Java_com_caucho_bootjni_JniProcess_exec(JNIEnv *env, jobject obj, jobjectArray j_argv, jobjectArray j_envp, jstring j_chroot, jstring j_pwd, jstring j_user, jstring j_group) { char **argv; char **envp; char chroot_path[4096]; char pwd[4096]; char user[256]; char group[256]; int uid = -1; int gid = -1; int len; int i; int pipe_fds[2]; int pid; jclass c_jni_process; jfieldID f_stdoutFd; jfieldID f_pid; #ifdef WIN32 if (1) return -1; #endif /* WIN32 */ user[0] = 0; group[0] = 0; chroot_path[0] = 0; if (! j_argv) { resin_printf_exception(env, "java/lang/NullPointerException", "argv"); return 0; } if (! j_envp) { resin_printf_exception(env, "java/lang/NullPointerException", "argv"); return 0; } if (! j_pwd) { resin_printf_exception(env, "java/lang/NullPointerException", "pwd"); return 0; } c_jni_process = (*env)->FindClass(env, "com/caucho/bootjni/JniProcess"); if (! c_jni_process) { resin_printf_exception(env, "java/lang/NullPointerException", "can't load JniProcess"); return 0; } f_stdoutFd = (*env)->GetFieldID(env, c_jni_process, "_stdoutFd", "I"); if (! f_stdoutFd) { resin_printf_exception(env, "java/lang/NullPointerException", "can't load field"); return 0; } f_pid = (*env)->GetFieldID(env, c_jni_process, "_pid", "I"); if (! f_pid) { resin_printf_exception(env, "java/lang/NullPointerException", "can't load field"); return 0; } len = (*env)->GetArrayLength(env, j_argv); argv = malloc((len + 1) * sizeof(char*)); argv[len] = 0; for (i = 0; i < len; i++) { jstring j_string; j_string = (*env)->GetObjectArrayElement(env, j_argv, i); if (j_string) { int strlen = (*env)->GetStringUTFLength(env, j_string); argv[i] = (char *) malloc(strlen + 1); argv[i] = get_utf8(env, j_string, argv[i], strlen + 1); } } len = (*env)->GetArrayLength(env, j_envp); envp = malloc((len + 1) * sizeof(char*)); envp[len] = 0; for (i = 0; i < len; i++) { jstring j_string; j_string = (*env)->GetObjectArrayElement(env, j_envp, i); if (j_string) { int strlen = (*env)->GetStringUTFLength(env, j_string); envp[i] = (char *) malloc(strlen + 1); envp[i] = get_utf8(env, j_string, envp[i], strlen + 1); } } if (j_chroot) { int strlen = (*env)->GetStringUTFLength(env, j_chroot); get_utf8(env, j_chroot, chroot_path, strlen + 1); } else chroot_path[0] = 0; if (j_pwd) { int strlen = (*env)->GetStringUTFLength(env, j_pwd); get_utf8(env, j_pwd, pwd, strlen + 1); } #ifndef WIN32 if (j_user) { struct passwd *passwd; int strlen = (*env)->GetStringUTFLength(env, j_user); get_utf8(env, j_user, user, strlen + 1); passwd = getpwnam(user); if (! passwd) { resin_printf_exception(env, "java/lang/IllegalArgumentException", "%s is an unknown user", user); return 0; } uid = passwd->pw_uid; gid = passwd->pw_gid; } if (j_group) { struct group *group_ent; int strlen = (*env)->GetStringUTFLength(env, j_group); get_utf8(env, j_group, group, strlen + 1); group_ent = getgrnam(group); if (! group_ent) { resin_printf_exception(env, "java/lang/IllegalArgumentException", "%s is an unknown group", group); return 0; } gid = group_ent->gr_gid; } pipe(pipe_fds); pid = fork(); if (pid > 0) { close(pipe_fds[1]); (*env)->SetIntField(env, obj, f_stdoutFd, pipe_fds[0]); (*env)->SetIntField(env, obj, f_pid, pid); return 1; } else if (pid < 0) { close(pipe_fds[0]); close(pipe_fds[1]); resin_printf_exception(env, "java/lang/NullPointerException", "can't fork"); return 0; } close(pipe_fds[0]); /* if (fork()) exit(0); setsid(); */ #ifndef WIN32 if (chroot_path[0]) { chroot(chroot_path); } #endif if (gid >= 0) setregid(gid, gid); if (uid >= 0) { setreuid(uid, uid); if (getuid() != uid) { fprintf(stderr, "Can't setuid to %d, received %d\n", uid, getuid()); exit(1); } } chdir(pwd); #endif dup2(pipe_fds[1], 1); dup2(pipe_fds[1], 2); for (i = 0; envp[i]; i++) putenv(envp[i]); execvp(argv[0], argv); fprintf(stderr, "exec failed %s -> %d\n", argv[0], errno); exit(1); return -1; } jint Java_com_caucho_bootjni_JniProcess_waitpid(JNIEnv *env, jobject obj, jint pid, jboolean is_block) { int status = 0; int result; #ifdef WIN32 return -1; #else if (pid < 0) { resin_printf_exception(env, "java/lang/IllegalArgumentException", "invalid pid"); return -1; } result = waitpid(pid, &status, is_block ? 0 : WNOHANG); fprintf(stdout, "WAIT RES=%d status=%d\n", result, status); fflush(stdout); if (result == 0) return -1; if (result < 0) { resin_printf_exception(env, "java/lang/IllegalArgumentException", "invalid result %d", result); return -1; } if (WIFSIGNALED(status)) return 128 + WTERMSIG(status); else return WEXITSTATUS(status); #endif }