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