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 34 #include <stdio.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <pwd.h> 38 #include <signal.h> 39 #include <stdlib.h> 40 #include <unistd.h> 41 #include <string.h> 42 #include <dirent.h> 43 #include <ctype.h> 44 #include <limits.h> 45 #include <sys/types.h> 46 #include <sys/stat.h> 47 #include <sys/wait.h> 48 49 #ifdef _AIX 50 #include <sys/procfs.h> 51 #endif 52 #ifdef __solaris__ 53 #include <procfs.h> 54 #endif 55 56 /** 57 * This file contains the implementation of the native ProcessHandleImpl 58 * functions which are common to all Unix variants. 59 * 60 * The currently supported Unix variants are Solaris, Linux, MaxOS X and AIX. 61 * The various similarities and differences between these systems make it hard 62 * to find a clear boundary between platform specific and shared code. 63 * 64 * In order to ease code sharing between the pltforms 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 are platform dependent, the 74 * 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_00024Info_initIDs() 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_00024Info_initIDs() 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 96 * the same on Solaris and AIX but different on Linux and MacOSX. We therefore 97 * simply call the two helpers os_getStatInfo() and os_getCmdlineInfo(). The 98 * 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_getStatInfo() and 102 * unix_getCmdlineInfo() 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 111 112 #ifndef WIFEXITED 113 #define WIFEXITED(status) (((status)&0xFF) == 0) 114 #endif 115 116 #ifndef WEXITSTATUS 117 #define WEXITSTATUS(status) (((status)>>8)&0xFF) 118 #endif 119 120 #ifndef WIFSIGNALED 121 #define WIFSIGNALED(status) (((status)&0xFF) > 0 && ((status)&0xFF00) == 0) 122 #endif 123 124 #ifndef WTERMSIG 125 #define WTERMSIG(status) ((status)&0x7F) 126 #endif 127 128 #ifdef __solaris__ 129 /* The child exited because of a signal. 130 * The best value to return is 0x80 + signal number, 131 * because that is what all Unix shells do, and because 132 * it allows callers to distinguish between process exit and 133 * process death by signal. 134 * Unfortunately, the historical behavior on Solaris is to return 135 * the signal number, and we preserve this for compatibility. */ 136 #define WTERMSIG_RETURN(status) WTERMSIG(status); 137 #else 138 #define WTERMSIG_RETURN(status) (WTERMSIG(status) + 0x80); 139 #endif 140 141 #define RESTARTABLE(_cmd, _result) do { \ 142 do { \ 143 _result = _cmd; \ 144 } while((_result == -1) && (errno == EINTR)); \ 145 } while(0) 146 147 #define RESTARTABLE_RETURN_PTR(_cmd, _result) do { \ 148 do { \ 149 _result = _cmd; \ 150 } while((_result == NULL) && (errno == EINTR)); \ 151 } while(0) 152 153 154 /* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */ 155 jfieldID ProcessHandleImpl_Info_commandID; 156 157 /* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */ 158 jfieldID ProcessHandleImpl_Info_argumentsID; 159 160 /* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */ 161 jfieldID ProcessHandleImpl_Info_totalTimeID; 162 163 /* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */ 164 jfieldID ProcessHandleImpl_Info_startTimeID; 165 166 /* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */ 167 jfieldID ProcessHandleImpl_Info_userID; 168 169 /* static value for clock ticks per second. */ 170 long clock_ticks_per_second; 171 172 /************************************************************** 173 * Static method to initialize field IDs and the ticks per second rate. 174 * 175 * Class: java_lang_ProcessHandleImpl_Info 176 * Method: initIDs 177 * Signature: ()V 178 */ 179 JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs 180 (JNIEnv *env, jclass clazz) { 181 182 CHECK_NULL(ProcessHandleImpl_Info_commandID = 183 (*env)->GetFieldID(env, clazz, "command", "Ljava/lang/String;")); 184 CHECK_NULL(ProcessHandleImpl_Info_argumentsID = 185 (*env)->GetFieldID(env, clazz, "arguments", "[Ljava/lang/String;")); 186 CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = 187 (*env)->GetFieldID(env, clazz, "totalTime", "J")); 188 CHECK_NULL(ProcessHandleImpl_Info_startTimeID = 189 (*env)->GetFieldID(env, clazz, "startTime", "J")); 190 CHECK_NULL(ProcessHandleImpl_Info_userID = 191 (*env)->GetFieldID(env, clazz, "user", "Ljava/lang/String;")); 192 clock_ticks_per_second = sysconf(_SC_CLK_TCK); 193 194 os_init(env, clazz); 195 } 196 197 /* Block until a child process exits and return its exit code. 198 * Note, can only be called once for any given pid if reapStatus = true. 199 * 200 * Class: java_lang_ProcessHandleImpl 201 * Method: waitForProcessExit0 202 * Signature: (JZ)I 203 */ 204 JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_waitForProcessExit0 205 (JNIEnv* env, jclass junk, jlong jpid, jboolean reapStatus) { 206 pid_t pid = (pid_t)jpid; 207 errno = 0; 208 209 if (reapStatus != JNI_FALSE) { 210 /* Wait for the child process to exit. 211 * waitpid() is standard, so use it on all POSIX platforms. 212 * It is known to work when blocking to wait for the pid 213 * This returns immediately if the child has already exited. 214 */ 215 int status; 216 while (waitpid(pid, &status, 0) < 0) { 217 switch (errno) { 218 case ECHILD: return 0; 219 case EINTR: break; 220 default: return -1; 221 } 222 } 223 224 if (WIFEXITED(status)) { 225 return WEXITSTATUS(status); 226 } else if (WIFSIGNALED(status)) { 227 return WTERMSIG_RETURN(status); 228 } else { 229 return status; 230 } 231 } else { 232 /* 233 * Wait for the child process to exit without reaping the exitValue. 234 * waitid() is standard on all POSIX platforms. 235 * Note: waitid on Mac OS X 10.7 seems to be broken; 236 * it does not return the exit status consistently. 237 */ 238 siginfo_t siginfo; 239 int options = WEXITED | WNOWAIT; 240 memset(&siginfo, 0, sizeof siginfo); 241 while (waitid(P_PID, pid, &siginfo, options) < 0) { 242 switch (errno) { 243 case ECHILD: return 0; 244 case EINTR: break; 245 default: return -1; 246 } 247 } 248 249 if (siginfo.si_code == CLD_EXITED) { 250 /* 251 * The child exited normally; get its exit code. 252 */ 253 return siginfo.si_status; 254 } else if (siginfo.si_code == CLD_KILLED || siginfo.si_code == CLD_DUMPED) { 255 return WTERMSIG_RETURN(siginfo.si_status); 256 } else { 257 /* 258 * Unknown exit code; pass it through. 259 */ 260 return siginfo.si_status; 261 } 262 } 263 } 264 265 /* 266 * Class: java_lang_ProcessHandleImpl 267 * Method: getCurrentPid0 268 * Signature: ()J 269 */ 270 JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_getCurrentPid0 271 (JNIEnv *env, jclass clazz) { 272 pid_t pid = getpid(); 273 return (jlong) pid; 274 } 275 276 /* 277 * Class: java_lang_ProcessHandleImpl 278 * Method: isAlive0 279 * Signature: (J)Z 280 */ 281 JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_isAlive0 282 (JNIEnv *env, jobject obj, jlong jpid) { 283 pid_t pid = (pid_t) jpid; 284 return (kill(pid, 0) < 0) ? JNI_FALSE : JNI_TRUE; 285 } 286 287 /* 288 * Class: java_lang_ProcessHandleImpl 289 * Method: destroy0 290 * Signature: (JZ)Z 291 */ 292 JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_destroy0 293 (JNIEnv *env, jobject obj, jlong jpid, jboolean force) { 294 pid_t pid = (pid_t) jpid; 295 int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM; 296 return (kill(pid, sig) >= 0); 297 298 } 299 300 /* 301 * Returns the children of the requested pid and optionally each parent. 302 * 303 * Class: java_lang_ProcessHandleImpl 304 * Method: getChildPids 305 * Signature: (J[J[J)I 306 */ 307 JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0 308 (JNIEnv *env, jclass clazz, jlong jpid, 309 jlongArray jarray, jlongArray jparentArray) { 310 return os_getChildren(env, jpid, jarray, jparentArray); 311 } 312 313 /* 314 * Fill in the Info object from the OS information about the process. 315 * 316 * Class: java_lang_ProcessHandleImpl_Info 317 * Method: info0 318 * Signature: (Ljava/lang/ProcessHandle/Info;J)I 319 */ 320 JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0 321 (JNIEnv *env, jobject jinfo, jlong jpid) { 322 pid_t pid = (pid_t) jpid; 323 os_getStatInfo(env, jinfo, pid); 324 os_getCmdlineInfo(env, jinfo, pid); 325 } 326 327 /* 328 * Returns the parent pid of the requested pid. 329 * 330 * Class: java_lang_ProcessHandleImpl 331 * Method: parent0 332 * Signature: (J)J 333 */ 334 JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0 335 (JNIEnv *env, jobject obj, jlong jpid) { 336 pid_t pid = (pid_t) jpid; 337 pid_t ppid = -1; 338 339 pid_t mypid = getpid(); 340 if (pid == mypid) { 341 ppid = getppid(); 342 } else { 343 ppid = os_parentPid(env, pid); 344 } 345 return (jlong) ppid; 346 } 347 348 /** 349 * Construct the argument array by parsing the arguments from the sequence 350 * of arguments. The zero'th arg is the command executable but it will only 351 * be used if the 'cmdexe' argument is NULL. 352 */ 353 int unix_fillArgArray(JNIEnv *env, jobject jinfo, int nargs, 354 char *cp, char *argsEnd, jstring cmdexe) { 355 jobject argsArray; 356 int i; 357 358 if (nargs < 1) { 359 return 0; 360 } 361 362 if (cmdexe == NULL) { 363 // Create a string from arg[0] 364 CHECK_NULL_RETURN((cmdexe = JNU_NewStringPlatform(env, cp)), -1); 365 } 366 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, cmdexe); 367 JNU_CHECK_EXCEPTION_RETURN(env, -3); 368 369 // Create a String array for nargs-1 elements 370 argsArray = (*env)->NewObjectArray(env, nargs - 1, JNU_ClassString(env), NULL); 371 CHECK_NULL_RETURN(argsArray, -1); 372 373 for (i = 0; i < nargs - 1; i++) { 374 jstring str = NULL; 375 376 cp += strlen(cp) + 1; 377 if (cp > argsEnd || *cp == '\0') { 378 return -2; // Off the end pointer or an empty argument is an error 379 } 380 381 CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1); 382 383 (*env)->SetObjectArrayElement(env, argsArray, i, str); 384 JNU_CHECK_EXCEPTION_RETURN(env, -3); 385 } 386 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray); 387 JNU_CHECK_EXCEPTION_RETURN(env, -4); 388 return 0; 389 } 390 391 /* Size of password or group entry when not available via sysconf */ 392 #define ENT_BUF_SIZE 1024 393 394 /** 395 * Return a strong username for the uid_t or null. 396 */ 397 jstring unix_uidToUser(JNIEnv* env, uid_t uid) { 398 int result = 0; 399 int buflen; 400 char* pwbuf; 401 jstring name = NULL; 402 403 /* allocate buffer for password record */ 404 buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX); 405 if (buflen == -1) 406 buflen = ENT_BUF_SIZE; 407 pwbuf = (char*)malloc(buflen); 408 if (pwbuf == NULL) { 409 JNU_ThrowOutOfMemoryError(env, "Unable to open getpwent"); 410 } else { 411 struct passwd pwent; 412 struct passwd* p = NULL; 413 414 #ifdef __solaris__ 415 RESTARTABLE_RETURN_PTR(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen), p); 416 #else 417 RESTARTABLE(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen, &p), result); 418 #endif 419 420 // Return the Java String if a name was found 421 if (result == 0 && p != NULL && 422 p->pw_name != NULL && *(p->pw_name) != '\0') { 423 name = JNU_NewStringPlatform(env, p->pw_name); 424 } 425 free(pwbuf); 426 } 427 return name; 428 } 429 430 /* 431 * The following fuctions are common on Solaris, Linux and AIX. 432 */ 433 434 #if defined(__solaris__) || defined (__linux__) || defined(_AIX) 435 436 /* 437 * Reads /proc and accumulates any process who parent pid matches. 438 * The resulting pids are stored into the array of longs. 439 * The number of pids is returned if they all fit. 440 * If the array is too short, the negative of the desired length is returned. 441 */ 442 jint unix_getChildren(JNIEnv *env, jlong jpid, 443 jlongArray jarray, jlongArray jparentArray) { 444 DIR* dir; 445 struct dirent* ptr; 446 pid_t pid = (pid_t) jpid; 447 pid_t ppid = 0; 448 size_t count = 0; 449 jlong* pids = NULL; 450 jlong* ppids = NULL; 451 size_t parentArraySize = 0; 452 size_t arraySize = 0; 453 454 arraySize = (*env)->GetArrayLength(env, jarray); 455 JNU_CHECK_EXCEPTION_RETURN(env, -1); 456 if (jparentArray != NULL) { 457 parentArraySize = (*env)->GetArrayLength(env, jparentArray); 458 JNU_CHECK_EXCEPTION_RETURN(env, -1); 459 460 if (arraySize != parentArraySize) { 461 JNU_ThrowIllegalArgumentException(env, "array sizes not equal"); 462 return 0; 463 } 464 } 465 466 /* 467 * To locate the children we scan /proc looking for files that have a 468 * position integer as a filename. 469 */ 470 if ((dir = opendir("/proc")) == NULL) { 471 JNU_ThrowByNameWithLastError(env, 472 "java/lang/Runtime", "Unable to open /proc"); 473 return -1; 474 } 475 476 do { // Block to break out of on Exception 477 pids = (*env)->GetLongArrayElements(env, jarray, NULL); 478 if (pids == NULL) { 479 break; 480 } 481 if (jparentArray != NULL) { 482 ppids = (*env)->GetLongArrayElements(env, jparentArray, NULL); 483 if (ppids == NULL) { 484 break; 485 } 486 } 487 488 while ((ptr = readdir(dir)) != NULL) { 489 /* skip files that aren't numbers */ 490 pid_t childpid = (pid_t) atoi(ptr->d_name); 491 if ((int) childpid <= 0) { 492 continue; 493 } 494 495 ppid = 0; 496 if (pid != 0 || jparentArray != NULL) { 497 // os_parentPid opens and reads /proc/pid/stat 498 ppid = os_parentPid(env, childpid); 499 } 500 if (pid == 0 || ppid == pid) { 501 if (count < arraySize) { 502 // Only store if it fits 503 pids[count] = (jlong) childpid; 504 505 if (ppids != NULL) { 506 // Store the parentPid 507 ppids[count] = (jlong) ppid; 508 } 509 } 510 count++; // Count to tabulate size needed 511 } 512 } 513 } while (0); 514 515 if (pids != NULL) { 516 (*env)->ReleaseLongArrayElements(env, jarray, pids, 0); 517 } 518 if (ppids != NULL) { 519 (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0); 520 } 521 522 closedir(dir); 523 // If more pids than array had size for; count will be greater than array size 524 return count; 525 } 526 527 #endif // defined(__solaris__) || defined (__linux__) || defined(_AIX) 528 529 /* 530 * The following fuctions are common on Solaris and AIX. 531 */ 532 533 #if defined(__solaris__) || defined(_AIX) 534 535 /** 536 * Read /proc/<pid>/stat and fill in the fields of the Info object. 537 * Gather the user and system times. 538 */ 539 void unix_getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid) { 540 FILE* fp; 541 pstatus_t pstatus; 542 struct stat stat_buf; 543 int ret; 544 char fn[32]; 545 int i, p; 546 char* s; 547 jlong totalTime; 548 549 /* 550 * Try to open /proc/%d/status 551 */ 552 snprintf(fn, sizeof fn, "/proc/%d/status", pid); 553 554 if (stat(fn, &stat_buf) < 0) { 555 return; 556 } 557 558 fp = fopen(fn, "r"); 559 if (fp == NULL) { 560 return; 561 } 562 563 ret = fread(&pstatus, 1, (sizeof pstatus), fp); 564 fclose(fp); 565 if (ret < 0) { 566 return; 567 } 568 569 totalTime = pstatus.pr_utime.tv_sec * 1000000000L + pstatus.pr_utime.tv_nsec + 570 pstatus.pr_stime.tv_sec * 1000000000L + pstatus.pr_stime.tv_nsec; 571 572 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime); 573 JNU_CHECK_EXCEPTION(env); 574 } 575 576 void unix_getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) { 577 FILE* fp; 578 psinfo_t psinfo; 579 int ret; 580 char fn[32]; 581 char exePath[PATH_MAX]; 582 jlong startTime; 583 jstring str = NULL, cmdexe = NULL; 584 585 /* 586 * try to open /proc/%d/psinfo 587 */ 588 snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid); 589 fp = fopen(fn, "r"); 590 if (fp == NULL) { 591 return; 592 } 593 594 ret = fread(&psinfo, 1, (sizeof psinfo), fp); 595 fclose(fp); 596 if (ret < 0) { 597 return; 598 } 599 600 CHECK_NULL((str = unix_uidToUser(env, psinfo.pr_uid))); 601 (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, str); 602 JNU_CHECK_EXCEPTION(env); 603 604 startTime = (jlong)psinfo.pr_start.tv_sec * (jlong)1000 + 605 (jlong)psinfo.pr_start.tv_nsec / 1000000; 606 (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime); 607 JNU_CHECK_EXCEPTION(env); 608 609 /* 610 * On Solris, the full path to the executable command is the link in 611 * /proc/<pid>/paths/a.out. But it is only readable for processes we own. 612 * On AIX, the readlink() call will simply fail because the link doesn't exist. 613 */ 614 snprintf(fn, sizeof fn, "/proc/%d/path/a.out", pid); 615 if ((ret = readlink(fn, exePath, PATH_MAX - 1)) > 0) { 616 // null terminate and create String to store for command 617 exePath[ret] = '\0'; 618 CHECK_NULL(cmdexe = JNU_NewStringPlatform(env, exePath)); 619 } 620 621 /* 622 * Now read psinfo.pr_psargs which contains the first PRARGSZ characters of the 623 * argument list (i.e. arg[0] arg[1] ...). Unfortunately, PRARGSZ is usually set 624 * to 80 characters only. Nevertheless it's better than nothing :) 625 */ 626 { 627 char prargs[PRARGSZ + 1]; 628 char *cp, *argsEnd; 629 int nargs = 1; 630 strncpy(prargs, psinfo.pr_psargs, PRARGSZ); 631 prargs[PRARGSZ] = '\0'; 632 argsEnd = &prargs[strlen(prargs)]; 633 if (argsEnd == prargs) { 634 /* If psinfo.pr_psargs didn't contain any strings, use psinfo.pr_fname 635 * (which only contains the last component of exec()ed pathname) as a 636 * last resort. This is true for AIX kernel processes for example. 637 */ 638 strncpy(prargs, psinfo.pr_fname, PRARGSZ); 639 prargs[PRARGSZ] = '\0'; 640 argsEnd = &prargs[strlen(prargs)]; 641 } else { 642 /* The arguments are separeted by a single whitespace character but we want 643 * them to be distinct, null-terminated strings. */ 644 for (cp = prargs; *cp != '\0'; cp++) { 645 if (*cp == ' ') { 646 *cp = '\0'; 647 nargs++; 648 } 649 } 650 } 651 unix_fillArgArray(env, jinfo, nargs, prargs, argsEnd, cmdexe); 652 } 653 } 654 655 #endif // defined(__solaris__) || defined(_AIX)