1 /*
   2  * Copyright (c) 2014, 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 
  55 void os_initNative(JNIEnv *env, jclass clazz) {
  56     bootTime_ms = getBoottime(env);
  57 }
  58 
  59 uid_t os_getUID(pid_t pid) {
  60     char fn[32];
  61     struct stat stat_buf;
  62 
  63     snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid);
  64     if (stat(fn, &stat_buf) == 0) {
  65         return stat_buf.st_uid;
  66     }
  67     return (uid_t) -1;
  68 }
  69 
  70 jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
  71                     jlongArray jparentArray, jlongArray jstimesArray) {
  72     return unix_getChildren(env, jpid, jarray, jparentArray, jstimesArray);
  73 }
  74 
  75 /**
  76  * Read /proc/<pid>/stat and return the ppid, total cputime and start time.
  77  * -1 is fail;  zero is unknown; >  0 is parent pid
  78  */
  79 pid_t os_getParentPid(JNIEnv *env, pid_t pid,
  80                       jlong *totalTime, jlong* startTime) {
  81     FILE* fp;
  82     char buffer[2048];
  83     int statlen;
  84     char fn[32];
  85     char* s;
  86     int parentPid;
  87     long unsigned int utime = 0;      // clock tics
  88     long unsigned int stime = 0;      // clock tics
  89     long long unsigned int start = 0; // microseconds
  90 
  91     /*
  92      * Try to stat and then open /proc/%d/stat
  93      */
  94     snprintf(fn, sizeof fn, "/proc/%d/stat", pid);
  95 
  96     fp = fopen(fn, "r");
  97     if (fp == NULL) {
  98         return -1;              // fail, no such /proc/pid/stat
  99     }
 100 
 101     /*
 102      * The format is: pid (command) state ppid ...
 103      * As the command could be anything we must find the right most
 104      * ")" and then skip the white spaces that follow it.
 105      */
 106     statlen = fread(buffer, 1, (sizeof buffer - 1), fp);
 107     fclose(fp);
 108     if (statlen < 0) {
 109         return 0;               // parent pid is not available
 110     }
 111 
 112     buffer[statlen] = '\0';
 113     s = strchr(buffer, '(');
 114     if (s == NULL) {
 115         return 0;               // parent pid is not available
 116     }
 117     // Found start of command, skip to end
 118     s++;
 119     s = strrchr(s, ')');
 120     if (s == NULL) {
 121         return 0;               // parent pid is not available
 122     }
 123     s++;
 124 
 125     // Scan the needed fields from status, retaining only ppid(4),
 126     // utime (14), stime(15), starttime(22)
 127     if (4 != sscanf(s, " %*c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu",
 128             &parentPid, &utime, &stime, &start)) {
 129         return 0;              // not all values parsed; return error
 130     }
 131 
 132     *totalTime = (utime + stime) * (jlong)(1000000000 / clock_ticks_per_second);
 133 
 134     *startTime = bootTime_ms + ((start * 1000) / clock_ticks_per_second);
 135 
 136     return parentPid;
 137 }
 138 
 139 void os_getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
 140     int fd;
 141     int cmdlen = 0;
 142     char *cmdline = NULL, *cmdEnd;  // used for command line args and exe
 143     jstring cmdexe = NULL;
 144     char fn[32];
 145 
 146     /*
 147      * Try to open /proc/%d/cmdline
 148      */
 149     snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid);
 150     if ((fd = open(fn, O_RDONLY)) < 0) {
 151         return;
 152     }
 153 
 154     do {                // Block to break out of on errors
 155         int i;
 156         char *s;
 157 
 158         cmdline = (char*)malloc(PATH_MAX);
 159         if (cmdline == NULL) {
 160             break;
 161         }
 162 
 163         /*
 164          * The path to the executable command is the link in /proc/<pid>/exe.
 165          */
 166         snprintf(fn, sizeof fn, "/proc/%d/exe", pid);
 167         if ((cmdlen = readlink(fn, cmdline, PATH_MAX - 1)) > 0) {
 168             // null terminate and create String to store for command
 169             cmdline[cmdlen] = '\0';
 170             cmdexe = JNU_NewStringPlatform(env, cmdline);
 171             (*env)->ExceptionClear(env);        // unconditionally clear any exception
 172         }
 173 
 174         /*
 175          * The buffer format is the arguments nul terminated with an extra nul.
 176          */
 177         cmdlen = read(fd, cmdline, PATH_MAX-1);
 178         if (cmdlen < 0) {
 179             break;
 180         }
 181 
 182         // Terminate the buffer and count the arguments
 183         cmdline[cmdlen] = '\0';
 184         cmdEnd = &cmdline[cmdlen + 1];
 185         for (s = cmdline,i = 0; *s != '\0' && (s < cmdEnd); i++) {
 186             s += strnlen(s, (cmdEnd - s)) + 1;
 187         }
 188 
 189         unix_fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe);
 190     } while (0);
 191 
 192     if (cmdline != NULL) {
 193         free(cmdline);
 194     }
 195     if (fd >= 0) {
 196         close(fd);
 197     }
 198 }
 199 
 200 /**
 201  * Read the boottime from /proc/stat.
 202  */
 203 static long long getBoottime(JNIEnv *env) {
 204     FILE *fp;
 205     char *line = NULL;
 206     size_t len = 0;
 207     long long bootTime = 0;
 208 
 209     fp = fopen("/proc/stat", "r");
 210     if (fp == NULL) {
 211         return -1;
 212     }
 213 
 214     while (getline(&line, &len, fp) != -1) {
 215         if (sscanf(line, "btime %llu", &bootTime) == 1) {
 216             break;
 217         }
 218     }
 219     free(line);
 220 
 221     if (fp != 0) {
 222         fclose(fp);
 223     }
 224 
 225     return bootTime * 1000;
 226 }