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 #include <stdio.h>
34 #include <errno.h>
35 #include <signal.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <string.h>
39 
40 #include <sys/sysctl.h>
41 
42 /**
43  * Implementation of native ProcessHandleImpl functions for MAC OS X.
44  * See ProcessHandleImpl_unix.c for more details.
45  */
46 
os_initNative(JNIEnv * env,jclass clazz)47 void os_initNative(JNIEnv *env, jclass clazz) {}
48 
49 /*
50  * Returns the children of the requested pid and optionally each parent.
51  *
52  * Use sysctl to accumulate any process whose parent pid is zero or matches.
53  * The resulting pids are stored into the array of longs.
54  * The number of pids is returned if they all fit.
55  * If the parentArray is non-null, store the parent pid.
56  * If the array is too short, excess pids are not stored and
57  * the desired length is returned.
58  */
os_getChildren(JNIEnv * env,jlong jpid,jlongArray jarray,jlongArray jparentArray,jlongArray jstimesArray)59 jint os_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray,
60                     jlongArray jparentArray, jlongArray jstimesArray) {
61     jlong* pids = NULL;
62     jlong* ppids = NULL;
63     jlong* stimes = NULL;
64     jsize parentArraySize = 0;
65     jsize arraySize = 0;
66     jsize stimesSize = 0;
67     jsize count = 0;
68     size_t bufSize = 0;
69     pid_t pid = (pid_t) jpid;
70 
71     arraySize = (*env)->GetArrayLength(env, jarray);
72     JNU_CHECK_EXCEPTION_RETURN(env, -1);
73     if (jparentArray != NULL) {
74         parentArraySize = (*env)->GetArrayLength(env, jparentArray);
75         JNU_CHECK_EXCEPTION_RETURN(env, -1);
76 
77         if (arraySize != parentArraySize) {
78             JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
79             return 0;
80         }
81     }
82     if (jstimesArray != NULL) {
83         stimesSize = (*env)->GetArrayLength(env, jstimesArray);
84         JNU_CHECK_EXCEPTION_RETURN(env, -1);
85 
86         if (arraySize != stimesSize) {
87             JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
88             return 0;
89         }
90     }
91 
92     // Get buffer size needed to read all processes
93     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
94     if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0) {
95         JNU_ThrowByNameWithLastError(env,
96             "java/lang/RuntimeException", "sysctl failed");
97         return -1;
98     }
99 
100     // Allocate buffer big enough for all processes
101     void *buffer = malloc(bufSize);
102     if (buffer == NULL) {
103         JNU_ThrowOutOfMemoryError(env, "malloc failed");
104         return -1;
105     }
106 
107     // Read process info for all processes
108     if (sysctl(mib, 4, buffer, &bufSize, NULL, 0) < 0) {
109         JNU_ThrowByNameWithLastError(env,
110             "java/lang/RuntimeException", "sysctl failed");
111         free(buffer);
112         return -1;
113     }
114 
115     do { // Block to break out of on Exception
116         struct kinfo_proc *kp = (struct kinfo_proc *) buffer;
117         unsigned long nentries = bufSize / sizeof (struct kinfo_proc);
118         long i;
119 
120         pids = (*env)->GetLongArrayElements(env, jarray, NULL);
121         if (pids == NULL) {
122             break;
123         }
124         if (jparentArray != NULL) {
125             ppids  = (*env)->GetLongArrayElements(env, jparentArray, NULL);
126             if (ppids == NULL) {
127                 break;
128             }
129         }
130         if (jstimesArray != NULL) {
131             stimes  = (*env)->GetLongArrayElements(env, jstimesArray, NULL);
132             if (stimes == NULL) {
133                 break;
134             }
135         }
136 
137         // Process each entry in the buffer
138         for (i = nentries; --i >= 0; ++kp) {
139             if (pid == 0 || kp->kp_eproc.e_ppid == pid) {
140                 if (count < arraySize) {
141                     // Only store if it fits
142                     pids[count] = (jlong) kp->kp_proc.p_pid;
143                     if (ppids != NULL) {
144                         // Store the parentPid
145                         ppids[count] = (jlong) kp->kp_eproc.e_ppid;
146                     }
147                     if (stimes != NULL) {
148                         // Store the process start time
149                         jlong startTime = kp->kp_proc.p_starttime.tv_sec * 1000 +
150                                           kp->kp_proc.p_starttime.tv_usec / 1000;
151                         stimes[count] = startTime;
152                     }
153                 }
154                 count++; // Count to tabulate size needed
155             }
156         }
157     } while (0);
158 
159     if (pids != NULL) {
160         (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
161     }
162     if (ppids != NULL) {
163         (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
164     }
165     if (stimes != NULL) {
166         (*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0);
167     }
168 
169     free(buffer);
170     // If more pids than array had size for; count will be greater than array size
171     return count;
172 }
173 
174 /**
175  * Use sysctl and return the ppid, total cputime and start time.
176  * Return: -1 is fail;  >=  0 is parent pid
177  * 'total' will contain the running time of 'pid' in nanoseconds.
178  * 'start' will contain the start time of 'pid' in milliseconds since epoch.
179  */
os_getParentPidAndTimings(JNIEnv * env,pid_t jpid,jlong * totalTime,jlong * startTime)180 pid_t os_getParentPidAndTimings(JNIEnv *env, pid_t jpid,
181                                 jlong *totalTime, jlong *startTime) {
182 
183     const pid_t pid = (pid_t) jpid;
184     pid_t ppid = -1;
185     struct kinfo_proc kp;
186     size_t bufSize = sizeof kp;
187 
188     // Read the process info for the specific pid
189     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
190 
191     if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
192         JNU_ThrowByNameWithLastError(env,
193             "java/lang/RuntimeException", "sysctl failed");
194         return -1;
195     }
196     if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
197         *startTime = (jlong) (kp.kp_proc.p_starttime.tv_sec * 1000 +
198                               kp.kp_proc.p_starttime.tv_usec / 1000);
199         ppid = kp.kp_eproc.e_ppid;
200     }
201 
202     // Get cputime if for current process
203     if (pid == getpid()) {
204         struct rusage usage;
205         if (getrusage(RUSAGE_SELF, &usage) == 0) {
206           jlong microsecs =
207               usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec +
208               usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec;
209           *totalTime = microsecs * 1000;
210         }
211     }
212 
213     return ppid;
214 
215 }
216 
217 /**
218  * Return the uid of a process or -1 on error
219  */
getUID(pid_t pid)220 static uid_t getUID(pid_t pid) {
221     struct kinfo_proc kp;
222     size_t bufSize = sizeof kp;
223 
224     // Read the process info for the specific pid
225     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
226 
227     if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) == 0) {
228         if (bufSize > 0 && kp.kp_proc.p_pid == pid) {
229             return kp.kp_eproc.e_ucred.cr_uid;
230         }
231     }
232     return (uid_t)-1;
233 }
234 
235 /**
236  * Retrieve the command and arguments for the process and store them
237  * into the Info object.
238  */
os_getCmdlineAndUserInfo(JNIEnv * env,jobject jinfo,pid_t pid)239 void os_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
240     int mib[3], maxargs, nargs, i;
241     size_t size;
242     char *args, *cp, *sp, *np;
243 
244     // Get the UID first. This is done here because it is cheap to do it here
245     // on other platforms like Linux/Solaris/AIX where the uid comes from the
246     // same source like the command line info.
247     unix_getUserInfo(env, jinfo, getUID(pid));
248 
249     // Get the maximum size of the arguments
250     mib[0] = CTL_KERN;
251     mib[1] = KERN_ARGMAX;
252     size = sizeof(maxargs);
253     if (sysctl(mib, 2, &maxargs, &size, NULL, 0) == -1) {
254             JNU_ThrowByNameWithLastError(env,
255                 "java/lang/RuntimeException", "sysctl failed");
256         return;
257     }
258 
259     // Allocate an args buffer and get the arguments
260     args = (char *)malloc(maxargs);
261     if (args == NULL) {
262         JNU_ThrowOutOfMemoryError(env, "malloc failed");
263         return;
264     }
265 
266     do {            // a block to break out of on error
267         char *argsEnd;
268         jstring cmdexe = NULL;
269 
270         mib[0] = CTL_KERN;
271         mib[1] = KERN_PROCARGS2;
272         mib[2] = pid;
273         size = (size_t) maxargs;
274         if (sysctl(mib, 3, args, &size, NULL, 0) == -1) {
275             if (errno != EINVAL) {
276                 JNU_ThrowByNameWithLastError(env,
277                     "java/lang/RuntimeException", "sysctl failed");
278             }
279             break;
280         }
281         memcpy(&nargs, args, sizeof(nargs));
282 
283         cp = &args[sizeof(nargs)];      // Strings start after nargs
284         argsEnd = &args[size];
285 
286         // Store the command executable path
287         if ((cmdexe = JNU_NewStringPlatform(env, cp)) == NULL) {
288             break;
289         }
290 
291         // Skip trailing nulls after the executable path
292         for (cp = cp + strnlen(cp, argsEnd - cp); cp < argsEnd; cp++) {
293             if (*cp != '\0') {
294                 break;
295             }
296         }
297 
298         unix_fillArgArray(env, jinfo, nargs, cp, argsEnd, cmdexe, NULL);
299     } while (0);
300     // Free the arg buffer
301     free(args);
302 }
303 
304