1 /*
   2  * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright 2013 SAP AG. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.  Oracle designates this
   9  * particular file as subject to the "Classpath" exception as provided
  10  * by Oracle in the LICENSE file that accompanied this code.
  11  *
  12  * This code is distributed in the hope that it will be useful, but WITHOUT
  13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15  * version 2 for more details (a copy is included in the LICENSE file that
  16  * accompanied this code).
  17  *
  18  * You should have received a copy of the GNU General Public License version
  19  * 2 along with this work; if not, write to the Free Software Foundation,
  20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  21  *
  22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  23  * or visit www.oracle.com if you need additional information or have any
  24  * questions.
  25  */
  26 
  27 #include "jni.h"
  28 #include "jni_util.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/socket.h>
  40 #include <sys/stat.h>
  41 #include <sys/un.h>
  42 
  43 /*
  44  * Based on 'LinuxVirtualMachine.c'. Non-relevant code has been removed and all
  45  * occurrences of the string "Linux" have been replaced by "Aix".
  46  */
  47 
  48 #include "sun_tools_attach_AixVirtualMachine.h"
  49 
  50 #define RESTARTABLE(_cmd, _result) do { \
  51   do { \
  52     _result = _cmd; \
  53   } while((_result == -1) && (errno == EINTR)); \
  54 } while(0)
  55 
  56 
  57 /*
  58  * Class:     sun_tools_attach_AixVirtualMachine
  59  * Method:    socket
  60  * Signature: ()I
  61  */
  62 JNIEXPORT jint JNICALL Java_sun_tools_attach_AixVirtualMachine_socket
  63   (JNIEnv *env, jclass cls)
  64 {
  65     int fd = socket(PF_UNIX, SOCK_STREAM, 0);
  66     if (fd == -1) {
  67         JNU_ThrowIOExceptionWithLastError(env, "socket");
  68     }
  69     /* added time out values */
  70     else {
  71         struct timeval tv;
  72         tv.tv_sec = 2 * 60;
  73         tv.tv_usec = 0;
  74 
  75         setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv));
  76         setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv));
  77     }
  78     return (jint)fd;
  79 }
  80 
  81 /*
  82  * Class:     sun_tools_attach_AixVirtualMachine
  83  * Method:    connect
  84  * Signature: (ILjava/lang/String;)I
  85  */
  86 JNIEXPORT void JNICALL Java_sun_tools_attach_AixVirtualMachine_connect
  87   (JNIEnv *env, jclass cls, jint fd, jstring path)
  88 {
  89     jboolean isCopy;
  90     const char* p = GetStringPlatformChars(env, path, &isCopy);
  91     if (p != NULL) {
  92         struct sockaddr_un addr;
  93         int err = 0;
  94 
  95         /* added missing structure initialization */
  96         memset(&addr,0, sizeof(addr));
  97         addr.sun_family = AF_UNIX;
  98         strcpy(addr.sun_path, p);
  99         /* We must call bind with the actual socketaddr length. This is obligatory for AS400. */
 100         if (connect(fd, (struct sockaddr*)&addr, SUN_LEN(&addr)) == -1) {
 101             err = errno;
 102         }
 103 
 104         if (isCopy) {
 105             JNU_ReleaseStringPlatformChars(env, path, p);
 106         }
 107 
 108         /*
 109          * If the connect failed then we throw the appropriate exception
 110          * here (can't throw it before releasing the string as can't call
 111          * JNI with pending exception)
 112          */
 113         if (err != 0) {
 114             if (err == ENOENT) {
 115                 JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
 116             } else {
 117                 char* msg = strdup(strerror(err));
 118                 JNU_ThrowIOException(env, msg);
 119                 if (msg != NULL) {
 120                     free(msg);
 121                 }
 122             }
 123         }
 124     }
 125 }
 126 
 127 
 128 /*
 129  * Structure and callback function used to send a QUIT signal to all
 130  * children of a given process
 131  */
 132 typedef struct {
 133     pid_t ppid;
 134 } SendQuitContext;
 135 
 136 static void SendQuitCallback(const pid_t pid, void* user_data) {
 137     SendQuitContext* context = (SendQuitContext*)user_data;
 138     pid_t parent = getParent(pid);
 139     if (parent == context->ppid) {
 140         kill(pid, SIGQUIT);
 141     }
 142 }
 143 
 144 /*
 145  * Class:     sun_tools_attach_AixVirtualMachine
 146  * Method:    sendQuitTo
 147  * Signature: (I)V
 148  */
 149 JNIEXPORT void JNICALL Java_sun_tools_attach_AixVirtualMachine_sendQuitTo
 150   (JNIEnv *env, jclass cls, jint pid)
 151 {
 152     if (kill((pid_t)pid, SIGQUIT)) {
 153         JNU_ThrowIOExceptionWithLastError(env, "kill");
 154     }
 155 }
 156 
 157 /*
 158  * Class:     sun_tools_attach_AixVirtualMachine
 159  * Method:    checkPermissions
 160  * Signature: (Ljava/lang/String;)V
 161  */
 162 JNIEXPORT void JNICALL Java_sun_tools_attach_AixVirtualMachine_checkPermissions
 163   (JNIEnv *env, jclass cls, jstring path)
 164 {
 165     jboolean isCopy;
 166     const char* p = GetStringPlatformChars(env, path, &isCopy);
 167     if (p != NULL) {
 168         struct stat64 sb;
 169         uid_t uid, gid;
 170         int res;
 171         /* added missing initialization of the stat64 buffer */
 172         memset(&sb, 0, sizeof(struct stat64));
 173 
 174         /*
 175          * Check that the path is owned by the effective uid/gid of this
 176          * process. Also check that group/other access is not allowed.
 177          */
 178         uid = geteuid();
 179         gid = getegid();
 180 
 181         res = stat64(p, &sb);
 182         if (res != 0) {
 183             /* save errno */
 184             res = errno;
 185         }
 186 
 187         /* release p here before we throw an I/O exception */
 188         if (isCopy) {
 189             JNU_ReleaseStringPlatformChars(env, path, p);
 190         }
 191 
 192         if (res == 0) {
 193             if ( (sb.st_uid != uid) || (sb.st_gid != gid) ||
 194                  ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) ) {
 195                 JNU_ThrowIOException(env, "well-known file is not secure");
 196             }
 197         } else {
 198             char* msg = strdup(strerror(res));
 199             JNU_ThrowIOException(env, msg);
 200             if (msg != NULL) {
 201                 free(msg);
 202             }
 203         }
 204     }
 205 }
 206 
 207 /*
 208  * Class:     sun_tools_attach_AixVirtualMachine
 209  * Method:    close
 210  * Signature: (I)V
 211  */
 212 JNIEXPORT void JNICALL Java_sun_tools_attach_AixVirtualMachine_close
 213   (JNIEnv *env, jclass cls, jint fd)
 214 {
 215     int res;
 216     /* Fixed deadlock when this call of close by the client is not seen by the attach server
 217      * which has accepted the (very short) connection already and is waiting for the request. But read don't get a byte,
 218      * because the close is lost without shutdown.
 219      */
 220     shutdown(fd, 2);
 221     RESTARTABLE(close(fd), res);
 222 }
 223 
 224 /*
 225  * Class:     sun_tools_attach_AixVirtualMachine
 226  * Method:    read
 227  * Signature: (I[BI)I
 228  */
 229 JNIEXPORT jint JNICALL Java_sun_tools_attach_AixVirtualMachine_read
 230   (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
 231 {
 232     unsigned char buf[128];
 233     size_t len = sizeof(buf);
 234     ssize_t n;
 235 
 236     size_t remaining = (size_t)(baLen - off);
 237     if (len > remaining) {
 238         len = remaining;
 239     }
 240 
 241     RESTARTABLE(read(fd, buf+off, len), n);
 242     if (n == -1) {
 243         JNU_ThrowIOExceptionWithLastError(env, "read");
 244     } else {
 245         if (n == 0) {
 246             n = -1;     // EOF
 247         } else {
 248             (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf+off));
 249         }
 250     }
 251     return n;
 252 }
 253 
 254 /*
 255  * Class:     sun_tools_attach_AixVirtualMachine
 256  * Method:    write
 257  * Signature: (I[B)V
 258  */
 259 JNIEXPORT void JNICALL Java_sun_tools_attach_AixVirtualMachine_write
 260   (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen)
 261 {
 262     size_t remaining = bufLen;
 263     do {
 264         unsigned char buf[128];
 265         size_t len = sizeof(buf);
 266         int n;
 267 
 268         if (len > remaining) {
 269             len = remaining;
 270         }
 271         (*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf);
 272 
 273         RESTARTABLE(write(fd, buf, len), n);
 274         if (n > 0) {
 275             off += n;
 276             remaining -= n;
 277         } else {
 278             JNU_ThrowIOExceptionWithLastError(env, "write");
 279             return;
 280         }
 281 
 282     } while (remaining > 0);
 283 }