Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/native/sun/tools/attach/LinuxVirtualMachine.c
32288 views
/*1* Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include "jni.h"26#include "jni_util.h"27#include "jvm.h"2829#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/types.h>39#include <sys/socket.h>40#include <sys/stat.h>41#include <sys/un.h>4243#include "sun_tools_attach_LinuxVirtualMachine.h"4445#define RESTARTABLE(_cmd, _result) do { \46do { \47_result = _cmd; \48} while((_result == -1) && (errno == EINTR)); \49} while(0)5051/*52* Defines a callback that is invoked for each process53*/54typedef void (*ProcessCallback)(const pid_t pid, void* user_data);5556/*57* Invokes the callback function for each process58*/59static void forEachProcess(ProcessCallback f, void* user_data) {60DIR* dir;61struct dirent* ptr;6263/*64* To locate the children we scan /proc looking for files that have a65* position integer as a filename.66*/67if ((dir = opendir("/proc")) == NULL) {68return;69}70while ((ptr = readdir(dir)) != NULL) {71pid_t pid;7273/* skip current/parent directories */74if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {75continue;76}7778/* skip files that aren't numbers */79pid = (pid_t)atoi(ptr->d_name);80if ((int)pid <= 0) {81continue;82}8384/* invoke the callback */85(*f)(pid, user_data);86}87closedir(dir);88}899091/*92* Returns the parent pid of a given pid, or -1 if not found93*/94static pid_t getParent(pid_t pid) {95char state;96FILE* fp;97char stat[2048];98int statlen;99char fn[32];100int i, p;101char* s;102103/*104* try to open /proc/%d/stat105*/106sprintf(fn, "/proc/%d/stat", pid);107fp = fopen(fn, "r");108if (fp == NULL) {109return -1;110}111112/*113* The format is: pid (command) state ppid ...114* As the command could be anything we must find the right most115* ")" and then skip the white spaces that follow it.116*/117statlen = fread(stat, 1, 2047, fp);118stat[statlen] = '\0';119fclose(fp);120s = strrchr(stat, ')');121if (s == NULL) {122return -1;123}124do s++; while (isspace(*s));125i = sscanf(s, "%c %d", &state, &p);126return (pid_t)p;127}128129130/*131* Class: sun_tools_attach_LinuxVirtualMachine132* Method: socket133* Signature: ()I134*/135JNIEXPORT jint JNICALL Java_sun_tools_attach_LinuxVirtualMachine_socket136(JNIEnv *env, jclass cls)137{138int fd = socket(PF_UNIX, SOCK_STREAM, 0);139if (fd == -1) {140JNU_ThrowIOExceptionWithLastError(env, "socket");141}142return (jint)fd;143}144145/*146* Class: sun_tools_attach_LinuxVirtualMachine147* Method: connect148* Signature: (ILjava/lang/String;)I149*/150JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_connect151(JNIEnv *env, jclass cls, jint fd, jstring path)152{153jboolean isCopy;154const char* p = GetStringPlatformChars(env, path, &isCopy);155if (p != NULL) {156struct sockaddr_un addr;157socklen_t sockLen = sizeof(addr);158int err = 0;159160memset(&addr, 0, sizeof(addr));161addr.sun_family = AF_UNIX;162/* strncpy is safe because addr.sun_path was zero-initialized before. */163#ifndef __ANDROID__164strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1);165#else166/* Abstract namespace, first char is '\0', don't use strcpy */167jint len = (*env)->GetStringLength(env, path);168sockLen = offsetof(struct sockaddr_un, sun_path) + len;169memcpy(addr.sun_path, p, len);170#endif171172if (connect(fd, (struct sockaddr*)&addr, sockLen) == -1) {173err = errno;174}175176if (isCopy) {177JNU_ReleaseStringPlatformChars(env, path, p);178}179180/*181* If the connect failed then we throw the appropriate exception182* here (can't throw it before releasing the string as can't call183* JNI with pending exception)184*/185if (err != 0) {186if (err == ENOENT) {187JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);188} else {189char* msg = strdup(strerror(err));190JNU_ThrowIOException(env, msg);191if (msg != NULL) {192free(msg);193}194}195}196}197}198199/*200* Class: sun_tools_attach_LinuxVirtualMachine201* Method: isLinuxThreads202* Signature: ()V203*/204JNIEXPORT jboolean JNICALL Java_sun_tools_attach_LinuxVirtualMachine_isLinuxThreads205(JNIEnv *env, jclass cls)206{207# ifndef _CS_GNU_LIBPTHREAD_VERSION208# define _CS_GNU_LIBPTHREAD_VERSION 3209# endif210size_t n;211char* s;212jboolean res;213214#ifndef __ANDROID__215n = confstr(_CS_GNU_LIBPTHREAD_VERSION, NULL, 0);216if (n <= 0) {217/* glibc before 2.3.2 only has LinuxThreads */218return JNI_TRUE;219}220221s = (char *)malloc(n);222if (s == NULL) {223JNU_ThrowOutOfMemoryError(env, "malloc failed");224return JNI_TRUE;225}226confstr(_CS_GNU_LIBPTHREAD_VERSION, s, n);227#else228s = "Android NPTL";229#endif230231/*232* If the LIBPTHREAD version include "NPTL" then we know we233* have the new threads library and not LinuxThreads234*/235res = (jboolean)(strstr(s, "NPTL") == NULL);236free(s);237return res;238}239240/*241* Structure and callback function used to count the children of242* a given process, and record the pid of the "manager thread".243*/244typedef struct {245pid_t ppid;246int count;247pid_t mpid;248} ChildCountContext;249250static void ChildCountCallback(const pid_t pid, void* user_data) {251ChildCountContext* context = (ChildCountContext*)user_data;252if (getParent(pid) == context->ppid) {253context->count++;254/*255* Remember the pid of the first child. If the final count is256* one then this is the pid of the LinuxThreads manager.257*/258if (context->count == 1) {259context->mpid = pid;260}261}262}263264/*265* Class: sun_tools_attach_LinuxVirtualMachine266* Method: getLinuxThreadsManager267* Signature: (I)I268*/269JNIEXPORT jint JNICALL Java_sun_tools_attach_LinuxVirtualMachine_getLinuxThreadsManager270(JNIEnv *env, jclass cls, jint pid)271{272ChildCountContext context;273274/*275* Iterate over all processes to find how many children 'pid' has276*/277context.ppid = pid;278context.count = 0;279context.mpid = (pid_t)0;280forEachProcess(ChildCountCallback, (void*)&context);281282/*283* If there's no children then this is likely the pid of the primordial284* created by the launcher - in that case the LinuxThreads manager is the285* parent of this process.286*/287if (context.count == 0) {288pid_t parent = getParent(pid);289if ((int)parent > 0) {290return (jint)parent;291}292}293294/*295* There's one child so this is likely the embedded VM case where the296* the primordial thread == LinuxThreads initial thread. The LinuxThreads297* manager in that case is the child.298*/299if (context.count == 1) {300return (jint)context.mpid;301}302303/*304* If we get here it's most likely we were given the wrong pid305*/306JNU_ThrowIOException(env, "Unable to get pid of LinuxThreads manager thread");307return -1;308}309310/*311* Structure and callback function used to send a QUIT signal to all312* children of a given process313*/314typedef struct {315pid_t ppid;316} SendQuitContext;317318static void SendQuitCallback(const pid_t pid, void* user_data) {319SendQuitContext* context = (SendQuitContext*)user_data;320pid_t parent = getParent(pid);321if (parent == context->ppid) {322#ifndef __ANDROID__323kill(pid, SIGQUIT);324#else325/* Dalvik intercepts SIGQUIT so use SIGTERM */326kill(pid, SIGTERM);327#endif328}329}330331/*332* Class: sun_tools_attach_LinuxVirtualMachine333* Method: sendQuitToChildrenOf334* Signature: (I)V335*/336JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_sendQuitToChildrenOf337(JNIEnv *env, jclass cls, jint pid)338{339SendQuitContext context;340context.ppid = (pid_t)pid;341342/*343* Iterate over all children of 'pid' and send a QUIT signal to each.344*/345forEachProcess(SendQuitCallback, (void*)&context);346}347348/*349* Class: sun_tools_attach_LinuxVirtualMachine350* Method: sendQuitTo351* Signature: (I)V352*/353JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_sendQuitTo354(JNIEnv *env, jclass cls, jint pid)355{356if (kill((pid_t)pid, SIGQUIT)) {357JNU_ThrowIOExceptionWithLastError(env, "kill");358}359}360361/*362* Class: sun_tools_attach_LinuxVirtualMachine363* Method: checkPermissions364* Signature: (Ljava/lang/String;)V365*/366JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_checkPermissions367(JNIEnv *env, jclass cls, jstring path)368{369#ifndef __ANDROID__370jboolean isCopy;371const char* p = GetStringPlatformChars(env, path, &isCopy);372if (p != NULL) {373struct stat64 sb;374uid_t uid, gid;375int res;376377/*378* Check that the path is owned by the effective uid/gid of this379* process. Also check that group/other access is not allowed.380*/381uid = geteuid();382gid = getegid();383384res = stat64(p, &sb);385if (res != 0) {386/* save errno */387res = errno;388}389390if (res == 0) {391char msg[100];392jboolean isError = JNI_FALSE;393if (sb.st_uid != uid) {394jio_snprintf(msg, sizeof(msg)-1,395"file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid);396isError = JNI_TRUE;397} else if (sb.st_gid != gid) {398jio_snprintf(msg, sizeof(msg)-1,399"file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid);400isError = JNI_TRUE;401} else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) {402jio_snprintf(msg, sizeof(msg)-1,403"file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777);404isError = JNI_TRUE;405}406if (isError) {407char buf[256];408jio_snprintf(buf, sizeof(buf)-1, "well-known file %s is not secure: %s", p, msg);409JNU_ThrowIOException(env, buf);410}411} else {412char* msg = strdup(strerror(res));413JNU_ThrowIOException(env, msg);414if (msg != NULL) {415free(msg);416}417}418419if (isCopy) {420JNU_ReleaseStringPlatformChars(env, path, p);421}422}423#endif424}425426/*427* Class: sun_tools_attach_LinuxVirtualMachine428* Method: close429* Signature: (I)V430*/431JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_close432(JNIEnv *env, jclass cls, jint fd)433{434int res;435RESTARTABLE(close(fd), res);436}437438/*439* Class: sun_tools_attach_LinuxVirtualMachine440* Method: read441* Signature: (I[BI)I442*/443JNIEXPORT jint JNICALL Java_sun_tools_attach_LinuxVirtualMachine_read444(JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)445{446unsigned char buf[128];447size_t len = sizeof(buf);448ssize_t n;449450size_t remaining = (size_t)(baLen - off);451if (len > remaining) {452len = remaining;453}454455RESTARTABLE(read(fd, buf, len), n);456if (n == -1) {457JNU_ThrowIOExceptionWithLastError(env, "read");458} else {459if (n == 0) {460n = -1; // EOF461} else {462(*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf));463}464}465return n;466}467468/*469* Class: sun_tools_attach_LinuxVirtualMachine470* Method: write471* Signature: (I[B)V472*/473JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_write474(JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen)475{476size_t remaining = bufLen;477do {478unsigned char buf[128];479size_t len = sizeof(buf);480int n;481482if (len > remaining) {483len = remaining;484}485(*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf);486487RESTARTABLE(write(fd, buf, len), n);488if (n > 0) {489off += n;490remaining -= n;491} else {492JNU_ThrowIOExceptionWithLastError(env, "write");493return;494}495496} while (remaining > 0);497}498499500