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 <stdio.h>
  32 #include <errno.h>
  33 #include <signal.h>
  34 #include <stdlib.h>
  35 #include <unistd.h>
  36 #include <string.h>
  37 
  38 #include <sys/sysctl.h>
  39 
  40 /**
  41  * Implementations of ProcessHandleImpl functions for MAC OS X;
  42  * are NOT common to all Unix variants.
  43  */
  44 
  45 static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid);
  46 static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid);
  47 
  48 /*
  49  * Common Unix function to lookup the uid and return the user name.
  50  */
  51 extern jstring uidToUser(JNIEnv* env, uid_t uid);
  52 
  53 /* Field id for jString 'command' in java.lang.ProcessHandle.Info */
  54 static jfieldID ProcessHandleImpl_Info_commandID;
  55 
  56 /* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */
  57 static jfieldID ProcessHandleImpl_Info_argumentsID;
  58 
  59 /* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */
  60 static jfieldID ProcessHandleImpl_Info_totalTimeID;
  61 
  62 /* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */
  63 static jfieldID ProcessHandleImpl_Info_startTimeID;
  64 
  65 /* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
  66 static jfieldID ProcessHandleImpl_Info_userID;
  67 
  68 /* static value for clock ticks per second. */
  69 static long clock_ticks_per_second;
  70 
  71 /**************************************************************
  72  * Static method to initialize field IDs and the ticks per second rate.
  73  *
  74  * Class:     java_lang_ProcessHandleImpl_Info
  75  * Method:    initIDs
  76  * Signature: ()V
  77  */
  78 JNIEXPORT void JNICALL
  79 Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) {
  80     CHECK_NULL(ProcessHandleImpl_Info_commandID =
  81             (*env)->GetFieldID(env, clazz, "command", "Ljava/lang/String;"));
  82     CHECK_NULL(ProcessHandleImpl_Info_argumentsID =
  83             (*env)->GetFieldID(env, clazz, "arguments", "[Ljava/lang/String;"));
  84     CHECK_NULL(ProcessHandleImpl_Info_totalTimeID =
  85             (*env)->GetFieldID(env, clazz, "totalTime", "J"));
  86     CHECK_NULL(ProcessHandleImpl_Info_startTimeID =
  87             (*env)->GetFieldID(env, clazz, "startTime", "J"));
  88     CHECK_NULL(ProcessHandleImpl_Info_userID =
  89             (*env)->GetFieldID(env, clazz, "user", "Ljava/lang/String;"));
  90 }
  91 /**************************************************************
  92  * Static method to initialize the ticks per second rate.
  93  *
  94  * Class:     java_lang_ProcessHandleImpl
  95  * Method:    initNative
  96  * Signature: ()V
  97  */
  98 JNIEXPORT void JNICALL
  99 Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) {
 100       clock_ticks_per_second = sysconf(_SC_CLK_TCK);
 101 }
 102 
 103 /*
 104  * Check if a process is alive.
 105  * Return the start time (ms since 1970) if it is available.
 106  * If the start time is not available return 0.
 107  * If the pid is invalid, return -1.
 108  *
 109  * Class:     java_lang_ProcessHandleImpl
 110  * Method:    isAlive0
 111  * Signature: (J)J
 112  */
 113 JNIEXPORT jlong JNICALL
 114 Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jobject obj, jlong jpid) {
 115     pid_t pid = (pid_t) jpid;
 116     struct kinfo_proc kp;
 117     size_t bufSize = sizeof kp;
 118 
 119     // Read the process info for the specific pid
 120     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
 121 
 122     if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
 123         return  (errno == EINVAL) ? -1 : 0;
 124     } else {
 125         return (bufSize == 0) ?  -1 :
 126                                  (jlong) (kp.kp_proc.p_starttime.tv_sec * 1000
 127                                         + kp.kp_proc.p_starttime.tv_usec / 1000);
 128     }
 129 }
 130 
 131 /*
 132  * Returns the parent pid of the requested pid.
 133  *
 134  * Class:     java_lang_ProcessHandleImpl
 135  * Method:    parent0
 136  * Signature: (J)J
 137  */
 138 JNIEXPORT jlong JNICALL
 139 Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env,
 140                                          jobject obj,
 141                                          jlong jpid,
 142                                          jlong startTime) {
 143     pid_t pid = (pid_t) jpid;
 144     pid_t ppid = -1;
 145 
 146     if (pid == getpid()) {
 147         ppid = getppid();
 148     } else {
 149         const pid_t pid = (pid_t) jpid;
 150         struct kinfo_proc kp;
 151         size_t bufSize = sizeof kp;
 152 
 153         // Read the process info for the specific pid
 154         int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
 155         if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
 156             JNU_ThrowByNameWithLastError(env,
 157                 "java/lang/RuntimeException", "sysctl failed");
 158             return -1;
 159         }
 160         // If the buffer is full and for the pid requested then check the start
 161         if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
 162             jlong start = (jlong) (kp.kp_proc.p_starttime.tv_sec * 1000
 163                                    + kp.kp_proc.p_starttime.tv_usec / 1000);
 164             if (start == startTime || start == 0 || startTime == 0) {
 165                 ppid = kp.kp_eproc.e_ppid;
 166             }
 167         }
 168     }
 169     return (jlong) ppid;
 170 }
 171 
 172 /*
 173  * Returns the children of the requested pid and optionally each parent.
 174  *
 175  * Class:     java_lang_ProcessHandleImpl
 176  * Method:    getProcessPids0
 177  * Signature: (J[J[J)I
 178  *
 179  * Use sysctl to accumulate any process whose parent pid is zero or matches.
 180  * The resulting pids are stored into the array of longs.
 181  * The number of pids is returned if they all fit.
 182  * If the parentArray is non-null, store the parent pid.
 183  * If the array is too short, excess pids are not stored and
 184  * the desired length is returned.
 185  */
 186 JNIEXPORT jint JNICALL
 187 Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env,
 188                                                  jclass clazz,
 189                                                  jlong jpid,
 190                                                  jlongArray jarray,
 191                                                  jlongArray jparentArray,
 192                                                  jlongArray jstimesArray) {
 193     jlong* pids = NULL;
 194     jlong* ppids = NULL;
 195     jlong* stimes = NULL;
 196     jsize parentArraySize = 0;
 197     jsize arraySize = 0;
 198     jsize stimesSize = 0;
 199     jsize count = 0;
 200     size_t bufSize = 0;
 201     pid_t pid = (pid_t) jpid;
 202 
 203     arraySize = (*env)->GetArrayLength(env, jarray);
 204     JNU_CHECK_EXCEPTION_RETURN(env, -1);
 205     if (jparentArray != NULL) {
 206         parentArraySize = (*env)->GetArrayLength(env, jparentArray);
 207         JNU_CHECK_EXCEPTION_RETURN(env, -1);
 208 
 209         if (arraySize != parentArraySize) {
 210             JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
 211             return 0;
 212         }
 213     }
 214     if (jstimesArray != NULL) {
 215         stimesSize = (*env)->GetArrayLength(env, jstimesArray);
 216         JNU_CHECK_EXCEPTION_RETURN(env, -1);
 217 
 218         if (arraySize != stimesSize) {
 219             JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
 220             return 0;
 221         }
 222     }
 223 
 224     // Get buffer size needed to read all processes
 225     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
 226     if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0) {
 227         JNU_ThrowByNameWithLastError(env,
 228             "java/lang/RuntimeException", "sysctl failed");
 229         return -1;
 230     }
 231 
 232     // Allocate buffer big enough for all processes
 233     void *buffer = malloc(bufSize);
 234     if (buffer == NULL) {
 235         JNU_ThrowOutOfMemoryError(env, "malloc failed");
 236         return -1;
 237     }
 238 
 239     // Read process info for all processes
 240     if (sysctl(mib, 4, buffer, &bufSize, NULL, 0) < 0) {
 241         JNU_ThrowByNameWithLastError(env,
 242             "java/lang/RuntimeException", "sysctl failed");
 243         free(buffer);
 244         return -1;
 245     }
 246 
 247     do { // Block to break out of on Exception
 248         struct kinfo_proc *kp = (struct kinfo_proc *) buffer;
 249         unsigned long nentries = bufSize / sizeof (struct kinfo_proc);
 250         long i;
 251 
 252         pids = (*env)->GetLongArrayElements(env, jarray, NULL);
 253         if (pids == NULL) {
 254             break;
 255         }
 256         if (jparentArray != NULL) {
 257             ppids  = (*env)->GetLongArrayElements(env, jparentArray, NULL);
 258             if (ppids == NULL) {
 259                 break;
 260             }
 261         }
 262         if (jstimesArray != NULL) {
 263             stimes  = (*env)->GetLongArrayElements(env, jstimesArray, NULL);
 264             if (stimes == NULL) {
 265                 break;
 266             }
 267         }
 268 
 269         // Process each entry in the buffer
 270         for (i = nentries; --i >= 0; ++kp) {
 271             if (pid == 0 || kp->kp_eproc.e_ppid == pid) {
 272                 if (count < arraySize) {
 273                     // Only store if it fits
 274                     pids[count] = (jlong) kp->kp_proc.p_pid;
 275                     if (ppids != NULL) {
 276                         // Store the parentPid
 277                         ppids[count] = (jlong) kp->kp_eproc.e_ppid;
 278                     }
 279                     if (stimes != NULL) {
 280                         // Store the process start time
 281                         jlong startTime = kp->kp_proc.p_starttime.tv_sec * 1000 +
 282                                           kp->kp_proc.p_starttime.tv_usec / 1000;
 283                         stimes[count] = startTime;
 284                     }
 285                 }
 286                 count++; // Count to tabulate size needed
 287             }
 288         }
 289     } while (0);
 290 
 291     if (pids != NULL) {
 292         (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
 293     }
 294     if (ppids != NULL) {
 295         (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
 296     }
 297     if (stimes != NULL) {
 298         (*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);
 299     }
 300 
 301     free(buffer);
 302     // If more pids than array had size for; count will be greater than array size
 303     return count;
 304 }
 305 
 306 /**************************************************************
 307  * Implementation of ProcessHandleImpl_Info native methods.
 308  */
 309 
 310 /*
 311  * Fill in the Info object from the OS information about the process.
 312  *
 313  * Class:     java_lang_ProcessHandleImpl
 314  * Method:    info0
 315  * Signature: (J)I
 316  */
 317 JNIEXPORT void JNICALL
 318 Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env,
 319                                                  jobject jinfo,
 320                                                  jlong jpid) {
 321     pid_t pid = (pid_t) jpid;
 322     getStatInfo(env, jinfo, pid);
 323     getCmdlineInfo(env, jinfo, pid);
 324 }
 325 
 326 /**
 327  * Read /proc/<pid>/stat and fill in the fields of the Info object.
 328  * The executable name, plus the user, system, and start times are gathered.
 329  */
 330 static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t jpid) {
 331     jlong totalTime;                    // nanoseconds
 332     unsigned long long startTime;       // milliseconds
 333 
 334     const pid_t pid = (pid_t) jpid;
 335     struct kinfo_proc kp;
 336     size_t bufSize = sizeof kp;
 337 
 338     // Read the process info for the specific pid
 339     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
 340 
 341     if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
 342         if (errno == EINVAL) {
 343             return;
 344         } else {
 345             JNU_ThrowByNameWithLastError(env,
 346                 "java/lang/RuntimeException", "sysctl failed");
 347         }
 348         return;
 349     }
 350 
 351     // Convert the UID to the username
 352     jstring name = NULL;
 353     CHECK_NULL((name = uidToUser(env, kp.kp_eproc.e_ucred.cr_uid)));
 354     (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name);
 355     JNU_CHECK_EXCEPTION(env);
 356 
 357     startTime = kp.kp_proc.p_starttime.tv_sec * 1000 +
 358                 kp.kp_proc.p_starttime.tv_usec / 1000;
 359 
 360     (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
 361     JNU_CHECK_EXCEPTION(env);
 362 
 363     // Get cputime if for current process
 364     if (pid == getpid()) {
 365         struct rusage usage;
 366         if (getrusage(RUSAGE_SELF, &usage) != 0) {
 367             return;
 368         }
 369         jlong microsecs =
 370             usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec +
 371             usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec;
 372         totalTime = microsecs * 1000;
 373         (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime);
 374         JNU_CHECK_EXCEPTION(env);
 375     }
 376 }
 377 
 378 /**
 379  * Construct the argument array by parsing the arguments from the sequence of arguments.
 380  */
 381 static int fillArgArray(JNIEnv *env, jobject jinfo, int nargs,
 382                         const char *cp, const char *argsEnd) {
 383     jstring str = NULL;
 384     jobject argsArray;
 385     int i;
 386 
 387     if (nargs < 1) {
 388         return 0;
 389     }
 390     // Create a String array for nargs-1 elements
 391     CHECK_NULL_RETURN((argsArray = (*env)->NewObjectArray(env,
 392             nargs - 1, JNU_ClassString(env), NULL)), -1);
 393 
 394     for (i = 0; i < nargs - 1; i++) {
 395         // skip to the next argument; omits arg[0]
 396         cp += strnlen(cp, (argsEnd - cp)) + 1;
 397 
 398         if (cp > argsEnd || *cp == '\0') {
 399             return -2;  // Off the end pointer or an empty argument is an error
 400         }
 401 
 402         CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1);
 403 
 404         (*env)->SetObjectArrayElement(env, argsArray, i, str);
 405         JNU_CHECK_EXCEPTION_RETURN(env, -3);
 406     }
 407     (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray);
 408     JNU_CHECK_EXCEPTION_RETURN(env, -4);
 409     return 0;
 410 }
 411 
 412 /**
 413  * Retrieve the command and arguments for the process and store them
 414  * into the Info object.
 415  */
 416 static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
 417     int mib[3], maxargs, nargs, i;
 418     size_t size;
 419     char *args, *cp, *sp, *np;
 420 
 421     // Get the maximum size of the arguments
 422     mib[0] = CTL_KERN;
 423     mib[1] = KERN_ARGMAX;
 424     size = sizeof(maxargs);
 425     if (sysctl(mib, 2, &maxargs, &size, NULL, 0) == -1) {
 426             JNU_ThrowByNameWithLastError(env,
 427                     "java/lang/RuntimeException", "sysctl failed");
 428         return;
 429     }
 430 
 431     // Allocate an args buffer and get the arguments
 432     args = (char *)malloc(maxargs);
 433     if (args == NULL) {
 434         JNU_ThrowOutOfMemoryError(env, "malloc failed");
 435         return;
 436     }
 437 
 438     do {            // a block to break out of on error
 439         char *argsEnd;
 440         jstring str = NULL;
 441 
 442         mib[0] = CTL_KERN;
 443         mib[1] = KERN_PROCARGS2;
 444         mib[2] = pid;
 445         size = (size_t) maxargs;
 446         if (sysctl(mib, 3, args, &size, NULL, 0) == -1) {
 447             if (errno != EINVAL) {
 448             JNU_ThrowByNameWithLastError(env,
 449                     "java/lang/RuntimeException", "sysctl failed");
 450             }
 451             break;
 452         }
 453         memcpy(&nargs, args, sizeof(nargs));
 454 
 455         cp = &args[sizeof(nargs)];      // Strings start after nargs
 456         argsEnd = &args[size];
 457 
 458         // Store the command executable path
 459         if ((str = JNU_NewStringPlatform(env, cp)) == NULL) {
 460             break;
 461         }
 462         (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, str);
 463         if ((*env)->ExceptionCheck(env)) {
 464             break;
 465         }
 466 
 467         // Skip trailing nulls after the executable path
 468         for (cp = cp + strnlen(cp, argsEnd - cp); cp < argsEnd; cp++) {
 469             if (*cp != '\0') {
 470                 break;
 471             }
 472         }
 473 
 474         fillArgArray(env, jinfo, nargs, cp, argsEnd);
 475     } while (0);
 476     // Free the arg buffer
 477     free(args);
 478 }
 479