1 /*
   2  * Copyright (c) 2005, 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 "jvm.h"
  29 
  30 #include <stdio.h>
  31 #include <stdlib.h>
  32 #include <string.h>
  33 #include <errno.h>
  34 #include <unistd.h>
  35 #include <signal.h>
  36 #include <dirent.h>
  37 #include <ctype.h>
  38 #include <sys/types.h>
  39 #include <sys/types.h>
  40 #include <sys/socket.h>
  41 #include <sys/stat.h>
  42 #include <sys/un.h>
  43 
  44 #include "sun_tools_attach_VirtualMachineImpl.h"
  45 
  46 #define RESTARTABLE(_cmd, _result) do { \
  47   do { \
  48     _result = _cmd; \
  49   } while((_result == -1) && (errno == EINTR)); \
  50 } while(0)
  51 
  52 /*
  53  * Declare library specific JNI_Onload entry if static build
  54  */
  55 DEF_STATIC_JNI_OnLoad
  56 
  57 /*
  58  * Defines a callback that is invoked for each process
  59  */
  60 typedef void (*ProcessCallback)(const pid_t pid, void* user_data);
  61 
  62 /*
  63  * Invokes the callback function for each process
  64  */
  65 static void forEachProcess(ProcessCallback f, void* user_data) {
  66     DIR* dir;
  67     struct dirent* ptr;
  68 
  69     /*
  70      * To locate the children we scan /proc looking for files that have a
  71      * position integer as a filename.
  72      */
  73     if ((dir = opendir("/proc")) == NULL) {
  74         return;
  75     }
  76     while ((ptr = readdir(dir)) != NULL) {
  77         pid_t pid;
  78 
  79         /* skip current/parent directories */
  80         if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {
  81             continue;
  82         }
  83 
  84         /* skip files that aren't numbers */
  85         pid = (pid_t)atoi(ptr->d_name);
  86         if ((int)pid <= 0) {
  87             continue;
  88         }
  89 
  90         /* invoke the callback */
  91         (*f)(pid, user_data);
  92     }
  93     closedir(dir);
  94 }
  95 
  96 
  97 /*
  98  * Returns the parent pid of a given pid, or -1 if not found
  99  */
 100 static pid_t getParent(pid_t pid) {
 101     char state;
 102     FILE* fp;
 103     char stat[2048];
 104     int statlen;
 105     char fn[32];
 106     int i, p;
 107     char* s;
 108 
 109     /*
 110      * try to open /proc/%d/stat
 111      */
 112     sprintf(fn, "/proc/%d/stat", pid);
 113     fp = fopen(fn, "r");
 114     if (fp == NULL) {
 115         return -1;
 116     }
 117 
 118     /*
 119      * The format is: pid (command) state ppid ...
 120      * As the command could be anything we must find the right most
 121      * ")" and then skip the white spaces that follow it.
 122      */
 123     statlen = fread(stat, 1, 2047, fp);
 124     stat[statlen] = '\0';
 125     fclose(fp);
 126     s = strrchr(stat, ')');
 127     if (s == NULL) {
 128         return -1;
 129     }
 130     do s++; while (isspace(*s));
 131     i = sscanf(s, "%c %d", &state, &p);
 132     return (pid_t)p;
 133 }
 134 
 135 
 136 /*
 137  * Class:     sun_tools_attach_VirtualMachineImpl
 138  * Method:    socket
 139  * Signature: ()I
 140  */
 141 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_socket
 142   (JNIEnv *env, jclass cls)
 143 {
 144     int fd = socket(PF_UNIX, SOCK_STREAM, 0);
 145     if (fd == -1) {
 146         JNU_ThrowIOExceptionWithLastError(env, "socket");
 147     }
 148     return (jint)fd;
 149 }
 150 
 151 /*
 152  * Class:     sun_tools_attach_VirtualMachineImpl
 153  * Method:    connect
 154  * Signature: (ILjava/lang/String;)I
 155  */
 156 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connect
 157   (JNIEnv *env, jclass cls, jint fd, jstring path)
 158 {
 159     jboolean isCopy;
 160     const char* p = GetStringPlatformChars(env, path, &isCopy);
 161     if (p != NULL) {
 162         struct sockaddr_un addr;
 163         int err = 0;
 164 
 165         memset(&addr, 0, sizeof(addr));
 166         addr.sun_family = AF_UNIX;
 167         /* strncpy is safe because addr.sun_path was zero-initialized before. */
 168         strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1);
 169 
 170         if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
 171             err = errno;
 172         }
 173 
 174         if (isCopy) {
 175             JNU_ReleaseStringPlatformChars(env, path, p);
 176         }
 177 
 178         /*
 179          * If the connect failed then we throw the appropriate exception
 180          * here (can't throw it before releasing the string as can't call
 181          * JNI with pending exception)
 182          */
 183         if (err != 0) {
 184             if (err == ENOENT) {
 185                 JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
 186             } else {
 187                 char* msg = strdup(strerror(err));
 188                 JNU_ThrowIOException(env, msg);
 189                 if (msg != NULL) {
 190                     free(msg);
 191                 }
 192             }
 193         }
 194     }
 195 }
 196 
 197 /*
 198  * Class:     sun_tools_attach_VirtualMachineImpl
 199  * Method:    isLinuxThreads
 200  * Signature: ()V
 201  */
 202 JNIEXPORT jboolean JNICALL Java_sun_tools_attach_VirtualMachineImpl_isLinuxThreads
 203   (JNIEnv *env, jclass cls)
 204 {
 205 # ifndef _CS_GNU_LIBPTHREAD_VERSION
 206 # define _CS_GNU_LIBPTHREAD_VERSION 3
 207 # endif
 208     size_t n;
 209     char* s;
 210     jboolean res;
 211 
 212     n = confstr(_CS_GNU_LIBPTHREAD_VERSION, NULL, 0);
 213     if (n <= 0) {
 214        /* glibc before 2.3.2 only has LinuxThreads */
 215        return JNI_TRUE;
 216     }
 217 
 218     s = (char *)malloc(n);
 219     if (s == NULL) {
 220         JNU_ThrowOutOfMemoryError(env, "malloc failed");
 221         return JNI_TRUE;
 222     }
 223     confstr(_CS_GNU_LIBPTHREAD_VERSION, s, n);
 224 
 225     /*
 226      * If the LIBPTHREAD version include "NPTL" then we know we
 227      * have the new threads library and not LinuxThreads
 228      */
 229     res = (jboolean)(strstr(s, "NPTL") == NULL);
 230     free(s);
 231     return res;
 232 }
 233 
 234 /*
 235  * Structure and callback function used to count the children of
 236  * a given process, and record the pid of the "manager thread".
 237  */
 238 typedef struct {
 239     pid_t ppid;
 240     int count;
 241     pid_t mpid;
 242 } ChildCountContext;
 243 
 244 static void ChildCountCallback(const pid_t pid, void* user_data) {
 245     ChildCountContext* context = (ChildCountContext*)user_data;
 246     if (getParent(pid) == context->ppid) {
 247         context->count++;
 248         /*
 249          * Remember the pid of the first child. If the final count is
 250          * one then this is the pid of the LinuxThreads manager.
 251          */
 252         if (context->count == 1) {
 253             context->mpid = pid;
 254         }
 255     }
 256 }
 257 
 258 /*
 259  * Class:     sun_tools_attach_VirtualMachineImpl
 260  * Method:    getLinuxThreadsManager
 261  * Signature: (I)I
 262  */
 263 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_getLinuxThreadsManager
 264   (JNIEnv *env, jclass cls, jint pid)
 265 {
 266     ChildCountContext context;
 267 
 268     /*
 269      * Iterate over all processes to find how many children 'pid' has
 270      */
 271     context.ppid = pid;
 272     context.count = 0;
 273     context.mpid = (pid_t)0;
 274     forEachProcess(ChildCountCallback, (void*)&context);
 275 
 276     /*
 277      * If there's no children then this is likely the pid of the primordial
 278      * created by the launcher - in that case the LinuxThreads manager is the
 279      * parent of this process.
 280      */
 281     if (context.count == 0) {
 282         pid_t parent = getParent(pid);
 283         if ((int)parent > 0) {
 284             return (jint)parent;
 285         }
 286     }
 287 
 288     /*
 289      * There's one child so this is likely the embedded VM case where the
 290      * the primordial thread == LinuxThreads initial thread. The LinuxThreads
 291      * manager in that case is the child.
 292      */
 293     if (context.count == 1) {
 294         return (jint)context.mpid;
 295     }
 296 
 297     /*
 298      * If we get here it's most likely we were given the wrong pid
 299      */
 300     JNU_ThrowIOException(env, "Unable to get pid of LinuxThreads manager thread");
 301     return -1;
 302 }
 303 
 304 /*
 305  * Structure and callback function used to send a QUIT signal to all
 306  * children of a given process
 307  */
 308 typedef struct {
 309     pid_t ppid;
 310 } SendQuitContext;
 311 
 312 static void SendQuitCallback(const pid_t pid, void* user_data) {
 313     SendQuitContext* context = (SendQuitContext*)user_data;
 314     pid_t parent = getParent(pid);
 315     if (parent == context->ppid) {
 316         kill(pid, SIGQUIT);
 317     }
 318 }
 319 
 320 /*
 321  * Class:     sun_tools_attach_VirtualMachineImpl
 322  * Method:    sendQuitToChildrenOf
 323  * Signature: (I)V
 324  */
 325 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_sendQuitToChildrenOf
 326   (JNIEnv *env, jclass cls, jint pid)
 327 {
 328     SendQuitContext context;
 329     context.ppid = (pid_t)pid;
 330 
 331     /*
 332      * Iterate over all children of 'pid' and send a QUIT signal to each.
 333      */
 334     forEachProcess(SendQuitCallback, (void*)&context);
 335 }
 336 
 337 /*
 338  * Class:     sun_tools_attach_VirtualMachineImpl
 339  * Method:    sendQuitTo
 340  * Signature: (I)V
 341  */
 342 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_sendQuitTo
 343   (JNIEnv *env, jclass cls, jint pid)
 344 {
 345     if (kill((pid_t)pid, SIGQUIT)) {
 346         JNU_ThrowIOExceptionWithLastError(env, "kill");
 347     }
 348 }
 349 
 350 /*
 351  * Class:     sun_tools_attach_VirtualMachineImpl
 352  * Method:    checkPermissions
 353  * Signature: (Ljava/lang/String;)V
 354  */
 355 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions
 356   (JNIEnv *env, jclass cls, jstring path)
 357 {
 358     jboolean isCopy;
 359     const char* p = GetStringPlatformChars(env, path, &isCopy);
 360     if (p != NULL) {
 361         struct stat64 sb;
 362         uid_t uid, gid;
 363         int res;
 364 
 365         /*
 366          * Check that the path is owned by the effective uid/gid of this
 367          * process. Also check that group/other access is not allowed.
 368          */
 369         uid = geteuid();
 370         gid = getegid();
 371 
 372         res = stat64(p, &sb);
 373         if (res != 0) {
 374             /* save errno */
 375             res = errno;
 376         }
 377 
 378         if (res == 0) {
 379             char msg[100];
 380             jboolean isError = JNI_FALSE;
 381             if (sb.st_uid != uid) {
 382                 jio_snprintf(msg, sizeof(msg)-1,
 383                     "file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid);
 384                 isError = JNI_TRUE;
 385             } else if (sb.st_gid != gid) {
 386                 jio_snprintf(msg, sizeof(msg)-1,
 387                     "file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid);
 388                 isError = JNI_TRUE;
 389             } else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) {
 390                 jio_snprintf(msg, sizeof(msg)-1,
 391                     "file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777);
 392                 isError = JNI_TRUE;
 393             }
 394             if (isError) {
 395                 char buf[256];
 396                 jio_snprintf(buf, sizeof(buf)-1, "well-known file %s is not secure: %s", p, msg);
 397                 JNU_ThrowIOException(env, buf);
 398             }
 399         } else {
 400             char* msg = strdup(strerror(res));
 401             JNU_ThrowIOException(env, msg);
 402             if (msg != NULL) {
 403                 free(msg);
 404             }
 405         }
 406 
 407         if (isCopy) {
 408             JNU_ReleaseStringPlatformChars(env, path, p);
 409         }
 410     }
 411 }
 412 
 413 /*
 414  * Class:     sun_tools_attach_VirtualMachineImpl
 415  * Method:    close
 416  * Signature: (I)V
 417  */
 418 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_close
 419   (JNIEnv *env, jclass cls, jint fd)
 420 {
 421     int res;
 422     RESTARTABLE(close(fd), res);
 423 }
 424 
 425 /*
 426  * Class:     sun_tools_attach_VirtualMachineImpl
 427  * Method:    read
 428  * Signature: (I[BI)I
 429  */
 430 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_read
 431   (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
 432 {
 433     unsigned char buf[128];
 434     size_t len = sizeof(buf);
 435     ssize_t n;
 436 
 437     size_t remaining = (size_t)(baLen - off);
 438     if (len > remaining) {
 439         len = remaining;
 440     }
 441 
 442     RESTARTABLE(read(fd, buf, len), n);
 443     if (n == -1) {
 444         JNU_ThrowIOExceptionWithLastError(env, "read");
 445     } else {
 446         if (n == 0) {
 447             n = -1;     // EOF
 448         } else {
 449             (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf));
 450         }
 451     }
 452     return n;
 453 }
 454 
 455 /*
 456  * Class:     sun_tools_attach_VirtualMachineImpl
 457  * Method:    write
 458  * Signature: (I[B)V
 459  */
 460 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_write
 461   (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen)
 462 {
 463     size_t remaining = bufLen;
 464     do {
 465         unsigned char buf[128];
 466         size_t len = sizeof(buf);
 467         int n;
 468 
 469         if (len > remaining) {
 470             len = remaining;
 471         }
 472         (*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf);
 473 
 474         RESTARTABLE(write(fd, buf, len), n);
 475         if (n > 0) {
 476            off += n;
 477            remaining -= n;
 478         } else {
 479             JNU_ThrowIOExceptionWithLastError(env, "write");
 480             return;
 481         }
 482 
 483     } while (remaining > 0);
 484 }