|
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 |
|
32 #include <stdio.h> |
|
33 |
|
34 #include <errno.h> |
|
35 #include <fcntl.h> |
|
36 #include <pwd.h> |
|
37 #include <signal.h> |
|
38 #include <stdlib.h> |
|
39 #include <unistd.h> |
|
40 #include <sys/types.h> |
|
41 #include <sys/stat.h> |
|
42 #include <sys/wait.h> |
|
43 |
|
44 #include <string.h> |
|
45 #include <dirent.h> |
|
46 #include <ctype.h> |
|
47 |
|
48 /** |
|
49 * Implementations of ProcessHandleImpl functions that are common to all |
|
50 * Unix variants: |
|
51 * - waitForProcessExit0(pid, reap) |
|
52 * - getCurrentPid0() |
|
53 * - destroy0(pid, force) |
|
54 */ |
|
55 |
|
56 |
|
57 #ifndef WIFEXITED |
|
58 #define WIFEXITED(status) (((status)&0xFF) == 0) |
|
59 #endif |
|
60 |
|
61 #ifndef WEXITSTATUS |
|
62 #define WEXITSTATUS(status) (((status)>>8)&0xFF) |
|
63 #endif |
|
64 |
|
65 #ifndef WIFSIGNALED |
|
66 #define WIFSIGNALED(status) (((status)&0xFF) > 0 && ((status)&0xFF00) == 0) |
|
67 #endif |
|
68 |
|
69 #ifndef WTERMSIG |
|
70 #define WTERMSIG(status) ((status)&0x7F) |
|
71 #endif |
|
72 |
|
73 #define RESTARTABLE(_cmd, _result) do { \ |
|
74 do { \ |
|
75 _result = _cmd; \ |
|
76 } while((_result == -1) && (errno == EINTR)); \ |
|
77 } while(0) |
|
78 |
|
79 #define RESTARTABLE_RETURN_PTR(_cmd, _result) do { \ |
|
80 do { \ |
|
81 _result = _cmd; \ |
|
82 } while((_result == NULL) && (errno == EINTR)); \ |
|
83 } while(0) |
|
84 |
|
85 |
|
86 /* Block until a child process exits and return its exit code. |
|
87 * Note, can only be called once for any given pid if reapStatus = true. |
|
88 */ |
|
89 JNIEXPORT jint JNICALL |
|
90 Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env, |
|
91 jclass junk, |
|
92 jlong jpid, |
|
93 jboolean reapStatus) |
|
94 { |
|
95 pid_t pid = (pid_t)jpid; |
|
96 errno = 0; |
|
97 |
|
98 if (reapStatus != JNI_FALSE) { |
|
99 /* Wait for the child process to exit. |
|
100 * waitpid() is standard, so use it on all POSIX platforms. |
|
101 * It is known to work when blocking to wait for the pid |
|
102 * This returns immediately if the child has already exited. |
|
103 */ |
|
104 int status; |
|
105 while (waitpid(pid, &status, 0) < 0) { |
|
106 switch (errno) { |
|
107 case ECHILD: return 0; |
|
108 case EINTR: break; |
|
109 default: return -1; |
|
110 } |
|
111 } |
|
112 |
|
113 if (WIFEXITED(status)) { |
|
114 return WEXITSTATUS(status); |
|
115 } else if (WIFSIGNALED(status)) { |
|
116 /* The child exited because of a signal. |
|
117 * The best value to return is 0x80 + signal number, |
|
118 * because that is what all Unix shells do, and because |
|
119 * it allows callers to distinguish between process exit and |
|
120 * process death by signal. |
|
121 * Unfortunately, the historical behavior on Solaris is to return |
|
122 * the signal number, and we preserve this for compatibility. */ |
|
123 #ifdef __solaris__ |
|
124 return WTERMSIG(status); |
|
125 #else |
|
126 return 0x80 + WTERMSIG(status); |
|
127 #endif |
|
128 } else { |
|
129 return status; |
|
130 } |
|
131 } else { |
|
132 /* |
|
133 * Wait for the child process to exit without reaping the exitValue. |
|
134 * waitid() is standard on all POSIX platforms. |
|
135 * Note: waitid on Mac OS X 10.7 seems to be broken; |
|
136 * it does not return the exit status consistently. |
|
137 */ |
|
138 siginfo_t siginfo; |
|
139 int options = WEXITED | WNOWAIT; |
|
140 memset(&siginfo, 0, sizeof siginfo); |
|
141 while (waitid(P_PID, pid, &siginfo, options) < 0) { |
|
142 switch (errno) { |
|
143 case ECHILD: return 0; |
|
144 case EINTR: break; |
|
145 default: return -1; |
|
146 } |
|
147 } |
|
148 |
|
149 if (siginfo.si_code == CLD_EXITED) { |
|
150 /* |
|
151 * The child exited normally; get its exit code. |
|
152 */ |
|
153 return siginfo.si_status; |
|
154 } else if (siginfo.si_code == CLD_KILLED || siginfo.si_code == CLD_DUMPED) { |
|
155 /* The child exited because of a signal. |
|
156 * The best value to return is 0x80 + signal number, |
|
157 * because that is what all Unix shells do, and because |
|
158 * it allows callers to distinguish between process exit and |
|
159 * process death by signal. |
|
160 * Unfortunately, the historical behavior on Solaris is to return |
|
161 * the signal number, and we preserve this for compatibility. */ |
|
162 #ifdef __solaris__ |
|
163 return WTERMSIG(siginfo.si_status); |
|
164 #else |
|
165 return 0x80 + WTERMSIG(siginfo.si_status); |
|
166 #endif |
|
167 } else { |
|
168 /* |
|
169 * Unknown exit code; pass it through. |
|
170 */ |
|
171 return siginfo.si_status; |
|
172 } |
|
173 } |
|
174 } |
|
175 |
|
176 /* |
|
177 * Class: java_lang_ProcessHandleImpl |
|
178 * Method: getCurrentPid0 |
|
179 * Signature: ()J |
|
180 */ |
|
181 JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_getCurrentPid0 |
|
182 (JNIEnv *env, jclass clazz) { |
|
183 pid_t pid = getpid(); |
|
184 return (jlong) pid; |
|
185 } |
|
186 |
|
187 /* |
|
188 * Class: java_lang_ProcessHandleImpl |
|
189 * Method: isAlive0 |
|
190 * Signature: (J)Z |
|
191 */ |
|
192 JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_isAlive0 |
|
193 (JNIEnv *env, jobject obj, jlong jpid) { |
|
194 pid_t pid = (pid_t) jpid; |
|
195 return (kill(pid, 0) < 0) ? JNI_FALSE : JNI_TRUE; |
|
196 } |
|
197 |
|
198 /* |
|
199 * Class: java_lang_ProcessHandleImpl |
|
200 * Method: destroy0 |
|
201 * Signature: (Z)Z |
|
202 */ |
|
203 JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_destroy0 |
|
204 (JNIEnv *env, jobject obj, jlong jpid, jboolean force) { |
|
205 pid_t pid = (pid_t) jpid; |
|
206 int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM; |
|
207 return (kill(pid, sig) >= 0); |
|
208 |
|
209 } |
|
210 |
|
211 /** |
|
212 * Size of password or group entry when not available via sysconf |
|
213 */ |
|
214 #define ENT_BUF_SIZE 1024 |
|
215 |
|
216 /** |
|
217 * Return a strong username for the uid_t or null. |
|
218 */ |
|
219 jstring uidToUser(JNIEnv* env, uid_t uid) { |
|
220 int result = 0; |
|
221 int buflen; |
|
222 char* pwbuf; |
|
223 jstring name = NULL; |
|
224 |
|
225 /* allocate buffer for password record */ |
|
226 buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX); |
|
227 if (buflen == -1) |
|
228 buflen = ENT_BUF_SIZE; |
|
229 pwbuf = (char*)malloc(buflen); |
|
230 if (pwbuf == NULL) { |
|
231 JNU_ThrowOutOfMemoryError(env, "Unable to open getpwent"); |
|
232 } else { |
|
233 struct passwd pwent; |
|
234 struct passwd* p = NULL; |
|
235 |
|
236 #ifdef __solaris__ |
|
237 RESTARTABLE_RETURN_PTR(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen), p); |
|
238 #else |
|
239 RESTARTABLE(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen, &p), result); |
|
240 #endif |
|
241 |
|
242 // Return the Java String if a name was found |
|
243 if (result == 0 && p != NULL && |
|
244 p->pw_name != NULL && *(p->pw_name) != '\0') { |
|
245 name = JNU_NewStringPlatform(env, p->pw_name); |
|
246 } |
|
247 free(pwbuf); |
|
248 } |
|
249 return name; |
|
250 } |
|
251 |
|
252 /** |
|
253 * Implementations of ProcessHandleImpl functions that are common to |
|
254 * (some) Unix variants: |
|
255 * - getProcessPids0(pid, pidArray, parentArray) |
|
256 */ |
|
257 |
|
258 #if defined(__linux__) || defined(__AIX__) |
|
259 |
|
260 /* |
|
261 * Signatures for internal OS specific functions. |
|
262 */ |
|
263 static pid_t parentPid(JNIEnv *env, pid_t pid); |
|
264 static jint getChildren(JNIEnv *env, jlong jpid, |
|
265 jlongArray array, jlongArray jparentArray); |
|
266 |
|
267 static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid); |
|
268 static void getCmdlineInfo(JNIEnv *env, pid_t pid, jobject jinfo); |
|
269 static long long getBoottime(JNIEnv *env); |
|
270 |
|
271 jstring uidToUser(JNIEnv* env, uid_t uid); |
|
272 |
|
273 /* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */ |
|
274 static jfieldID ProcessHandleImpl_Info_commandID; |
|
275 |
|
276 /* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */ |
|
277 static jfieldID ProcessHandleImpl_Info_argumentsID; |
|
278 |
|
279 /* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */ |
|
280 static jfieldID ProcessHandleImpl_Info_totalTimeID; |
|
281 |
|
282 /* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */ |
|
283 static jfieldID ProcessHandleImpl_Info_startTimeID; |
|
284 |
|
285 /* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */ |
|
286 static jfieldID ProcessHandleImpl_Info_userID; |
|
287 |
|
288 /* static value for clock ticks per second. */ |
|
289 static long clock_ticks_per_second; |
|
290 |
|
291 /* A static offset in milliseconds since boot. */ |
|
292 static long long bootTime_ms; |
|
293 |
|
294 /************************************************************** |
|
295 * Static method to initialize field IDs and the ticks per second rate. |
|
296 * |
|
297 * Class: java_lang_ProcessHandleImpl_Info |
|
298 * Method: initIDs |
|
299 * Signature: ()V |
|
300 */ |
|
301 JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs |
|
302 (JNIEnv *env, jclass clazz) { |
|
303 |
|
304 CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env, |
|
305 clazz, "command", "Ljava/lang/String;")); |
|
306 CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env, |
|
307 clazz, "arguments", "[Ljava/lang/String;")); |
|
308 CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env, |
|
309 clazz, "totalTime", "J")); |
|
310 CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env, |
|
311 clazz, "startTime", "J")); |
|
312 CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env, |
|
313 clazz, "user", "Ljava/lang/String;")); |
|
314 clock_ticks_per_second = sysconf(_SC_CLK_TCK); |
|
315 bootTime_ms = getBoottime(env); |
|
316 } |
|
317 |
|
318 /* |
|
319 * Returns the parent pid of the requested pid. |
|
320 * |
|
321 * Class: java_lang_ProcessHandleImpl |
|
322 * Method: parent0 |
|
323 * Signature: (J)J |
|
324 */ |
|
325 JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0 |
|
326 (JNIEnv *env, jobject obj, jlong jpid) { |
|
327 pid_t pid = (pid_t) jpid; |
|
328 pid_t ppid = -1; |
|
329 |
|
330 pid_t mypid = getpid(); |
|
331 if (pid == mypid) { |
|
332 ppid = getppid(); |
|
333 } else { |
|
334 ppid = parentPid(env, pid); |
|
335 } |
|
336 return (jlong) ppid; |
|
337 } |
|
338 |
|
339 /* |
|
340 * Returns the children of the requested pid and optionally each parent. |
|
341 * |
|
342 * Class: java_lang_ProcessHandleImpl |
|
343 * Method: getChildPids |
|
344 * Signature: (J[J[J)I |
|
345 */ |
|
346 JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0 |
|
347 (JNIEnv *env, jclass clazz, jlong jpid, |
|
348 jlongArray jarray, jlongArray jparentArray) { |
|
349 return getChildren(env, jpid, jarray, jparentArray); |
|
350 } |
|
351 |
|
352 /* |
|
353 * Reads /proc and accumulates any process who parent pid matches. |
|
354 * The resulting pids are stored into the array of longs. |
|
355 * The number of pids is returned if they all fit. |
|
356 * If the array is too short, the negative of the desired length is returned. |
|
357 */ |
|
358 static jint getChildren(JNIEnv *env, jlong jpid, |
|
359 jlongArray jarray, jlongArray jparentArray) { |
|
360 DIR* dir; |
|
361 struct dirent* ptr; |
|
362 pid_t pid = (pid_t) jpid; |
|
363 pid_t ppid = 0; |
|
364 size_t count = 0; |
|
365 jlong* pids = NULL; |
|
366 jlong* ppids = NULL; |
|
367 size_t parentArraySize = 0; |
|
368 size_t arraySize = 0; |
|
369 |
|
370 arraySize = (*env)->GetArrayLength(env, jarray); |
|
371 JNU_CHECK_EXCEPTION_RETURN(env, -1); |
|
372 if (jparentArray != NULL) { |
|
373 parentArraySize = (*env)->GetArrayLength(env, jparentArray); |
|
374 JNU_CHECK_EXCEPTION_RETURN(env, -1); |
|
375 |
|
376 if (arraySize != parentArraySize) { |
|
377 JNU_ThrowIllegalArgumentException(env, "array sizes not equal"); |
|
378 return 0; |
|
379 } |
|
380 } |
|
381 |
|
382 /* |
|
383 * To locate the children we scan /proc looking for files that have a |
|
384 * position integer as a filename. |
|
385 */ |
|
386 if ((dir = opendir("/proc")) == NULL) { |
|
387 JNU_ThrowByNameWithLastError(env, |
|
388 "java/lang/Runtime", "Unable to open /proc"); |
|
389 return -1; |
|
390 } |
|
391 |
|
392 do { // Block to break out of on Exception |
|
393 pids = (*env)->GetLongArrayElements(env, jarray, NULL); |
|
394 if (pids == NULL) { |
|
395 break; |
|
396 } |
|
397 if (jparentArray != NULL) { |
|
398 ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL); |
|
399 if (ppids == NULL) { |
|
400 break; |
|
401 } |
|
402 } |
|
403 |
|
404 while ((ptr = readdir(dir)) != NULL) { |
|
405 /* skip files that aren't numbers */ |
|
406 pid_t childpid = (pid_t) atoi(ptr->d_name); |
|
407 if ((int) childpid <= 0) { |
|
408 continue; |
|
409 } |
|
410 |
|
411 ppid = 0; |
|
412 if (pid != 0 || jparentArray != NULL) { |
|
413 // parentPid opens and reads /proc/pid/stat |
|
414 ppid = parentPid(env, childpid); |
|
415 } |
|
416 if (pid == 0 || ppid == pid) { |
|
417 if (count < arraySize) { |
|
418 // Only store if it fits |
|
419 pids[count] = (jlong) childpid; |
|
420 |
|
421 if (ppids != NULL) { |
|
422 // Store the parentPid |
|
423 ppids[count] = (jlong) ppid; |
|
424 } |
|
425 } |
|
426 count++; // Count to tabulate size needed |
|
427 } |
|
428 } |
|
429 } while (0); |
|
430 |
|
431 if (pids != NULL) { |
|
432 (*env)->ReleaseLongArrayElements(env, jarray, pids, 0); |
|
433 } |
|
434 if (ppids != NULL) { |
|
435 (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0); |
|
436 } |
|
437 |
|
438 closedir(dir); |
|
439 // If more pids than array had size for; count will be greater than array size |
|
440 return count; |
|
441 } |
|
442 |
|
443 /* |
|
444 * Returns the parent pid of a given pid, or -1 if not found |
|
445 */ |
|
446 static pid_t parentPid(JNIEnv *env, pid_t pid) { |
|
447 char state; |
|
448 FILE* fp; |
|
449 char stat[2048]; |
|
450 int statlen; |
|
451 char fn[32]; |
|
452 int i, p; |
|
453 char* s; |
|
454 |
|
455 /* |
|
456 * try to open /proc/%d/stat |
|
457 */ |
|
458 snprintf(fn, sizeof fn, "/proc/%d/stat", pid); |
|
459 fp = fopen(fn, "r"); |
|
460 if (fp == NULL) { |
|
461 return -1; |
|
462 } |
|
463 |
|
464 /* |
|
465 * The format is: pid (command) state ppid ... |
|
466 * As the command could be anything we must find the right most |
|
467 * ")" and then skip the white spaces that follow it. |
|
468 */ |
|
469 statlen = fread(stat, 1, (sizeof stat - 1), fp); |
|
470 fclose(fp); |
|
471 if (statlen < 0) { |
|
472 return -1; |
|
473 } |
|
474 |
|
475 stat[statlen] = '\0'; |
|
476 s = strrchr(stat, ')'); |
|
477 if (s == NULL) { |
|
478 return -1; |
|
479 } |
|
480 do s++; while (isspace(*s)); |
|
481 i = sscanf(s, "%c %d", &state, &p); |
|
482 if (i != 2) { |
|
483 return (pid_t)-1; |
|
484 } |
|
485 return (pid_t) p; |
|
486 } |
|
487 |
|
488 /************************************************************** |
|
489 * Implementation of ProcessHandleImpl_Info native methods. |
|
490 */ |
|
491 |
|
492 /* |
|
493 * Fill in the Info object from the OS information about the process. |
|
494 * |
|
495 * Class: java_lang_ProcessHandleImpl_Info |
|
496 * Method: info0 |
|
497 * Signature: (JLjava/lang/ProcessHandle/Info;)I |
|
498 */ |
|
499 JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0 |
|
500 (JNIEnv *env, jobject jinfo, jlong jpid) { |
|
501 pid_t pid = (pid_t) jpid; |
|
502 getStatInfo(env, jinfo, (pid_t)pid); |
|
503 getCmdlineInfo(env, pid, jinfo); |
|
504 } |
|
505 |
|
506 /** |
|
507 * Read /proc/<pid>/stat and fill in the fields of the Info object. |
|
508 * The executable name, plus the user, system, and start times are gathered. |
|
509 */ |
|
510 static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid) { |
|
511 char state; |
|
512 FILE* fp; |
|
513 char buffer[2048]; |
|
514 struct stat stat_buf; |
|
515 int statlen; |
|
516 char fn[32]; |
|
517 int i, ppid = -2; |
|
518 char* s; |
|
519 char *cmd; |
|
520 jstring name = NULL; |
|
521 unsigned long userTime = 0; // clock tics |
|
522 unsigned long totalTime = 0; // clock tics |
|
523 jlong total = 0; // nano seconds |
|
524 unsigned long long startTime = 0; // microseconds |
|
525 |
|
526 /* |
|
527 * Try to stat and then open /proc/%d/stat |
|
528 */ |
|
529 snprintf(fn, sizeof fn, "/proc/%d/stat", pid); |
|
530 |
|
531 if (stat(fn, &stat_buf) < 0) { |
|
532 return; |
|
533 } |
|
534 |
|
535 CHECK_NULL((name = uidToUser(env, stat_buf.st_uid))); |
|
536 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name); |
|
537 JNU_CHECK_EXCEPTION(env); |
|
538 |
|
539 fp = fopen(fn, "r"); |
|
540 if (fp == NULL) { |
|
541 return; |
|
542 } |
|
543 |
|
544 /* |
|
545 * The format is: pid (command) state ppid ... |
|
546 * As the command could be anything we must find the right most |
|
547 * ")" and then skip the white spaces that follow it. |
|
548 */ |
|
549 statlen = fread(buffer, 1, (sizeof buffer - 1), fp); |
|
550 fclose(fp); |
|
551 if (statlen < 0) { |
|
552 return; |
|
553 } |
|
554 |
|
555 buffer[statlen] = '\0'; |
|
556 s = strchr(buffer, '('); |
|
557 if (s == NULL) { |
|
558 return; |
|
559 } |
|
560 // Found start of command, skip to end |
|
561 s++; |
|
562 s = strrchr(s, ')'); |
|
563 if (s == NULL) { |
|
564 return; |
|
565 } |
|
566 s++; |
|
567 |
|
568 // Scan the needed fields from status, retaining only ppid(4), |
|
569 // utime (14), stime(15), starttime(22) |
|
570 i = sscanf(s, " %c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu", |
|
571 &state, &ppid, &userTime, &totalTime, &startTime); |
|
572 if (i != 5) { |
|
573 return; // not all values parsed; return error |
|
574 } |
|
575 |
|
576 total = (userTime + totalTime) * (jlong)(1000000000 / clock_ticks_per_second); |
|
577 |
|
578 startTime = bootTime_ms + ((startTime * 1000) / clock_ticks_per_second); |
|
579 |
|
580 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, total); |
|
581 JNU_CHECK_EXCEPTION(env); |
|
582 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime); |
|
583 JNU_CHECK_EXCEPTION(env); |
|
584 } |
|
585 |
|
586 /** |
|
587 * Construct the argument array by parsing the arguments from the sequence |
|
588 * of arguments. The zero'th arg is the command executable |
|
589 */ |
|
590 static int fillArgArray(JNIEnv *env, jobject jinfo, |
|
591 int nargs, char *cp, char *argsEnd, jstring cmdexe) { |
|
592 jobject argsArray; |
|
593 int i; |
|
594 |
|
595 if (nargs < 1) { |
|
596 return 0; |
|
597 } |
|
598 |
|
599 if (cmdexe == NULL) { |
|
600 // Create a string from arg[0] |
|
601 CHECK_NULL_RETURN((cmdexe = JNU_NewStringPlatform(env, cp)), -1); |
|
602 } |
|
603 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, cmdexe); |
|
604 JNU_CHECK_EXCEPTION_RETURN(env, -3); |
|
605 |
|
606 // Create a String array for nargs-1 elements |
|
607 argsArray = (*env)->NewObjectArray(env, nargs - 1, JNU_ClassString(env), NULL); |
|
608 CHECK_NULL_RETURN(argsArray, -1); |
|
609 |
|
610 for (i = 0; i < nargs - 1; i++) { |
|
611 jstring str = NULL; |
|
612 |
|
613 cp += strnlen(cp, (argsEnd - cp)) + 1; |
|
614 if (cp > argsEnd || *cp == '\0') { |
|
615 return -2; // Off the end pointer or an empty argument is an error |
|
616 } |
|
617 |
|
618 CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1); |
|
619 |
|
620 (*env)->SetObjectArrayElement(env, argsArray, i, str); |
|
621 JNU_CHECK_EXCEPTION_RETURN(env, -3); |
|
622 } |
|
623 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray); |
|
624 JNU_CHECK_EXCEPTION_RETURN(env, -4); |
|
625 return 0; |
|
626 } |
|
627 |
|
628 |
|
629 static void getCmdlineInfo(JNIEnv *env, pid_t pid, jobject jinfo) { |
|
630 int fd; |
|
631 int cmdlen = 0; |
|
632 char *cmdline = NULL, *cmdEnd; // used for command line args and exe |
|
633 jstring cmdexe = NULL; |
|
634 char fn[32]; |
|
635 |
|
636 /* |
|
637 * Try to open /proc/%d/cmdline |
|
638 */ |
|
639 snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid); |
|
640 if ((fd = open(fn, O_RDONLY)) < 0) { |
|
641 return; |
|
642 } |
|
643 |
|
644 do { // Block to break out of on errors |
|
645 int i; |
|
646 char *s; |
|
647 |
|
648 cmdline = (char*)malloc(PATH_MAX); |
|
649 if (cmdline == NULL) { |
|
650 break; |
|
651 } |
|
652 |
|
653 /* |
|
654 * The path to the executable command is the link in /proc/<pid>/exe. |
|
655 */ |
|
656 snprintf(fn, sizeof fn, "/proc/%d/exe", pid); |
|
657 if ((cmdlen = readlink(fn, cmdline, PATH_MAX - 1)) > 0) { |
|
658 // null terminate and create String to store for command |
|
659 cmdline[cmdlen] = '\0'; |
|
660 cmdexe = JNU_NewStringPlatform(env, cmdline); |
|
661 (*env)->ExceptionClear(env); // unconditionally clear any exception |
|
662 } |
|
663 |
|
664 /* |
|
665 * The buffer format is the arguments nul terminated with an extra nul. |
|
666 */ |
|
667 cmdlen = read(fd, cmdline, PATH_MAX-1); |
|
668 if (cmdlen < 0) { |
|
669 break; |
|
670 } |
|
671 |
|
672 // Terminate the buffer and count the arguments |
|
673 cmdline[cmdlen] = '\0'; |
|
674 cmdEnd = &cmdline[cmdlen + 1]; |
|
675 for (s = cmdline,i = 0; *s != '\0' && (s < cmdEnd); i++) { |
|
676 s += strnlen(s, (cmdEnd - s)) + 1; |
|
677 } |
|
678 |
|
679 if (fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe) < 0) { |
|
680 break; |
|
681 } |
|
682 } while (0); |
|
683 |
|
684 if (cmdline != NULL) { |
|
685 free(cmdline); |
|
686 } |
|
687 if (fd >= 0) { |
|
688 close(fd); |
|
689 } |
|
690 } |
|
691 |
|
692 /** |
|
693 * Read the boottime from /proc/stat. |
|
694 */ |
|
695 static long long getBoottime(JNIEnv *env) { |
|
696 FILE *fp; |
|
697 char *line = NULL; |
|
698 size_t len = 0; |
|
699 long long bootTime = 0; |
|
700 |
|
701 fp = fopen("/proc/stat", "r"); |
|
702 if (fp == NULL) { |
|
703 return -1; |
|
704 } |
|
705 |
|
706 while (getline(&line, &len, fp) != -1) { |
|
707 if (sscanf(line, "btime %llu", &bootTime) == 1) { |
|
708 break; |
|
709 } |
|
710 } |
|
711 free(line); |
|
712 |
|
713 if (fp != 0) { |
|
714 fclose(fp); |
|
715 } |
|
716 |
|
717 return bootTime * 1000; |
|
718 } |
|
719 |
|
720 #endif // defined(__linux__) || defined(__AIX__) |
|
721 |
|
722 |
|
723 /* Block until a child process exits and return its exit code. |
|
724 Note, can only be called once for any given pid. */ |
|
725 JNIEXPORT jint JNICALL |
|
726 Java_java_lang_ProcessImpl_waitForProcessExit(JNIEnv* env, |
|
727 jobject junk, |
|
728 jint pid) |
|
729 { |
|
730 /* We used to use waitid() on Solaris, waitpid() on Linux, but |
|
731 * waitpid() is more standard, so use it on all POSIX platforms. */ |
|
732 int status; |
|
733 /* Wait for the child process to exit. This returns immediately if |
|
734 the child has already exited. */ |
|
735 while (waitpid(pid, &status, 0) < 0) { |
|
736 switch (errno) { |
|
737 case ECHILD: return 0; |
|
738 case EINTR: break; |
|
739 default: return -1; |
|
740 } |
|
741 } |
|
742 |
|
743 if (WIFEXITED(status)) { |
|
744 /* |
|
745 * The child exited normally; get its exit code. |
|
746 */ |
|
747 return WEXITSTATUS(status); |
|
748 } else if (WIFSIGNALED(status)) { |
|
749 /* The child exited because of a signal. |
|
750 * The best value to return is 0x80 + signal number, |
|
751 * because that is what all Unix shells do, and because |
|
752 * it allows callers to distinguish between process exit and |
|
753 * process death by signal. |
|
754 * Unfortunately, the historical behavior on Solaris is to return |
|
755 * the signal number, and we preserve this for compatibility. */ |
|
756 #ifdef __solaris__ |
|
757 return WTERMSIG(status); |
|
758 #else |
|
759 return 0x80 + WTERMSIG(status); |
|
760 #endif |
|
761 } else { |
|
762 /* |
|
763 * Unknown exit code; pass it through. |
|
764 */ |
|
765 return status; |
|
766 } |
|
767 } |
|
768 |
|
769 |