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 }