176 /* |
180 /* |
177 * Class: java_lang_ProcessHandleImpl |
181 * Class: java_lang_ProcessHandleImpl |
178 * Method: getCurrentPid0 |
182 * Method: getCurrentPid0 |
179 * Signature: ()J |
183 * Signature: ()J |
180 */ |
184 */ |
181 JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_getCurrentPid0 |
185 JNIEXPORT jlong JNICALL |
182 (JNIEnv *env, jclass clazz) { |
186 Java_java_lang_ProcessHandleImpl_getCurrentPid0(JNIEnv *env, jclass clazz) { |
183 pid_t pid = getpid(); |
187 pid_t pid = getpid(); |
184 return (jlong) pid; |
188 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 } |
189 } |
197 |
190 |
198 /* |
191 /* |
199 * Class: java_lang_ProcessHandleImpl |
192 * Class: java_lang_ProcessHandleImpl |
200 * Method: destroy0 |
193 * Method: destroy0 |
201 * Signature: (Z)Z |
194 * Signature: (Z)Z |
202 */ |
195 */ |
203 JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_destroy0 |
196 JNIEXPORT jboolean JNICALL |
204 (JNIEnv *env, jobject obj, jlong jpid, jboolean force) { |
197 Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env, |
|
198 jobject obj, |
|
199 jlong jpid, |
|
200 jlong startTime, |
|
201 jboolean force) { |
205 pid_t pid = (pid_t) jpid; |
202 pid_t pid = (pid_t) jpid; |
206 int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM; |
203 int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM; |
207 return (kill(pid, sig) >= 0); |
204 jlong start = Java_java_lang_ProcessHandleImpl_isAlive0(env, obj, jpid); |
208 |
205 |
|
206 if (start == startTime || start == 0 || startTime == 0) { |
|
207 return (kill(pid, sig) < 0) ? JNI_FALSE : JNI_TRUE; |
|
208 } else { |
|
209 return JNI_FALSE; |
|
210 } |
209 } |
211 } |
210 |
212 |
211 /** |
213 /** |
212 * Size of password or group entry when not available via sysconf |
214 * Size of password or group entry when not available via sysconf |
213 */ |
215 */ |
309 clazz, "totalTime", "J")); |
308 clazz, "totalTime", "J")); |
310 CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env, |
309 CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env, |
311 clazz, "startTime", "J")); |
310 clazz, "startTime", "J")); |
312 CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env, |
311 CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env, |
313 clazz, "user", "Ljava/lang/String;")); |
312 clazz, "user", "Ljava/lang/String;")); |
|
313 } |
|
314 |
|
315 /************************************************************** |
|
316 * Static method to initialize the ticks per second rate. |
|
317 * |
|
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) { |
314 clock_ticks_per_second = sysconf(_SC_CLK_TCK); |
324 clock_ticks_per_second = sysconf(_SC_CLK_TCK); |
315 bootTime_ms = getBoottime(env); |
325 bootTime_ms = getBoottime(env); |
316 } |
326 } |
317 |
327 |
318 /* |
328 /* |
|
329 * Check if a process is alive. |
|
330 * Return the start time (ms since 1970) if it is available. |
|
331 * If the start time is not available return 0. |
|
332 * If the pid is invalid, return -1. |
|
333 * |
|
334 * Class: java_lang_ProcessHandleImpl |
|
335 * Method: isAlive0 |
|
336 * Signature: (J)J |
|
337 */ |
|
338 JNIEXPORT jlong JNICALL |
|
339 Java_java_lang_ProcessHandleImpl_isAlive0(JNIEnv *env, jobject obj, jlong jpid) { |
|
340 pid_t pid = (pid_t) jpid; |
|
341 jlong startTime = 0L; |
|
342 jlong totalTime = 0L; |
|
343 pid_t ppid = getStatInfo(env, pid, &totalTime, &startTime); |
|
344 return (ppid <= 0) ? -1 : startTime; |
|
345 } |
|
346 |
|
347 /* |
319 * Returns the parent pid of the requested pid. |
348 * Returns the parent pid of the requested pid. |
|
349 * The start time of the process must match (or be ANY). |
320 * |
350 * |
321 * Class: java_lang_ProcessHandleImpl |
351 * Class: java_lang_ProcessHandleImpl |
322 * Method: parent0 |
352 * Method: parent0 |
323 * Signature: (J)J |
353 * Signature: (J)J |
324 */ |
354 */ |
325 JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0 |
355 JNIEXPORT jlong JNICALL |
326 (JNIEnv *env, jobject obj, jlong jpid) { |
356 Java_java_lang_ProcessHandleImpl_parent0(JNIEnv *env, |
|
357 jobject obj, |
|
358 jlong jpid, |
|
359 jlong startTime) { |
327 pid_t pid = (pid_t) jpid; |
360 pid_t pid = (pid_t) jpid; |
328 pid_t ppid = -1; |
361 pid_t ppid; |
329 |
362 |
330 pid_t mypid = getpid(); |
363 pid_t mypid = getpid(); |
331 if (pid == mypid) { |
364 if (pid == mypid) { |
332 ppid = getppid(); |
365 ppid = getppid(); |
333 } else { |
366 } else { |
334 ppid = parentPid(env, pid); |
367 jlong start = 0L;; |
|
368 jlong total = 0L; // unused |
|
369 ppid = getStatInfo(env, pid, &total, &start); |
|
370 if (start != startTime && start != 0 && startTime != 0) { |
|
371 ppid = -1; |
|
372 } |
335 } |
373 } |
336 return (jlong) ppid; |
374 return (jlong) ppid; |
337 } |
375 } |
338 |
376 |
339 /* |
377 /* |
340 * Returns the children of the requested pid and optionally each parent. |
378 * Returns the children of the requested pid and optionally each parent. |
341 * |
379 * Reads /proc and accumulates any process who parent pid matches. |
|
380 * The resulting pids are stored into the array of longs. |
|
381 * 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. * |
342 * Class: java_lang_ProcessHandleImpl |
383 * Class: java_lang_ProcessHandleImpl |
343 * Method: getChildPids |
384 * Method: getChildPids |
344 * Signature: (J[J[J)I |
385 * Signature: (J[J[J)I |
345 */ |
386 */ |
346 JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0 |
387 JNIEXPORT jint JNICALL |
347 (JNIEnv *env, jclass clazz, jlong jpid, |
388 Java_java_lang_ProcessHandleImpl_getProcessPids0(JNIEnv *env, |
348 jlongArray jarray, jlongArray jparentArray) { |
389 jclass clazz, |
349 return getChildren(env, jpid, jarray, jparentArray); |
390 jlong jpid, |
350 } |
391 jlongArray jarray, |
351 |
392 jlongArray jparentArray, |
352 /* |
393 jlongArray jstimesArray) { |
353 * Reads /proc and accumulates any process who parent pid matches. |
394 |
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; |
395 DIR* dir; |
361 struct dirent* ptr; |
396 struct dirent* ptr; |
362 pid_t pid = (pid_t) jpid; |
397 pid_t pid = (pid_t) jpid; |
363 pid_t ppid = 0; |
|
364 size_t count = 0; |
|
365 jlong* pids = NULL; |
398 jlong* pids = NULL; |
366 jlong* ppids = NULL; |
399 jlong* ppids = NULL; |
367 size_t parentArraySize = 0; |
400 jlong* stimes = NULL; |
368 size_t arraySize = 0; |
401 jsize parentArraySize = 0; |
|
402 jsize arraySize = 0; |
|
403 jsize stimesSize = 0; |
|
404 jsize count = 0; |
369 |
405 |
370 arraySize = (*env)->GetArrayLength(env, jarray); |
406 arraySize = (*env)->GetArrayLength(env, jarray); |
371 JNU_CHECK_EXCEPTION_RETURN(env, -1); |
407 JNU_CHECK_EXCEPTION_RETURN(env, -1); |
372 if (jparentArray != NULL) { |
408 if (jparentArray != NULL) { |
373 parentArraySize = (*env)->GetArrayLength(env, jparentArray); |
409 parentArraySize = (*env)->GetArrayLength(env, jparentArray); |
374 JNU_CHECK_EXCEPTION_RETURN(env, -1); |
410 JNU_CHECK_EXCEPTION_RETURN(env, -1); |
375 |
411 |
376 if (arraySize != parentArraySize) { |
412 if (arraySize != parentArraySize) { |
|
413 JNU_ThrowIllegalArgumentException(env, "array sizes not equal"); |
|
414 return 0; |
|
415 } |
|
416 } |
|
417 if (jstimesArray != NULL) { |
|
418 stimesSize = (*env)->GetArrayLength(env, jstimesArray); |
|
419 JNU_CHECK_EXCEPTION_RETURN(env, -1); |
|
420 |
|
421 if (arraySize != stimesSize) { |
377 JNU_ThrowIllegalArgumentException(env, "array sizes not equal"); |
422 JNU_ThrowIllegalArgumentException(env, "array sizes not equal"); |
378 return 0; |
423 return 0; |
379 } |
424 } |
380 } |
425 } |
381 |
426 |
398 ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL); |
443 ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL); |
399 if (ppids == NULL) { |
444 if (ppids == NULL) { |
400 break; |
445 break; |
401 } |
446 } |
402 } |
447 } |
|
448 if (jstimesArray != NULL) { |
|
449 stimes = (*env)->GetLongArrayElements(env, jstimesArray, NULL); |
|
450 if (stimes == NULL) { |
|
451 break; |
|
452 } |
|
453 } |
403 |
454 |
404 while ((ptr = readdir(dir)) != NULL) { |
455 while ((ptr = readdir(dir)) != NULL) { |
|
456 pid_t ppid = 0; |
|
457 jlong totalTime = 0L; |
|
458 jlong startTime = 0L; |
|
459 |
405 /* skip files that aren't numbers */ |
460 /* skip files that aren't numbers */ |
406 pid_t childpid = (pid_t) atoi(ptr->d_name); |
461 pid_t childpid = (pid_t) atoi(ptr->d_name); |
407 if ((int) childpid <= 0) { |
462 if ((int) childpid <= 0) { |
408 continue; |
463 continue; |
409 } |
464 } |
410 |
465 // Read /proc/pid/stat and get the parent pid, and start time |
411 ppid = 0; |
466 ppid = getStatInfo(env, childpid, &totalTime, &startTime); |
412 if (pid != 0 || jparentArray != NULL) { |
467 if (ppid > 0 && (pid == 0 || ppid == pid)) { |
413 // parentPid opens and reads /proc/pid/stat |
|
414 ppid = parentPid(env, childpid); |
|
415 } |
|
416 if (pid == 0 || ppid == pid) { |
|
417 if (count < arraySize) { |
468 if (count < arraySize) { |
418 // Only store if it fits |
469 // Only store if it fits |
419 pids[count] = (jlong) childpid; |
470 pids[count] = (jlong) childpid; |
420 |
471 |
421 if (ppids != NULL) { |
472 if (ppids != NULL) { |
422 // Store the parentPid |
473 // Store the parentPid |
423 ppids[count] = (jlong) ppid; |
474 ppids[count] = (jlong) ppid; |
424 } |
475 } |
|
476 if (stimes != NULL) { |
|
477 // Store the process start time |
|
478 stimes[count] = startTime; |
|
479 } |
425 } |
480 } |
426 count++; // Count to tabulate size needed |
481 count++; // Count to tabulate size needed |
427 } |
482 } |
428 } |
483 } |
429 } while (0); |
484 } while (0); |
432 (*env)->ReleaseLongArrayElements(env, jarray, pids, 0); |
487 (*env)->ReleaseLongArrayElements(env, jarray, pids, 0); |
433 } |
488 } |
434 if (ppids != NULL) { |
489 if (ppids != NULL) { |
435 (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0); |
490 (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0); |
436 } |
491 } |
|
492 if (stimes != NULL) { |
|
493 (*env)->ReleaseLongArrayElements(env, jstimesArray, stimes, 0); |
|
494 } |
437 |
495 |
438 closedir(dir); |
496 closedir(dir); |
439 // If more pids than array had size for; count will be greater than array size |
497 // If more pids than array had size for; count will be greater than array size |
440 return count; |
498 return count; |
441 } |
499 } |
442 |
500 |
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 |
501 |
488 /************************************************************** |
502 /************************************************************** |
489 * Implementation of ProcessHandleImpl_Info native methods. |
503 * Implementation of ProcessHandleImpl_Info native methods. |
490 */ |
504 */ |
491 |
505 |
494 * |
508 * |
495 * Class: java_lang_ProcessHandleImpl_Info |
509 * Class: java_lang_ProcessHandleImpl_Info |
496 * Method: info0 |
510 * Method: info0 |
497 * Signature: (JLjava/lang/ProcessHandle/Info;)I |
511 * Signature: (JLjava/lang/ProcessHandle/Info;)I |
498 */ |
512 */ |
499 JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0 |
513 JNIEXPORT void JNICALL |
500 (JNIEnv *env, jobject jinfo, jlong jpid) { |
514 Java_java_lang_ProcessHandleImpl_00024Info_info0(JNIEnv *env, |
|
515 jobject jinfo, |
|
516 jlong jpid) { |
501 pid_t pid = (pid_t) jpid; |
517 pid_t pid = (pid_t) jpid; |
502 getStatInfo(env, jinfo, (pid_t)pid); |
518 pid_t ppid; |
503 getCmdlineInfo(env, pid, jinfo); |
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 } |
504 } |
532 } |
505 |
533 |
506 /** |
534 /** |
507 * Read /proc/<pid>/stat and fill in the fields of the Info object. |
535 * Read /proc/<pid>/stat and return the ppid, total cputime and start time. |
508 * The executable name, plus the user, system, and start times are gathered. |
536 * -1 is fail; zero is unknown; > 0 is parent pid |
509 */ |
537 */ |
510 static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid) { |
538 static pid_t getStatInfo(JNIEnv *env, pid_t pid, |
511 char state; |
539 jlong *totalTime, jlong* startTime) { |
512 FILE* fp; |
540 FILE* fp; |
513 char buffer[2048]; |
541 char buffer[2048]; |
514 struct stat stat_buf; |
|
515 int statlen; |
542 int statlen; |
516 char fn[32]; |
543 char fn[32]; |
517 int i, ppid = -2; |
|
518 char* s; |
544 char* s; |
519 char *cmd; |
545 int parentPid; |
520 jstring name = NULL; |
546 long unsigned int utime = 0; // clock tics |
521 unsigned long userTime = 0; // clock tics |
547 long unsigned int stime = 0; // clock tics |
522 unsigned long totalTime = 0; // clock tics |
548 long long unsigned int start = 0; // microseconds |
523 jlong total = 0; // nano seconds |
|
524 unsigned long long startTime = 0; // microseconds |
|
525 |
549 |
526 /* |
550 /* |
527 * Try to stat and then open /proc/%d/stat |
551 * Try to stat and then open /proc/%d/stat |
528 */ |
552 */ |
529 snprintf(fn, sizeof fn, "/proc/%d/stat", pid); |
553 snprintf(fn, sizeof fn, STAT_FILE, 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 |
554 |
539 fp = fopen(fn, "r"); |
555 fp = fopen(fn, "r"); |
540 if (fp == NULL) { |
556 if (fp == NULL) { |
541 return; |
557 return -1; // fail, no such /proc/pid/stat |
542 } |
558 } |
543 |
559 |
544 /* |
560 /* |
545 * The format is: pid (command) state ppid ... |
561 * The format is: pid (command) state ppid ... |
546 * As the command could be anything we must find the right most |
562 * As the command could be anything we must find the right most |
547 * ")" and then skip the white spaces that follow it. |
563 * ")" and then skip the white spaces that follow it. |
548 */ |
564 */ |
549 statlen = fread(buffer, 1, (sizeof buffer - 1), fp); |
565 statlen = fread(buffer, 1, (sizeof buffer - 1), fp); |
550 fclose(fp); |
566 fclose(fp); |
551 if (statlen < 0) { |
567 if (statlen < 0) { |
552 return; |
568 return 0; // parent pid is not available |
553 } |
569 } |
554 |
570 |
555 buffer[statlen] = '\0'; |
571 buffer[statlen] = '\0'; |
556 s = strchr(buffer, '('); |
572 s = strchr(buffer, '('); |
557 if (s == NULL) { |
573 if (s == NULL) { |
558 return; |
574 return 0; // parent pid is not available |
559 } |
575 } |
560 // Found start of command, skip to end |
576 // Found start of command, skip to end |
561 s++; |
577 s++; |
562 s = strrchr(s, ')'); |
578 s = strrchr(s, ')'); |
563 if (s == NULL) { |
579 if (s == NULL) { |
564 return; |
580 return 0; // parent pid is not available |
565 } |
581 } |
566 s++; |
582 s++; |
567 |
583 |
568 // Scan the needed fields from status, retaining only ppid(4), |
584 // Scan the needed fields from status, retaining only ppid(4), |
569 // utime (14), stime(15), starttime(22) |
585 // 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", |
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", |
571 &state, &ppid, &userTime, &totalTime, &startTime); |
587 &parentPid, &utime, &stime, &start)) { |
572 if (i != 5) { |
588 return 0; // not all values parsed; return error |
573 return; // not all values parsed; return error |
589 } |
574 } |
590 |
575 |
591 *totalTime = (utime + stime) * (jlong)(1000000000 / clock_ticks_per_second); |
576 total = (userTime + totalTime) * (jlong)(1000000000 / clock_ticks_per_second); |
592 |
577 |
593 *startTime = bootTime_ms + ((start * 1000) / clock_ticks_per_second); |
578 startTime = bootTime_ms + ((startTime * 1000) / clock_ticks_per_second); |
594 |
579 |
595 return parentPid; |
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 } |
596 } |
585 |
597 |
586 /** |
598 /** |
587 * Construct the argument array by parsing the arguments from the sequence |
599 * Construct the argument array by parsing the arguments from the sequence |
588 * of arguments. The zero'th arg is the command executable |
600 * of arguments. The zero'th arg is the command executable |