jdk/src/java.base/linux/native/libjava/ProcessHandleImpl_linux.c
changeset 32209 24bb680a1609
child 32517 47fa336854c3
equal deleted inserted replaced
32167:c8753d0be177 32209:24bb680a1609
       
     1 /*
       
     2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 #include "jni.h"
       
    27 #include "jni_util.h"
       
    28 #include "java_lang_ProcessHandleImpl.h"
       
    29 #include "java_lang_ProcessHandleImpl_Info.h"
       
    30 
       
    31 #include "ProcessHandleImpl_unix.h"
       
    32 
       
    33 
       
    34 #include <fcntl.h>
       
    35 #include <limits.h>
       
    36 #include <stdlib.h>
       
    37 #include <unistd.h>
       
    38 #include <sys/types.h>
       
    39 #include <sys/stat.h>
       
    40 
       
    41 #include <string.h>
       
    42 #include <ctype.h>
       
    43 
       
    44 /*
       
    45  * Implementation of native ProcessHandleImpl functions for Linux.
       
    46  * See ProcessHandleImpl_unix.c for more details.
       
    47  */
       
    48 
       
    49 /* Signatures for internal OS specific functions. */
       
    50 static long long getBoottime(JNIEnv *env);
       
    51 
       
    52 /* A static offset in milliseconds since boot. */
       
    53 static long long bootTime_ms;
       
    54 static long clock_ticks_per_second;
       
    55 static int pageSize;
       
    56 
       
    57 void os_initNative(JNIEnv *env, jclass clazz) {
       
    58     bootTime_ms = getBoottime(env);
       
    59     clock_ticks_per_second = sysconf(_SC_CLK_TCK);
       
    60     pageSize = sysconf(_SC_PAGESIZE);
       
    61 }
       
    62 
       
    63 jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
       
    64                     jlongArray jparentArray, jlongArray jstimesArray) {
       
    65     return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray);
       
    66 }
       
    67 
       
    68 /**
       
    69  * Read /proc/<pid>/stat and return the ppid, total cputime and start time.
       
    70  * -1 is fail;  >=  0 is parent pid
       
    71  * 'total' will contain the running time of 'pid' in nanoseconds.
       
    72  * 'start' will contain the start time of 'pid' in milliseconds since epoch.
       
    73  */
       
    74 pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t pid,
       
    75                                 jlong *totalTime, jlong* startTime) {
       
    76     FILE* fp;
       
    77     char buffer[2048];
       
    78     int statlen;
       
    79     char fn[32];
       
    80     char* s;
       
    81     int parentPid;
       
    82     long unsigned int utime = 0;      // clock tics
       
    83     long unsigned int stime = 0;      // clock tics
       
    84     long long unsigned int start = 0; // microseconds
       
    85 
       
    86     /*
       
    87      * Try to stat and then open /proc/%d/stat
       
    88      */
       
    89     snprintf(fn, sizeof fn, "/proc/%d/stat", pid);
       
    90 
       
    91     fp = fopen(fn, "r");
       
    92     if (fp == NULL) {
       
    93         return -1;              // fail, no such /proc/pid/stat
       
    94     }
       
    95 
       
    96     /*
       
    97      * The format is: pid (command) state ppid ...
       
    98      * As the command could be anything we must find the right most
       
    99      * ")" and then skip the white spaces that follow it.
       
   100      */
       
   101     statlen = fread(buffer, 1, (sizeof buffer - 1), fp);
       
   102     fclose(fp);
       
   103     if (statlen < 0) {
       
   104         return -1;               // parent pid is not available
       
   105     }
       
   106 
       
   107     buffer[statlen] = '\0';
       
   108     s = strchr(buffer, '(');
       
   109     if (s == NULL) {
       
   110         return -1;               // parent pid is not available
       
   111     }
       
   112     // Found start of command, skip to end
       
   113     s++;
       
   114     s = strrchr(s, ')');
       
   115     if (s == NULL) {
       
   116         return -1;               // parent pid is not available
       
   117     }
       
   118     s++;
       
   119 
       
   120     // Scan the needed fields from status, retaining only ppid(4),
       
   121     // utime (14), stime(15), starttime(22)
       
   122     if (4 != sscanf(s, " %*c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu",
       
   123             &parentPid, &utime, &stime, &start)) {
       
   124         return 0;              // not all values parsed; return error
       
   125     }
       
   126 
       
   127     *totalTime = (utime + stime) * (jlong)(1000000000 / clock_ticks_per_second);
       
   128 
       
   129     *startTime = bootTime_ms + ((start * 1000) / clock_ticks_per_second);
       
   130 
       
   131     return parentPid;
       
   132 }
       
   133 
       
   134 void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
       
   135     int fd;
       
   136     int cmdlen = 0;
       
   137     char *cmdline = NULL, *cmdEnd = NULL; // used for command line args and exe
       
   138     char *args = NULL;
       
   139     jstring cmdexe = NULL;
       
   140     char fn[32];
       
   141     struct stat stat_buf;
       
   142 
       
   143     /*
       
   144      * Try to open /proc/<pid>/cmdline
       
   145      */
       
   146     snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid);
       
   147     if ((fd = open(fn, O_RDONLY)) < 0) {
       
   148         return;
       
   149     }
       
   150 
       
   151     if (fstat(fd, &stat_buf) == 0) {
       
   152         unix_getUserInfo(env, jinfo, stat_buf.st_uid);
       
   153     }
       
   154 
       
   155     do {                // Block to break out of on errors
       
   156         int i, truncated = 0;
       
   157         int count;
       
   158         char *s;
       
   159 
       
   160         /*
       
   161          * The path name read by readlink() is limited to PATH_MAX characters.
       
   162          * The content of /proc/<pid>/cmdline is limited to PAGE_SIZE characters.
       
   163          */
       
   164         cmdline = (char*)malloc((PATH_MAX > pageSize ? PATH_MAX : pageSize) + 1);
       
   165         if (cmdline == NULL) {
       
   166             break;
       
   167         }
       
   168 
       
   169         /*
       
   170          * On Linux, the full path to the executable command is the link in
       
   171          * /proc/<pid>/exe. But it is only readable for processes we own.
       
   172          */
       
   173         snprintf(fn, sizeof fn, "/proc/%d/exe", pid);
       
   174         if ((cmdlen = readlink(fn, cmdline, PATH_MAX)) > 0) {
       
   175             // null terminate and create String to store for command
       
   176             cmdline[cmdlen] = '\0';
       
   177             cmdexe = JNU_NewStringPlatform(env, cmdline);
       
   178             (*env)->ExceptionClear(env);        // unconditionally clear any exception
       
   179         }
       
   180 
       
   181         /*
       
   182          * The command-line arguments appear as a set of strings separated by
       
   183          * null bytes ('\0'), with a further null byte after the last
       
   184          * string. The last string is only null terminated if the whole command
       
   185          * line is not exceeding (PAGE_SIZE - 1) characters.
       
   186          */
       
   187         cmdlen = 0;
       
   188         s = cmdline;
       
   189         while ((count = read(fd, s, pageSize - cmdlen)) > 0) {
       
   190             cmdlen += count;
       
   191             s += count;
       
   192         }
       
   193         if (count < 0) {
       
   194             break;
       
   195         }
       
   196         // We have to null-terminate because the process may have changed argv[]
       
   197         // or because the content in /proc/<pid>/cmdline is truncated.
       
   198         cmdline[cmdlen] = '\0';
       
   199         if (cmdlen == pageSize && cmdline[pageSize - 1] != '\0') {
       
   200             truncated = 1;
       
   201         } else if (cmdlen == 0) {
       
   202             // /proc/<pid>/cmdline was empty. This usually happens for kernel processes
       
   203             // like '[kthreadd]'. We could try to read /proc/<pid>/comm in the future.
       
   204         }
       
   205         if (cmdlen > 0 && (cmdexe == NULL || truncated)) {
       
   206             // We have no exact command or the arguments are truncated.
       
   207             // In this case we save the command line from /proc/<pid>/cmdline.
       
   208             args = (char*)malloc(pageSize + 1);
       
   209             if (args != NULL) {
       
   210                 memcpy(args, cmdline, cmdlen + 1);
       
   211                 for (i = 0; i < cmdlen; i++) {
       
   212                     if (args[i] == '\0') {
       
   213                         args[i] = ' ';
       
   214                     }
       
   215                 }
       
   216             }
       
   217         }
       
   218         i = 0;
       
   219         if (!truncated) {
       
   220             // Count the arguments
       
   221             cmdEnd = &cmdline[cmdlen];
       
   222             for (s = cmdline; *s != '\0' && (s < cmdEnd); i++) {
       
   223                 s += strnlen(s, (cmdEnd - s)) + 1;
       
   224             }
       
   225         }
       
   226         unix_fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe, args);
       
   227     } while (0);
       
   228 
       
   229     if (cmdline != NULL) {
       
   230         free(cmdline);
       
   231     }
       
   232     if (args != NULL) {
       
   233         free(args);
       
   234     }
       
   235     if (fd >= 0) {
       
   236         close(fd);
       
   237     }
       
   238 }
       
   239 
       
   240 /**
       
   241  * Read the boottime from /proc/stat.
       
   242  */
       
   243 static long long getBoottime(JNIEnv *env) {
       
   244     FILE *fp;
       
   245     char *line = NULL;
       
   246     size_t len = 0;
       
   247     long long bootTime = 0;
       
   248 
       
   249     fp = fopen("/proc/stat", "r");
       
   250     if (fp == NULL) {
       
   251         return -1;
       
   252     }
       
   253 
       
   254     while (getline(&line, &len, fp) != -1) {
       
   255         if (sscanf(line, "btime %llu", &bootTime) == 1) {
       
   256             break;
       
   257         }
       
   258     }
       
   259     free(line);
       
   260 
       
   261     if (fp != 0) {
       
   262         fclose(fp);
       
   263     }
       
   264 
       
   265     return bootTime * 1000;
       
   266 }