26 #include "jni.h" |
26 #include "jni.h" |
27 #include "jni_util.h" |
27 #include "jni_util.h" |
28 #include "java_lang_ProcessHandleImpl.h" |
28 #include "java_lang_ProcessHandleImpl.h" |
29 #include "java_lang_ProcessHandleImpl_Info.h" |
29 #include "java_lang_ProcessHandleImpl_Info.h" |
30 |
30 |
|
31 #include "ProcessHandleImpl_unix.h" |
|
32 |
31 |
33 |
32 #include <stdio.h> |
34 #include <stdio.h> |
33 |
|
34 #include <errno.h> |
35 #include <errno.h> |
35 #include <fcntl.h> |
36 #include <fcntl.h> |
36 #include <pwd.h> |
37 #include <pwd.h> |
37 #include <signal.h> |
38 #include <signal.h> |
38 #include <stdlib.h> |
39 #include <stdlib.h> |
39 #include <unistd.h> |
40 #include <unistd.h> |
|
41 #include <string.h> |
|
42 #include <dirent.h> |
|
43 #include <ctype.h> |
|
44 #include <limits.h> |
40 #include <sys/types.h> |
45 #include <sys/types.h> |
41 #include <sys/stat.h> |
46 #include <sys/stat.h> |
42 #include <sys/wait.h> |
47 #include <sys/wait.h> |
43 |
48 |
44 #include <string.h> |
49 #ifdef _AIX |
45 #include <dirent.h> |
50 #include <sys/procfs.h> |
46 #include <ctype.h> |
51 #endif |
|
52 #ifdef __solaris__ |
|
53 #include <procfs.h> |
|
54 #endif |
47 |
55 |
48 /** |
56 /** |
49 * Implementations of ProcessHandleImpl functions that are common to all |
57 * This file contains the implementation of the native ProcessHandleImpl |
50 * Unix variants: |
58 * functions which are common to all Unix variants. |
51 * - waitForProcessExit0(pid, reap) |
59 * |
52 * - getCurrentPid0() |
60 * The currently supported Unix variants are Solaris, Linux, MaxOS X and AIX. |
53 * - destroy0(pid, force) |
61 * The various similarities and differences between these systems make it hard |
54 */ |
62 * to find a clear boundary between platform specific and shared code. |
|
63 * |
|
64 * In order to ease code sharing between the platforms while still keeping the |
|
65 * code as clean as possible (i.e. free of preprocessor macros) we use the |
|
66 * following source code layout (remember that ProcessHandleImpl_unix.c will |
|
67 * be compiled on EVERY Unix platform while ProcessHandleImpl_<os>.c will be |
|
68 * only compiled on the specific OS): |
|
69 * |
|
70 * - all the JNI wrappers for the ProcessHandleImpl functions go into this file |
|
71 * - if their implementation is common on ALL the supported Unix platforms it |
|
72 * goes right into the JNI wrappers |
|
73 * - if the whole function or substantial parts of it are platform dependent, |
|
74 * the implementation goes into os_<function_name> functions in |
|
75 * ProcessHandleImpl_<os>.c |
|
76 * - if at least two platforms implement an os_<function_name> function in the |
|
77 * same way, this implementation is factored out into unix_<function_name>, |
|
78 * placed into this file and called from the corresponding os_<function_name> |
|
79 * function. |
|
80 * - For convenience, all the os_ and unix_ functions are declared in |
|
81 * ProcessHandleImpl_unix.h which is included into every |
|
82 * ProcessHandleImpl_<os>.c file. |
|
83 * |
|
84 * Example 1: |
|
85 * ---------- |
|
86 * The implementation of Java_java_lang_ProcessHandleImpl_initNative() |
|
87 * is the same on all platforms except on Linux where it initilizes one |
|
88 * additional field. So we place the implementation right into |
|
89 * Java_java_lang_ProcessHandleImpl_initNative() but add call to |
|
90 * os_init() at the end of the function which is empty on all platforms |
|
91 * except Linux where it performs the additionally initializations. |
|
92 * |
|
93 * Example 2: |
|
94 * ---------- |
|
95 * The implementation of Java_java_lang_ProcessHandleImpl_00024Info_info0 is the |
|
96 * same on Solaris and AIX but different on Linux and MacOSX. We therefore simply |
|
97 * call the helpers os_getParentPidAndTimings() and os_getCmdlineAndUserInfo(). |
|
98 * The Linux and MaxOS X versions of these functions (in the corresponding files |
|
99 * ProcessHandleImpl_linux.c and ProcessHandleImpl_macosx.c) directly contain |
|
100 * the platform specific implementations while the Solaris and AIX |
|
101 * implementations simply call back to unix_getParentPidAndTimings() and |
|
102 * unix_getCmdlineAndUserInfo() which are implemented right in this file. |
|
103 * |
|
104 * The term "same implementation" is still a question of interpretation. It my |
|
105 * be acceptable to have a few ifdef'ed lines if that allows the sharing of a |
|
106 * huge function. On the other hand, if the platform specific code in a shared |
|
107 * function grows over a certain limit, it may be better to refactor that |
|
108 * functionality into corresponding, platform-specific os_ functions. |
|
109 */ |
|
110 |
55 |
111 |
56 #ifndef WIFEXITED |
112 #ifndef WIFEXITED |
57 #define WIFEXITED(status) (((status)&0xFF) == 0) |
113 #define WIFEXITED(status) (((status)&0xFF) == 0) |
58 #endif |
114 #endif |
59 |
115 |
79 do { \ |
148 do { \ |
80 _result = _cmd; \ |
149 _result = _cmd; \ |
81 } while((_result == NULL) && (errno == EINTR)); \ |
150 } while((_result == NULL) && (errno == EINTR)); \ |
82 } while(0) |
151 } while(0) |
83 |
152 |
84 #ifdef __solaris__ |
153 |
85 #define STAT_FILE "/proc/%d/status" |
154 /* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */ |
86 #else |
155 jfieldID ProcessHandleImpl_Info_commandID; |
87 #define STAT_FILE "/proc/%d/stat" |
156 |
88 #endif |
157 /* Field id for jString 'commandLine' in java.lang.ProcessHandleImpl.Info */ |
|
158 jfieldID ProcessHandleImpl_Info_commandLineID; |
|
159 |
|
160 /* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */ |
|
161 jfieldID ProcessHandleImpl_Info_argumentsID; |
|
162 |
|
163 /* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */ |
|
164 jfieldID ProcessHandleImpl_Info_totalTimeID; |
|
165 |
|
166 /* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */ |
|
167 jfieldID ProcessHandleImpl_Info_startTimeID; |
|
168 |
|
169 /* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */ |
|
170 jfieldID ProcessHandleImpl_Info_userID; |
|
171 |
|
172 /* Size of password or group entry when not available via sysconf */ |
|
173 #define ENT_BUF_SIZE 1024 |
|
174 /* The value for the size of the buffer used by getpwuid_r(). The result of */ |
|
175 /* sysconf(_SC_GETPW_R_SIZE_MAX) if available or ENT_BUF_SIZE otherwise. */ |
|
176 static long getpw_buf_size; |
|
177 |
|
178 /************************************************************** |
|
179 * Static method to initialize field IDs and the ticks per second rate. |
|
180 * |
|
181 * Class: java_lang_ProcessHandleImpl_Info |
|
182 * Method: initIDs |
|
183 * Signature: ()V |
|
184 */ |
|
185 JNIEXPORT void JNICALL |
|
186 Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) { |
|
187 |
|
188 CHECK_NULL(ProcessHandleImpl_Info_commandID = |
|
189 (*env)->GetFieldID(env, clazz, "command", "Ljava/lang/String;")); |
|
190 CHECK_NULL(ProcessHandleImpl_Info_commandLineID = |
|
191 (*env)->GetFieldID(env, clazz, "commandLine", "Ljava/lang/String;")); |
|
192 CHECK_NULL(ProcessHandleImpl_Info_argumentsID = |
|
193 (*env)->GetFieldID(env, clazz, "arguments", "[Ljava/lang/String;")); |
|
194 CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = |
|
195 (*env)->GetFieldID(env, clazz, "totalTime", "J")); |
|
196 CHECK_NULL(ProcessHandleImpl_Info_startTimeID = |
|
197 (*env)->GetFieldID(env, clazz, "startTime", "J")); |
|
198 CHECK_NULL(ProcessHandleImpl_Info_userID = |
|
199 (*env)->GetFieldID(env, clazz, "user", "Ljava/lang/String;")); |
|
200 } |
|
201 |
|
202 /*********************************************************** |
|
203 * Static method to initialize platform dependent constants. |
|
204 * |
|
205 * Class: java_lang_ProcessHandleImpl |
|
206 * Method: initNative |
|
207 * Signature: ()V |
|
208 */ |
|
209 JNIEXPORT void JNICALL |
|
210 Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) { |
|
211 getpw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX); |
|
212 if (getpw_buf_size == -1) { |
|
213 getpw_buf_size = ENT_BUF_SIZE; |
|
214 } |
|
215 os_initNative(env, clazz); |
|
216 } |
89 |
217 |
90 /* Block until a child process exits and return its exit code. |
218 /* Block until a child process exits and return its exit code. |
91 * Note, can only be called once for any given pid if reapStatus = true. |
219 * Note, can only be called once for any given pid if reapStatus = true. |
|
220 * |
|
221 * Class: java_lang_ProcessHandleImpl |
|
222 * Method: waitForProcessExit0 |
|
223 * Signature: (JZ)I |
92 */ |
224 */ |
93 JNIEXPORT jint JNICALL |
225 JNIEXPORT jint JNICALL |
94 Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env, |
226 Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env, |
95 jclass junk, |
227 jclass junk, |
96 jlong jpid, |
228 jlong jpid, |
97 jboolean reapStatus) |
229 jboolean reapStatus) { |
98 { |
|
99 pid_t pid = (pid_t)jpid; |
230 pid_t pid = (pid_t)jpid; |
100 errno = 0; |
231 errno = 0; |
101 |
232 |
102 if (reapStatus != JNI_FALSE) { |
233 if (reapStatus != JNI_FALSE) { |
103 /* Wait for the child process to exit. |
234 /* Wait for the child process to exit. |
208 } else { |
317 } else { |
209 return JNI_FALSE; |
318 return JNI_FALSE; |
210 } |
319 } |
211 } |
320 } |
212 |
321 |
213 /** |
322 /* |
214 * Size of password or group entry when not available via sysconf |
323 * Returns the children of the requested pid and optionally each parent and |
215 */ |
324 * start time. |
216 #define ENT_BUF_SIZE 1024 |
325 * Accumulates any process who parent pid matches. |
217 |
326 * The resulting pids are stored into the array of longs. |
218 /** |
327 * The number of pids is returned if they all fit. |
219 * Return a strong username for the uid_t or null. |
328 * If the array is too short, the negative of the desired length is returned. |
220 */ |
329 * Class: java_lang_ProcessHandleImpl |
221 jstring uidToUser(JNIEnv* env, uid_t uid) { |
330 * Method: getProcessPids0 |
222 int result = 0; |
331 * Signature: (J[J[J[J)I |
223 int buflen; |
332 */ |
224 char* pwbuf; |
333 JNIEXPORT jint JNICALL |
225 jstring name = NULL; |
334 Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env, |
226 |
335 jclass clazz, |
227 /* allocate buffer for password record */ |
336 jlong jpid, |
228 buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX); |
337 jlongArray jarray, |
229 if (buflen == -1) |
338 jlongArray jparentArray, |
230 buflen = ENT_BUF_SIZE; |
339 jlongArray jstimesArray) { |
231 pwbuf = (char*)malloc(buflen); |
340 return os_getChildren(env, jpid, jarray, jparentArray, jstimesArray); |
232 if (pwbuf == NULL) { |
341 } |
233 JNU_ThrowOutOfMemoryError(env, "Unable to open getpwent"); |
342 |
234 } else { |
343 /* |
235 struct passwd pwent; |
344 * Fill in the Info object from the OS information about the process. |
236 struct passwd* p = NULL; |
|
237 |
|
238 #ifdef __solaris__ |
|
239 RESTARTABLE_RETURN_PTR(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen), p); |
|
240 #else |
|
241 RESTARTABLE(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen, &p), result); |
|
242 #endif |
|
243 |
|
244 // Return the Java String if a name was found |
|
245 if (result == 0 && p != NULL && |
|
246 p->pw_name != NULL && *(p->pw_name) != '\0') { |
|
247 name = JNU_NewStringPlatform(env, p->pw_name); |
|
248 } |
|
249 free(pwbuf); |
|
250 } |
|
251 return name; |
|
252 } |
|
253 |
|
254 /** |
|
255 * Implementations of ProcessHandleImpl functions that are common to |
|
256 * (some) Unix variants: |
|
257 * - getProcessPids0(pid, pidArray, parentArray) |
|
258 */ |
|
259 |
|
260 #if defined(__linux__) || defined(__AIX__) |
|
261 |
|
262 /* |
|
263 * Signatures for internal OS specific functions. |
|
264 */ |
|
265 static pid_t getStatInfo(JNIEnv *env, pid_t pid, |
|
266 jlong *totalTime, jlong* startTime); |
|
267 static void getCmdlineInfo(JNIEnv *env, pid_t pid, jobject jinfo); |
|
268 static long long getBoottime(JNIEnv *env); |
|
269 |
|
270 jstring uidToUser(JNIEnv* env, uid_t uid); |
|
271 |
|
272 /* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */ |
|
273 static jfieldID ProcessHandleImpl_Info_commandID; |
|
274 |
|
275 /* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */ |
|
276 static jfieldID ProcessHandleImpl_Info_argumentsID; |
|
277 |
|
278 /* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */ |
|
279 static jfieldID ProcessHandleImpl_Info_totalTimeID; |
|
280 |
|
281 /* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */ |
|
282 static jfieldID ProcessHandleImpl_Info_startTimeID; |
|
283 |
|
284 /* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */ |
|
285 static jfieldID ProcessHandleImpl_Info_userID; |
|
286 |
|
287 /* static value for clock ticks per second. */ |
|
288 static long clock_ticks_per_second; |
|
289 |
|
290 /* A static offset in milliseconds since boot. */ |
|
291 static long long bootTime_ms; |
|
292 |
|
293 /************************************************************** |
|
294 * Static method to initialize field IDs and the ticks per second rate. |
|
295 * |
345 * |
296 * Class: java_lang_ProcessHandleImpl_Info |
346 * Class: java_lang_ProcessHandleImpl_Info |
297 * Method: initIDs |
347 * Method: info0 |
298 * Signature: ()V |
348 * Signature: (Ljava/lang/ProcessHandle/Info;J)I |
299 */ |
349 */ |
300 JNIEXPORT void JNICALL |
350 JNIEXPORT void JNICALL |
301 Java_java_lang_ProcessHandleImpl_00024Info_initIDs(JNIEnv *env, jclass clazz) { |
351 Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env, |
302 |
352 jobject jinfo, |
303 CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env, |
353 jlong jpid) { |
304 clazz, "command", "Ljava/lang/String;")); |
354 pid_t pid = (pid_t) jpid; |
305 CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env, |
355 pid_t ppid; |
306 clazz, "arguments", "[Ljava/lang/String;")); |
356 jlong totalTime = -1L; |
307 CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env, |
357 jlong startTime = -1L; |
308 clazz, "totalTime", "J")); |
358 |
309 CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env, |
359 ppid = os_getParentPidAndTimings(env, pid, &totalTime, &startTime); |
310 clazz, "startTime", "J")); |
360 if (ppid >= 0) { |
311 CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env, |
361 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime); |
312 clazz, "user", "Ljava/lang/String;")); |
362 JNU_CHECK_EXCEPTION(env); |
313 } |
363 |
314 |
364 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime); |
315 /************************************************************** |
365 JNU_CHECK_EXCEPTION(env); |
316 * Static method to initialize the ticks per second rate. |
366 } |
317 * |
367 os_getCmdlineAndUserInfo(env, jinfo, pid); |
318 * Class: java_lang_ProcessHandleImpl |
|
319 * Method: initNative |
|
320 * Signature: ()V |
|
321 */ |
|
322 JNIEXPORT void JNICALL |
|
323 Java_java_lang_ProcessHandleImpl_initNative(JNIEnv *env, jclass clazz) { |
|
324 clock_ticks_per_second = sysconf(_SC_CLK_TCK); |
|
325 bootTime_ms = getBoottime(env); |
|
326 } |
368 } |
327 |
369 |
328 /* |
370 /* |
329 * Check if a process is alive. |
371 * Check if a process is alive. |
330 * Return the start time (ms since 1970) if it is available. |
372 * Return the start time (ms since 1970) if it is available. |
338 JNIEXPORT jlong JNICALL |
380 JNIEXPORT jlong JNICALL |
339 Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jobject obj, jlong jpid) { |
381 Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jobject obj, jlong jpid) { |
340 pid_t pid = (pid_t) jpid; |
382 pid_t pid = (pid_t) jpid; |
341 jlong startTime = 0L; |
383 jlong startTime = 0L; |
342 jlong totalTime = 0L; |
384 jlong totalTime = 0L; |
343 pid_t ppid = getStatInfo(env, pid, &totalTime, &startTime); |
385 pid_t ppid = os_getParentPidAndTimings(env, pid, &totalTime, &startTime); |
344 return (ppid <= 0) ? -1 : startTime; |
386 return (ppid < 0) ? -1 : startTime; |
345 } |
387 } |
346 |
388 |
347 /* |
389 /* |
348 * Returns the parent pid of the requested pid. |
390 * Returns the parent pid of the requested pid. |
349 * The start time of the process must match (or be ANY). |
391 * The start time of the process must match (or be ANY). |
350 * |
392 * |
351 * Class: java_lang_ProcessHandleImpl |
393 * Class: java_lang_ProcessHandleImpl |
352 * Method: parent0 |
394 * Method: parent0 |
353 * Signature: (J)J |
395 * Signature: (JJ)J |
354 */ |
396 */ |
355 JNIEXPORT jlong JNICALL |
397 JNIEXPORT jlong JNICALL |
356 Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env, |
398 Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env, |
357 jobject obj, |
399 jobject obj, |
358 jlong jpid, |
400 jlong jpid, |
359 jlong startTime) { |
401 jlong startTime) { |
360 pid_t pid = (pid_t) jpid; |
402 pid_t pid = (pid_t) jpid; |
361 pid_t ppid; |
403 pid_t ppid; |
362 |
404 |
363 pid_t mypid = getpid(); |
405 if (pid == getpid()) { |
364 if (pid == mypid) { |
|
365 ppid = getppid(); |
406 ppid = getppid(); |
366 } else { |
407 } else { |
367 jlong start = 0L;; |
408 jlong start = 0L; |
368 jlong total = 0L; // unused |
409 jlong total = 0L; // unused |
369 ppid = getStatInfo(env, pid, &total, &start); |
410 ppid = os_getParentPidAndTimings(env, pid, &total, &start); |
370 if (start != startTime && start != 0 && startTime != 0) { |
411 if (start != startTime && start != 0 && startTime != 0) { |
371 ppid = -1; |
412 ppid = -1; |
372 } |
413 } |
373 } |
414 } |
374 return (jlong) ppid; |
415 return (jlong) ppid; |
375 } |
416 } |
376 |
417 |
377 /* |
418 /** |
378 * Returns the children of the requested pid and optionally each parent. |
419 * Construct the argument array by parsing the arguments from the sequence |
|
420 * of arguments. |
|
421 */ |
|
422 void unix_fillArgArray(JNIEnv *env, jobject jinfo, int nargs, char *cp, |
|
423 char *argsEnd, jstring cmdexe, char *cmdline) { |
|
424 jobject argsArray; |
|
425 int i; |
|
426 |
|
427 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, cmdexe); |
|
428 JNU_CHECK_EXCEPTION(env); |
|
429 |
|
430 if (nargs >= 1) { |
|
431 // Create a String array for nargs-1 elements |
|
432 argsArray = (*env)->NewObjectArray(env, nargs - 1, JNU_ClassString(env), NULL); |
|
433 CHECK_NULL(argsArray); |
|
434 |
|
435 for (i = 0; i < nargs - 1; i++) { |
|
436 jstring str = NULL; |
|
437 |
|
438 cp += strlen(cp) + 1; |
|
439 if (cp > argsEnd || *cp == '\0') { |
|
440 return; // Off the end pointer or an empty argument is an error |
|
441 } |
|
442 |
|
443 CHECK_NULL((str = JNU_NewStringPlatform(env, cp))); |
|
444 |
|
445 (*env)->SetObjectArrayElement(env, argsArray, i, str); |
|
446 JNU_CHECK_EXCEPTION(env); |
|
447 } |
|
448 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray); |
|
449 JNU_CHECK_EXCEPTION(env); |
|
450 } |
|
451 if (cmdline != NULL) { |
|
452 jstring commandLine = NULL; |
|
453 CHECK_NULL((commandLine = JNU_NewStringPlatform(env, cmdline))); |
|
454 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandLineID, commandLine); |
|
455 JNU_CHECK_EXCEPTION(env); |
|
456 } |
|
457 } |
|
458 |
|
459 void unix_getUserInfo(JNIEnv* env, jobject jinfo, uid_t uid) { |
|
460 int result = 0; |
|
461 char* pwbuf; |
|
462 jstring name = NULL; |
|
463 |
|
464 /* allocate buffer for password record */ |
|
465 pwbuf = (char*)malloc(getpw_buf_size); |
|
466 if (pwbuf == NULL) { |
|
467 JNU_ThrowOutOfMemoryError(env, "Unable to open getpwent"); |
|
468 } else { |
|
469 struct passwd pwent; |
|
470 struct passwd* p = NULL; |
|
471 |
|
472 #ifdef __solaris__ |
|
473 RESTARTABLE_RETURN_PTR(getpwuid_r(uid, &pwent, pwbuf, (size_t)getpw_buf_size), p); |
|
474 #else |
|
475 RESTARTABLE(getpwuid_r(uid, &pwent, pwbuf, (size_t)getpw_buf_size, &p), result); |
|
476 #endif |
|
477 |
|
478 // Create the Java String if a name was found |
|
479 if (result == 0 && p != NULL && |
|
480 p->pw_name != NULL && *(p->pw_name) != '\0') { |
|
481 name = JNU_NewStringPlatform(env, p->pw_name); |
|
482 } |
|
483 free(pwbuf); |
|
484 } |
|
485 if (name != NULL) { |
|
486 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name); |
|
487 } |
|
488 } |
|
489 |
|
490 /* |
|
491 * The following functions are common on Solaris, Linux and AIX. |
|
492 */ |
|
493 |
|
494 #if defined(__solaris__) || defined (__linux__) || defined(_AIX) |
|
495 |
|
496 /* |
|
497 * Returns the children of the requested pid and optionally each parent and |
|
498 * start time. |
379 * Reads /proc and accumulates any process who parent pid matches. |
499 * Reads /proc and accumulates any process who parent pid matches. |
380 * The resulting pids are stored into the array of longs. |
500 * The resulting pids are stored into the array of longs. |
381 * The number of pids is returned if they all fit. |
501 * The number of pids is returned if they all fit. |
382 * If the array is too short, the negative of the desired length is returned. * |
502 * If the array is too short, the negative of the desired length is returned. |
383 * Class: java_lang_ProcessHandleImpl |
503 */ |
384 * Method: getChildPids |
504 jint unix_getChildren(JNIEnv *env, jlong jpid, jlongArray jarray, |
385 * Signature: (J[J[J)I |
505 jlongArray jparentArray, jlongArray jstimesArray) { |
386 */ |
|
387 JNIEXPORT jint JNICALL |
|
388 Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env, |
|
389 jclass clazz, |
|
390 jlong jpid, |
|
391 jlongArray jarray, |
|
392 jlongArray jparentArray, |
|
393 jlongArray jstimesArray) { |
|
394 |
|
395 DIR* dir; |
506 DIR* dir; |
396 struct dirent* ptr; |
507 struct dirent* ptr; |
397 pid_t pid = (pid_t) jpid; |
508 pid_t pid = (pid_t) jpid; |
398 jlong* pids = NULL; |
509 jlong* pids = NULL; |
399 jlong* ppids = NULL; |
510 jlong* ppids = NULL; |
496 closedir(dir); |
608 closedir(dir); |
497 // If more pids than array had size for; count will be greater than array size |
609 // If more pids than array had size for; count will be greater than array size |
498 return count; |
610 return count; |
499 } |
611 } |
500 |
612 |
501 |
613 #endif // defined(__solaris__) || defined (__linux__) || defined(_AIX) |
502 /************************************************************** |
614 |
503 * Implementation of ProcessHandleImpl_Info native methods. |
615 /* |
504 */ |
616 * The following functions are common on Solaris and AIX. |
505 |
617 */ |
506 /* |
618 |
507 * Fill in the Info object from the OS information about the process. |
619 #if defined(__solaris__) || defined(_AIX) |
508 * |
|
509 * Class: java_lang_ProcessHandleImpl_Info |
|
510 * Method: info0 |
|
511 * Signature: (JLjava/lang/ProcessHandle/Info;)I |
|
512 */ |
|
513 JNIEXPORT void JNICALL |
|
514 Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env, |
|
515 jobject jinfo, |
|
516 jlong jpid) { |
|
517 pid_t pid = (pid_t) jpid; |
|
518 pid_t ppid; |
|
519 jlong totalTime = 0L; |
|
520 jlong startTime = -1L; |
|
521 |
|
522 ppid = getStatInfo(env, pid, &totalTime, &startTime); |
|
523 if (ppid > 0) { |
|
524 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime); |
|
525 JNU_CHECK_EXCEPTION(env); |
|
526 |
|
527 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime); |
|
528 JNU_CHECK_EXCEPTION(env); |
|
529 |
|
530 getCmdlineInfo(env, pid, jinfo); |
|
531 } |
|
532 } |
|
533 |
620 |
534 /** |
621 /** |
535 * Read /proc/<pid>/stat and return the ppid, total cputime and start time. |
622 * Helper function to get the 'psinfo_t' data from "/proc/%d/psinfo". |
536 * -1 is fail; zero is unknown; > 0 is parent pid |
623 * Returns 0 on success and -1 on error. |
537 */ |
624 */ |
538 static pid_t getStatInfo(JNIEnv *env, pid_t pid, |
625 static int getPsinfo(pid_t pid, psinfo_t *psinfo) { |
539 jlong *totalTime, jlong* startTime) { |
|
540 FILE* fp; |
626 FILE* fp; |
541 char buffer[2048]; |
|
542 int statlen; |
|
543 char fn[32]; |
627 char fn[32]; |
544 char* s; |
628 int ret; |
545 int parentPid; |
|
546 long unsigned int utime = 0; // clock tics |
|
547 long unsigned int stime = 0; // clock tics |
|
548 long long unsigned int start = 0; // microseconds |
|
549 |
629 |
550 /* |
630 /* |
551 * Try to stat and then open /proc/%d/stat |
631 * Try to open /proc/%d/psinfo |
552 */ |
632 */ |
553 snprintf(fn, sizeof fn, STAT_FILE, pid); |
633 snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid); |
554 |
|
555 fp = fopen(fn, "r"); |
634 fp = fopen(fn, "r"); |
556 if (fp == NULL) { |
635 if (fp == NULL) { |
557 return -1; // fail, no such /proc/pid/stat |
636 return -1; |
558 } |
637 } |
|
638 |
|
639 ret = fread(psinfo, 1, sizeof(psinfo_t), fp); |
|
640 fclose(fp); |
|
641 if (ret < sizeof(psinfo_t)) { |
|
642 return -1; |
|
643 } |
|
644 return 0; |
|
645 } |
|
646 |
|
647 /** |
|
648 * Read /proc/<pid>/psinfo and return the ppid, total cputime and start time. |
|
649 * Return: -1 is fail; >= 0 is parent pid |
|
650 * 'total' will contain the running time of 'pid' in nanoseconds. |
|
651 * 'start' will contain the start time of 'pid' in milliseconds since epoch. |
|
652 */ |
|
653 pid_t unix_getParentPidAndTimings(JNIEnv *env, pid_t pid, |
|
654 jlong *totalTime, jlong* startTime) { |
|
655 psinfo_t psinfo; |
|
656 |
|
657 if (getPsinfo(pid, &psinfo) < 0) { |
|
658 return -1; |
|
659 } |
|
660 |
|
661 *totalTime = psinfo.pr_time.tv_sec * 1000000000L + psinfo.pr_time.tv_nsec; |
|
662 |
|
663 *startTime = psinfo.pr_start.tv_sec * (jlong)1000 + |
|
664 psinfo.pr_start.tv_nsec / 1000000; |
|
665 |
|
666 return (pid_t) psinfo.pr_ppid; |
|
667 } |
|
668 |
|
669 void unix_getCmdlineAndUserInfo(JNIEnv *env, jobject jinfo, pid_t pid) { |
|
670 psinfo_t psinfo; |
|
671 char fn[32]; |
|
672 char exePath[PATH_MAX]; |
|
673 char prargs[PRARGSZ + 1]; |
|
674 jstring cmdexe = NULL; |
|
675 int ret; |
559 |
676 |
560 /* |
677 /* |
561 * The format is: pid (command) state ppid ... |
678 * On Solaris, the full path to the executable command is the link in |
562 * As the command could be anything we must find the right most |
679 * /proc/<pid>/paths/a.out. But it is only readable for processes we own. |
563 * ")" and then skip the white spaces that follow it. |
|
564 */ |
680 */ |
565 statlen = fread(buffer, 1, (sizeof buffer - 1), fp); |
681 #if defined(__solaris__) |
566 fclose(fp); |
682 snprintf(fn, sizeof fn, "/proc/%d/path/a.out", pid); |
567 if (statlen < 0) { |
683 if ((ret = readlink(fn, exePath, PATH_MAX - 1)) > 0) { |
568 return 0; // parent pid is not available |
684 // null terminate and create String to store for command |
569 } |
685 exePath[ret] = '\0'; |
570 |
686 CHECK_NULL(cmdexe = JNU_NewStringPlatform(env, exePath)); |
571 buffer[statlen] = '\0'; |
687 } |
572 s = strchr(buffer, '('); |
688 #endif |
573 if (s == NULL) { |
|
574 return 0; // parent pid is not available |
|
575 } |
|
576 // Found start of command, skip to end |
|
577 s++; |
|
578 s = strrchr(s, ')'); |
|
579 if (s == NULL) { |
|
580 return 0; // parent pid is not available |
|
581 } |
|
582 s++; |
|
583 |
|
584 // Scan the needed fields from status, retaining only ppid(4), |
|
585 // utime (14), stime(15), starttime(22) |
|
586 if (4 != sscanf(s, " %*c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu", |
|
587 &parentPid, &utime, &stime, &start)) { |
|
588 return 0; // not all values parsed; return error |
|
589 } |
|
590 |
|
591 *totalTime = (utime + stime) * (jlong)(1000000000 / clock_ticks_per_second); |
|
592 |
|
593 *startTime = bootTime_ms + ((start * 1000) / clock_ticks_per_second); |
|
594 |
|
595 return parentPid; |
|
596 } |
|
597 |
|
598 /** |
|
599 * Construct the argument array by parsing the arguments from the sequence |
|
600 * of arguments. The zero'th arg is the command executable |
|
601 */ |
|
602 static int fillArgArray(JNIEnv *env, jobject jinfo, |
|
603 int nargs, char *cp, char *argsEnd, jstring cmdexe) { |
|
604 jobject argsArray; |
|
605 int i; |
|
606 |
|
607 if (nargs < 1) { |
|
608 return 0; |
|
609 } |
|
610 |
|
611 if (cmdexe == NULL) { |
|
612 // Create a string from arg[0] |
|
613 CHECK_NULL_RETURN((cmdexe = JNU_NewStringPlatform(env, cp)), -1); |
|
614 } |
|
615 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, cmdexe); |
|
616 JNU_CHECK_EXCEPTION_RETURN(env, -3); |
|
617 |
|
618 // Create a String array for nargs-1 elements |
|
619 argsArray = (*env)->NewObjectArray(env, nargs - 1, JNU_ClassString(env), NULL); |
|
620 CHECK_NULL_RETURN(argsArray, -1); |
|
621 |
|
622 for (i = 0; i < nargs - 1; i++) { |
|
623 jstring str = NULL; |
|
624 |
|
625 cp += strnlen(cp, (argsEnd - cp)) + 1; |
|
626 if (cp > argsEnd || *cp == '\0') { |
|
627 return -2; // Off the end pointer or an empty argument is an error |
|
628 } |
|
629 |
|
630 CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1); |
|
631 |
|
632 (*env)->SetObjectArrayElement(env, argsArray, i, str); |
|
633 JNU_CHECK_EXCEPTION_RETURN(env, -3); |
|
634 } |
|
635 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray); |
|
636 JNU_CHECK_EXCEPTION_RETURN(env, -4); |
|
637 return 0; |
|
638 } |
|
639 |
|
640 |
|
641 static void getCmdlineInfo(JNIEnv *env, pid_t pid, jobject jinfo) { |
|
642 int fd; |
|
643 int cmdlen = 0; |
|
644 char *cmdline = NULL, *cmdEnd; // used for command line args and exe |
|
645 jstring cmdexe = NULL; |
|
646 char fn[32]; |
|
647 struct stat stat_buf; |
|
648 |
689 |
649 /* |
690 /* |
650 * Try to open /proc/%d/cmdline |
691 * Now try to open /proc/%d/psinfo |
651 */ |
692 */ |
652 snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid); |
693 if (getPsinfo(pid, &psinfo) < 0) { |
653 if ((fd = open(fn, O_RDONLY)) < 0) { |
694 unix_fillArgArray(env, jinfo, 0, NULL, NULL, cmdexe, NULL); |
654 return; |
695 return; |
655 } |
696 } |
656 |
697 |
657 do { // Block to break out of on errors |
698 unix_getUserInfo(env, jinfo, psinfo.pr_uid); |
658 int i; |
699 |
659 char *s; |
700 /* |
660 |
701 * Now read psinfo.pr_psargs which contains the first PRARGSZ characters of the |
661 cmdline = (char*)malloc(PATH_MAX); |
702 * argument list (i.e. arg[0] arg[1] ...). Unfortunately, PRARGSZ is usually set |
662 if (cmdline == NULL) { |
703 * to 80 characters only. Nevertheless it's better than nothing :) |
663 break; |
704 */ |
664 } |
705 strncpy(prargs, psinfo.pr_psargs, PRARGSZ); |
665 |
706 prargs[PRARGSZ] = '\0'; |
666 /* |
707 if (prargs[0] == '\0') { |
667 * The path to the executable command is the link in /proc/<pid>/exe. |
708 /* If psinfo.pr_psargs didn't contain any strings, use psinfo.pr_fname |
|
709 * (which only contains the last component of exec()ed pathname) as a |
|
710 * last resort. This is true for AIX kernel processes for example. |
668 */ |
711 */ |
669 snprintf(fn, sizeof fn, "/proc/%d/exe", pid); |
712 strncpy(prargs, psinfo.pr_fname, PRARGSZ); |
670 if ((cmdlen = readlink(fn, cmdline, PATH_MAX - 1)) > 0) { |
713 prargs[PRARGSZ] = '\0'; |
671 // null terminate and create String to store for command |
714 } |
672 cmdline[cmdlen] = '\0'; |
715 unix_fillArgArray(env, jinfo, 0, NULL, NULL, cmdexe, |
673 cmdexe = JNU_NewStringPlatform(env, cmdline); |
716 prargs[0] == '\0' ? NULL : prargs); |
674 (*env)->ExceptionClear(env); // unconditionally clear any exception |
717 } |
675 } |
718 |
676 |
719 #endif // defined(__solaris__) || defined(_AIX) |
677 /* |
|
678 * The buffer format is the arguments nul terminated with an extra nul. |
|
679 */ |
|
680 cmdlen = read(fd, cmdline, PATH_MAX-1); |
|
681 if (cmdlen < 0) { |
|
682 break; |
|
683 } |
|
684 |
|
685 // Terminate the buffer and count the arguments |
|
686 cmdline[cmdlen] = '\0'; |
|
687 cmdEnd = &cmdline[cmdlen + 1]; |
|
688 for (s = cmdline,i = 0; *s != '\0' && (s < cmdEnd); i++) { |
|
689 s += strnlen(s, (cmdEnd - s)) + 1; |
|
690 } |
|
691 |
|
692 if (fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe) < 0) { |
|
693 break; |
|
694 } |
|
695 |
|
696 // Get and store the user name |
|
697 if (fstat(fd, &stat_buf) == 0) { |
|
698 jstring name = uidToUser(env, stat_buf.st_uid); |
|
699 if (name != NULL) { |
|
700 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name); |
|
701 } |
|
702 } |
|
703 } while (0); |
|
704 |
|
705 if (cmdline != NULL) { |
|
706 free(cmdline); |
|
707 } |
|
708 if (fd >= 0) { |
|
709 close(fd); |
|
710 } |
|
711 } |
|
712 |
|
713 /** |
|
714 * Read the boottime from /proc/stat. |
|
715 */ |
|
716 static long long getBoottime(JNIEnv *env) { |
|
717 FILE *fp; |
|
718 char *line = NULL; |
|
719 size_t len = 0; |
|
720 long long bootTime = 0; |
|
721 |
|
722 fp = fopen("/proc/stat", "r"); |
|
723 if (fp == NULL) { |
|
724 return -1; |
|
725 } |
|
726 |
|
727 while (getline(&line, &len, fp) != -1) { |
|
728 if (sscanf(line, "btime %llu", &bootTime) == 1) { |
|
729 break; |
|
730 } |
|
731 } |
|
732 free(line); |
|
733 |
|
734 if (fp != 0) { |
|
735 fclose(fp); |
|
736 } |
|
737 |
|
738 return bootTime * 1000; |
|
739 } |
|
740 |
|
741 #endif // defined(__linux__) || defined(__AIX__) |
|
742 |
|
743 |
|
744 /* Block until a child process exits and return its exit code. |
|
745 Note, can only be called once for any given pid. */ |
|
746 JNIEXPORT jint JNICALL |
|
747 Java_java_lang_ProcessImpl_waitForProcessExit(JNIEnv* env, |
|
748 jobject junk, |
|
749 jint pid) |
|
750 { |
|
751 /* We used to use waitid() on Solaris, waitpid() on Linux, but |
|
752 * waitpid() is more standard, so use it on all POSIX platforms. */ |
|
753 int status; |
|
754 /* Wait for the child process to exit. This returns immediately if |
|
755 the child has already exited. */ |
|
756 while (waitpid(pid, &status, 0) < 0) { |
|
757 switch (errno) { |
|
758 case ECHILD: return 0; |
|
759 case EINTR: break; |
|
760 default: return -1; |
|
761 } |
|
762 } |
|
763 |
|
764 if (WIFEXITED(status)) { |
|
765 /* |
|
766 * The child exited normally; get its exit code. |
|
767 */ |
|
768 return WEXITSTATUS(status); |
|
769 } else if (WIFSIGNALED(status)) { |
|
770 /* The child exited because of a signal. |
|
771 * The best value to return is 0x80 + signal number, |
|
772 * because that is what all Unix shells do, and because |
|
773 * it allows callers to distinguish between process exit and |
|
774 * process death by signal. |
|
775 * Unfortunately, the historical behavior on Solaris is to return |
|
776 * the signal number, and we preserve this for compatibility. */ |
|
777 #ifdef __solaris__ |
|
778 return WTERMSIG(status); |
|
779 #else |
|
780 return 0x80 + WTERMSIG(status); |
|
781 #endif |
|
782 } else { |
|
783 /* |
|
784 * Unknown exit code; pass it through. |
|
785 */ |
|
786 return status; |
|
787 } |
|
788 } |
|
789 |
|
790 |
|