VirtualMachineImpl.c revision 13029:55573c377d64
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 */
55DEF_STATIC_JNI_OnLoad
56
57/*
58 * Defines a callback that is invoked for each process
59 */
60typedef void (*ProcessCallback)(const pid_t pid, void* user_data);
61
62/*
63 * Invokes the callback function for each process
64 */
65static 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 */
100static 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 */
141JNIEXPORT 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 */
156JNIEXPORT 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 */
202JNIEXPORT 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 */
238typedef struct {
239    pid_t ppid;
240    int count;
241    pid_t mpid;
242} ChildCountContext;
243
244static 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 */
263JNIEXPORT 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 */
308typedef struct {
309    pid_t ppid;
310} SendQuitContext;
311
312static 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 */
325JNIEXPORT 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 */
342JNIEXPORT 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 */
355JNIEXPORT 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 */
418JNIEXPORT 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 */
430JNIEXPORT 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 */
460JNIEXPORT 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}
485