jdk/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c
changeset 32209 24bb680a1609
parent 31682 c19dcf5e0b6d
child 40179 b5d59a6f093a
child 40182 1e16a8fd76ba
equal deleted inserted replaced
32167:c8753d0be177 32209:24bb680a1609
    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 
    65 #define WIFSIGNALED(status) (((status)&0xFF) > 0 && ((status)&0xFF00) == 0)
   121 #define WIFSIGNALED(status) (((status)&0xFF) > 0 && ((status)&0xFF00) == 0)
    66 #endif
   122 #endif
    67 
   123 
    68 #ifndef WTERMSIG
   124 #ifndef WTERMSIG
    69 #define WTERMSIG(status) ((status)&0x7F)
   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)
    70 #endif
   139 #endif
    71 
   140 
    72 #define RESTARTABLE(_cmd, _result) do { \
   141 #define RESTARTABLE(_cmd, _result) do { \
    73   do { \
   142   do { \
    74     _result = _cmd; \
   143     _result = _cmd; \
    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.
   115         }
   246         }
   116 
   247 
   117         if (WIFEXITED(status)) {
   248         if (WIFEXITED(status)) {
   118             return WEXITSTATUS(status);
   249             return WEXITSTATUS(status);
   119         } else if (WIFSIGNALED(status)) {
   250         } else if (WIFSIGNALED(status)) {
   120             /* The child exited because of a signal.
   251             return WTERMSIG_RETURN(status);
   121              * The best value to return is 0x80 + signal number,
       
   122              * because that is what all Unix shells do, and because
       
   123              * it allows callers to distinguish between process exit and
       
   124              * process death by signal.
       
   125              * Unfortunately, the historical behavior on Solaris is to return
       
   126              * the signal number, and we preserve this for compatibility. */
       
   127 #ifdef __solaris__
       
   128             return WTERMSIG(status);
       
   129 #else
       
   130             return 0x80 + WTERMSIG(status);
       
   131 #endif
       
   132         } else {
   252         } else {
   133             return status;
   253             return status;
   134         }
   254         }
   135      } else {
   255      } else {
   136         /*
   256         /*
   154              /*
   274              /*
   155               * The child exited normally; get its exit code.
   275               * The child exited normally; get its exit code.
   156               */
   276               */
   157              return siginfo.si_status;
   277              return siginfo.si_status;
   158         } else if (siginfo.si_code == CLD_KILLED || siginfo.si_code == CLD_DUMPED) {
   278         } else if (siginfo.si_code == CLD_KILLED || siginfo.si_code == CLD_DUMPED) {
   159              /* The child exited because of a signal.
   279              return WTERMSIG_RETURN(siginfo.si_status);
   160               * The best value to return is 0x80 + signal number,
       
   161               * because that is what all Unix shells do, and because
       
   162               * it allows callers to distinguish between process exit and
       
   163               * process death by signal.
       
   164               * Unfortunately, the historical behavior on Solaris is to return
       
   165               * the signal number, and we preserve this for compatibility. */
       
   166  #ifdef __solaris__
       
   167              return WTERMSIG(siginfo.si_status);
       
   168  #else
       
   169              return 0x80 + WTERMSIG(siginfo.si_status);
       
   170  #endif
       
   171         } else {
   280         } else {
   172              /*
   281              /*
   173               * Unknown exit code; pass it through.
   282               * Unknown exit code; pass it through.
   174               */
   283               */
   175              return siginfo.si_status;
   284              return siginfo.si_status;
   189 }
   298 }
   190 
   299 
   191 /*
   300 /*
   192  * Class:     java_lang_ProcessHandleImpl
   301  * Class:     java_lang_ProcessHandleImpl
   193  * Method:    destroy0
   302  * Method:    destroy0
   194  * Signature: (Z)Z
   303  * Signature: (JJZ)Z
   195  */
   304  */
   196 JNIEXPORT jboolean JNICALL
   305 JNIEXPORT jboolean JNICALL
   197 Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env,
   306 Java_java_lang_ProcessHandleImpl_destroy0(JNIEnv *env,
   198                                           jobject obj,
   307                                           jobject obj,
   199                                           jlong jpid,
   308                                           jlong jpid,
   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;
   460             /* skip files that aren't numbers */
   571             /* skip files that aren't numbers */
   461             pid_t childpid = (pid_t) atoi(ptr->d_name);
   572             pid_t childpid = (pid_t) atoi(ptr->d_name);
   462             if ((int) childpid <= 0) {
   573             if ((int) childpid <= 0) {
   463                 continue;
   574                 continue;
   464             }
   575             }
   465             // Read /proc/pid/stat and get the parent pid, and start time
   576 
   466             ppid = getStatInfo(env, childpid, &totalTime, &startTime);
   577             // Get the parent pid, and start time
   467             if (ppid > 0 && (pid == 0 || ppid == pid)) {
   578             ppid = os_getParentPidAndTimings(env, childpid, &totalTime, &startTime);
       
   579             if (ppid >= 0 && (pid == 0 || ppid == pid)) {
   468                 if (count < arraySize) {
   580                 if (count < arraySize) {
   469                     // Only store if it fits
   581                     // Only store if it fits
   470                     pids[count] = (jlong) childpid;
   582                     pids[count] = (jlong) childpid;
   471 
   583 
   472                     if (ppids != NULL) {
   584                     if (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