/* * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include "jni.h" #include "jni_util.h" #include "java_lang_ProcessHandleImpl.h" #include "java_lang_ProcessHandleImpl_Info.h" #include "ProcessHandleImpl_unix.h" #include #include #include #include #include #include #include #include /* * Implementation of native ProcessHandleImpl functions for Linux. * See ProcessHandleImpl_unix.c for more details. */ /* Signatures for internal OS specific functions. */ static long long getBoottime(JNIEnv *env); /* A static offset in milliseconds since boot. */ static long long bootTime_ms; void os_initNative(JNIEnv *env, jclass clazz) { bootTime_ms = getBoottime(env); } jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, jlongArray jparentArray, jlongArray jstimesArray) { return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray); } /** * Read /proc//stat and return the ppid, total cputime and start time. * -1 is fail; zero is unknown; > 0 is parent pid * 'total' will contain the running time of 'pid' in nanoseconds. * 'start' will contain the start time of 'pid' in milliseconds since epoch. */ pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid, jlong *totalTime, jlong* startTime) { FILE* fp; char buffer[2048]; int statlen; char fn[32]; char* s; int parentPid; long unsigned int utime = 0; // clock tics long unsigned int stime = 0; // clock tics long long unsigned int start = 0; // microseconds /* * Try to stat and then open /proc/%d/stat */ snprintf(fn, sizeof fn, "/proc/%d/stat", pid); fp = fopen(fn, "r"); if (fp == NULL) { return -1; // fail, no such /proc/pid/stat } /* * The format is: pid (command) state ppid ... * As the command could be anything we must find the right most * ")" and then skip the white spaces that follow it. */ statlen = fread(buffer, 1, (sizeof buffer - 1), fp); fclose(fp); if (statlen < 0) { return 0; // parent pid is not available } buffer[statlen] = '\0'; s = strchr(buffer, '('); if (s == NULL) { return 0; // parent pid is not available } // Found start of command, skip to end s++; s = strrchr(s, ')'); if (s == NULL) { return 0; // parent pid is not available } s++; // Scan the needed fields from status, retaining only ppid(4), // utime (14), stime(15), starttime(22) if (4 != sscanf(s, " %*c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu", &parentPid, &utime, &stime, &start)) { return 0; // not all values parsed; return error } *totalTime = (utime + stime) * (jlong)(1000000000 / clock_ticks_per_second); *startTime = bootTime_ms + ((start * 1000) / clock_ticks_per_second); return parentPid; } void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) { int fd; int cmdlen = 0; char *cmdline = NULL, *cmdEnd; // used for command line args and exe jstring cmdexe = NULL; char fn[32]; struct stat stat_buf; /* * Try to open /proc/%d/cmdline */ snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid); if ((fd = open(fn, O_RDONLY)) < 0) { return; } if (fstat(fd, &stat_buf) == 0) { unix_getUserInfo(env, jinfo, stat_buf.st_uid); } do { // Block to break out of on errors int i; char *s; cmdline = (char*)malloc(PATH_MAX); if (cmdline == NULL) { break; } /* * The path to the executable command is the link in /proc//exe. */ snprintf(fn, sizeof fn, "/proc/%d/exe", pid); if ((cmdlen = readlink(fn, cmdline, PATH_MAX - 1)) > 0) { // null terminate and create String to store for command cmdline[cmdlen] = '\0'; cmdexe = JNU_NewStringPlatform(env, cmdline); (*env)->ExceptionClear(env); // unconditionally clear any exception } /* * The buffer format is the arguments nul terminated with an extra nul. */ cmdlen = read(fd, cmdline, PATH_MAX-1); if (cmdlen < 0) { break; } // Terminate the buffer and count the arguments cmdline[cmdlen] = '\0'; cmdEnd = &cmdline[cmdlen + 1]; for (s = cmdline,i = 0; *s != '\0' && (s < cmdEnd); i++) { s += strnlen(s, (cmdEnd - s)) + 1; } unix_fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe); } while (0); if (cmdline != NULL) { free(cmdline); } if (fd >= 0) { close(fd); } } /** * Read the boottime from /proc/stat. */ static long long getBoottime(JNIEnv *env) { FILE *fp; char *line = NULL; size_t len = 0; long long bootTime = 0; fp = fopen("/proc/stat", "r"); if (fp == NULL) { return -1; } while (getline(&line, &len, fp) != -1) { if (sscanf(line, "btime %llu", &bootTime) == 1) { break; } } free(line); if (fp != 0) { fclose(fp); } return bootTime * 1000; }