1 /* 2 * Copyright (c) 2005, 2012, 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 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <errno.h> 33 #include <unistd.h> 34 #include <signal.h> 35 #include <dirent.h> 36 #include <ctype.h> 37 #include <sys/types.h> 38 #include <sys/socket.h> 39 #include <sys/stat.h> 40 #include <sys/syslimits.h> 41 #include <sys/un.h> 42 #include <fcntl.h> 43 44 #include "sun_tools_attach_BsdVirtualMachine.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 * Class: sun_tools_attach_BsdVirtualMachine 54 * Method: socket 55 * Signature: ()I 56 */ 57 JNIEXPORT jint JNICALL Java_sun_tools_attach_BsdVirtualMachine_socket 58 (JNIEnv *env, jclass cls) 59 { 60 int fd = socket(PF_UNIX, SOCK_STREAM, 0); 61 if (fd == -1) { 62 JNU_ThrowIOExceptionWithLastError(env, "socket"); 63 } 64 return (jint)fd; 65 } 66 67 /* 68 * Class: sun_tools_attach_BsdVirtualMachine 69 * Method: connect 70 * Signature: (ILjava/lang/String;)I 71 */ 72 JNIEXPORT void JNICALL Java_sun_tools_attach_BsdVirtualMachine_connect 73 (JNIEnv *env, jclass cls, jint fd, jstring path) 74 { 75 jboolean isCopy; 76 const char* p = GetStringPlatformChars(env, path, &isCopy); 77 if (p != NULL) { 78 struct sockaddr_un addr; 79 int err = 0; 80 81 memset(&addr, 0, sizeof(addr)); 82 addr.sun_family = AF_UNIX; 83 /* strncpy is safe because addr.sun_path was zero-initialized before. */ 84 strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1); 85 86 if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { 87 err = errno; 88 } 89 90 if (isCopy) { 91 JNU_ReleaseStringPlatformChars(env, path, p); 92 } 93 94 /* 95 * If the connect failed then we throw the appropriate exception 96 * here (can't throw it before releasing the string as can't call 97 * JNI with pending exception) 98 */ 99 if (err != 0) { 100 if (err == ENOENT) { 101 JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL); 102 } else { 103 char* msg = strdup(strerror(err)); 104 JNU_ThrowIOException(env, msg); 105 if (msg != NULL) { 106 free(msg); 107 } 108 } 109 } 110 } 111 } 112 113 /* 114 * Class: sun_tools_attach_BsdVirtualMachine 115 * Method: sendQuitTo 116 * Signature: (I)V 117 */ 118 JNIEXPORT void JNICALL Java_sun_tools_attach_BsdVirtualMachine_sendQuitTo 119 (JNIEnv *env, jclass cls, jint pid) 120 { 121 if (kill((pid_t)pid, SIGQUIT)) { 122 JNU_ThrowIOExceptionWithLastError(env, "kill"); 123 } 124 } 125 126 /* 127 * Class: sun_tools_attach_BsdVirtualMachine 128 * Method: checkPermissions 129 * Signature: (Ljava/lang/String;)V 130 */ 131 JNIEXPORT void JNICALL Java_sun_tools_attach_BsdVirtualMachine_checkPermissions 132 (JNIEnv *env, jclass cls, jstring path) 133 { 134 jboolean isCopy; 135 const char* p = GetStringPlatformChars(env, path, &isCopy); 136 if (p != NULL) { 137 struct stat sb; 138 uid_t uid, gid; 139 int res; 140 141 /* 142 * Check that the path is owned by the effective uid/gid of this 143 * process. Also check that group/other access is not allowed. 144 */ 145 uid = geteuid(); 146 gid = getegid(); 147 148 res = stat(p, &sb); 149 if (res != 0) { 150 /* save errno */ 151 res = errno; 152 } 153 154 /* release p here before we throw an I/O exception */ 155 if (isCopy) { 156 JNU_ReleaseStringPlatformChars(env, path, p); 157 } 158 159 if (res == 0) { 160 if ( (sb.st_uid != uid) || (sb.st_gid != gid) || 161 ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) ) { 162 JNU_ThrowIOException(env, "well-known file is not secure"); 163 } 164 } else { 165 char* msg = strdup(strerror(res)); 166 JNU_ThrowIOException(env, msg); 167 if (msg != NULL) { 168 free(msg); 169 } 170 } 171 } 172 } 173 174 /* 175 * Class: sun_tools_attach_BsdVirtualMachine 176 * Method: close 177 * Signature: (I)V 178 */ 179 JNIEXPORT void JNICALL Java_sun_tools_attach_BsdVirtualMachine_close 180 (JNIEnv *env, jclass cls, jint fd) 181 { 182 int res; 183 RESTARTABLE(close(fd), res); 184 } 185 186 /* 187 * Class: sun_tools_attach_BsdVirtualMachine 188 * Method: read 189 * Signature: (I[BI)I 190 */ 191 JNIEXPORT jint JNICALL Java_sun_tools_attach_BsdVirtualMachine_read 192 (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen) 193 { 194 unsigned char buf[128]; 195 size_t len = sizeof(buf); 196 ssize_t n; 197 198 size_t remaining = (size_t)(baLen - off); 199 if (len > remaining) { 200 len = remaining; 201 } 202 203 RESTARTABLE(read(fd, buf+off, len), n); 204 if (n == -1) { 205 JNU_ThrowIOExceptionWithLastError(env, "read"); 206 } else { 207 if (n == 0) { 208 n = -1; // EOF 209 } else { 210 (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf+off)); 211 } 212 } 213 return n; 214 } 215 216 /* 217 * Class: sun_tools_attach_BsdVirtualMachine 218 * Method: write 219 * Signature: (I[B)V 220 */ 221 JNIEXPORT void JNICALL Java_sun_tools_attach_BsdVirtualMachine_write 222 (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen) 223 { 224 size_t remaining = bufLen; 225 do { 226 unsigned char buf[128]; 227 size_t len = sizeof(buf); 228 int n; 229 230 if (len > remaining) { 231 len = remaining; 232 } 233 (*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf); 234 235 RESTARTABLE(write(fd, buf, len), n); 236 if (n > 0) { 237 off += n; 238 remaining -= n; 239 } else { 240 JNU_ThrowIOExceptionWithLastError(env, "write"); 241 return; 242 } 243 244 } while (remaining > 0); 245 } 246 247 /* 248 * Class: sun_tools_attach_BSDVirtualMachine 249 * Method: createAttachFile 250 * Signature: (Ljava.lang.String;)V 251 */ 252 JNIEXPORT void JNICALL Java_sun_tools_attach_BsdVirtualMachine_createAttachFile(JNIEnv *env, jclass cls, jstring path) 253 { 254 const char* _path; 255 jboolean isCopy; 256 int fd, rc; 257 258 _path = GetStringPlatformChars(env, path, &isCopy); 259 if (_path == NULL) { 260 JNU_ThrowIOException(env, "Must specify a path"); 261 return; 262 } 263 264 RESTARTABLE(open(_path, O_CREAT | O_EXCL, S_IWUSR | S_IRUSR), fd); 265 if (fd == -1) { 266 /* release p here before we throw an I/O exception */ 267 if (isCopy) { 268 JNU_ReleaseStringPlatformChars(env, path, _path); 269 } 270 JNU_ThrowIOExceptionWithLastError(env, "open"); 271 return; 272 } 273 274 RESTARTABLE(chown(_path, geteuid(), getegid()), rc); 275 276 RESTARTABLE(close(fd), rc); 277 278 /* release p here */ 279 if (isCopy) { 280 JNU_ReleaseStringPlatformChars(env, path, _path); 281 } 282 } 283 284 /* 285 * Class: sun_tools_attach_BSDVirtualMachine 286 * Method: getTempDir 287 * Signature: (V)Ljava.lang.String; 288 */ 289 JNIEXPORT jstring JNICALL Java_sun_tools_attach_BsdVirtualMachine_getTempDir(JNIEnv *env, jclass cls) 290 { 291 // This must be hard coded because it's the system's temporary 292 // directory not the java application's temp directory, ala java.io.tmpdir. 293 294 #ifdef __APPLE__ 295 // macosx has a secure per-user temporary directory 296 static char *temp_path = NULL; 297 char temp_path_storage[PATH_MAX]; 298 if (temp_path == NULL) { 299 int pathSize = confstr(_CS_DARWIN_USER_TEMP_DIR, temp_path_storage, PATH_MAX); 300 if (pathSize == 0 || pathSize > PATH_MAX) { 301 strlcpy(temp_path_storage, "/tmp", sizeof(temp_path_storage)); 302 } 303 temp_path = temp_path_storage; 304 } 305 return JNU_NewStringPlatform(env, temp_path); 306 #else /* __APPLE__ */ 307 return (*env)->NewStringUTF(env, "/tmp"); 308 #endif /* __APPLE__ */ 309 }