1/*
2 * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2015 SAP SE. 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#include "jvm.h"
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <errno.h>
35#include <unistd.h>
36#include <signal.h>
37#include <dirent.h>
38#include <ctype.h>
39#include <sys/types.h>
40#include <sys/socket.h>
41#include <sys/stat.h>
42#include <sys/un.h>
43
44/*
45 * Based on 'LinuxVirtualMachine.c'. Non-relevant code has been removed and all
46 * occurrences of the string "Linux" have been replaced by "Aix".
47 */
48
49#include "sun_tools_attach_VirtualMachineImpl.h"
50
51#define RESTARTABLE(_cmd, _result) do { \
52  do { \
53    _result = _cmd; \
54  } while((_result == -1) && (errno == EINTR)); \
55} while(0)
56
57
58/*
59 * Class:     sun_tools_attach_VirtualMachineImpl
60 * Method:    socket
61 * Signature: ()I
62 */
63JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_socket
64  (JNIEnv *env, jclass cls)
65{
66    int fd = socket(PF_UNIX, SOCK_STREAM, 0);
67    if (fd == -1) {
68        JNU_ThrowIOExceptionWithLastError(env, "socket");
69    }
70    /* added time out values */
71    else {
72        struct timeval tv;
73        tv.tv_sec = 2 * 60;
74        tv.tv_usec = 0;
75
76        setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv));
77        setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv));
78    }
79    return (jint)fd;
80}
81
82/*
83 * Class:     sun_tools_attach_VirtualMachineImpl
84 * Method:    connect
85 * Signature: (ILjava/lang/String;)I
86 */
87JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connect
88  (JNIEnv *env, jclass cls, jint fd, jstring path)
89{
90    jboolean isCopy;
91    const char* p = GetStringPlatformChars(env, path, &isCopy);
92    if (p != NULL) {
93        struct sockaddr_un addr;
94        int err = 0;
95
96        memset(&addr, 0, sizeof(addr));
97        addr.sun_family = AF_UNIX;
98        /* strncpy is safe because addr.sun_path was zero-initialized before. */
99        strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1);
100        /* We must call bind with the actual socketaddr length. This is obligatory for AS400. */
101        if (connect(fd, (struct sockaddr*)&addr, SUN_LEN(&addr)) == -1) {
102            err = errno;
103        }
104
105        if (isCopy) {
106            JNU_ReleaseStringPlatformChars(env, path, p);
107        }
108
109        /*
110         * If the connect failed then we throw the appropriate exception
111         * here (can't throw it before releasing the string as can't call
112         * JNI with pending exception)
113         */
114        if (err != 0) {
115            if (err == ENOENT) {
116                JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
117            } else {
118                char* msg = strdup(strerror(err));
119                JNU_ThrowIOException(env, msg);
120                if (msg != NULL) {
121                    free(msg);
122                }
123            }
124        }
125    }
126}
127
128
129/*
130 * Structure and callback function used to send a QUIT signal to all
131 * children of a given process
132 */
133typedef struct {
134    pid_t ppid;
135} SendQuitContext;
136
137static void SendQuitCallback(const pid_t pid, void* user_data) {
138    SendQuitContext* context = (SendQuitContext*)user_data;
139    pid_t parent = getParent(pid);
140    if (parent == context->ppid) {
141        kill(pid, SIGQUIT);
142    }
143}
144
145/*
146 * Class:     sun_tools_attach_VirtualMachineImpl
147 * Method:    sendQuitTo
148 * Signature: (I)V
149 */
150JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_sendQuitTo
151  (JNIEnv *env, jclass cls, jint pid)
152{
153    if (kill((pid_t)pid, SIGQUIT)) {
154        JNU_ThrowIOExceptionWithLastError(env, "kill");
155    }
156}
157
158/*
159 * Class:     sun_tools_attach_VirtualMachineImpl
160 * Method:    checkPermissions
161 * Signature: (Ljava/lang/String;)V
162 */
163JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions
164  (JNIEnv *env, jclass cls, jstring path)
165{
166    jboolean isCopy;
167    const char* p = GetStringPlatformChars(env, path, &isCopy);
168    if (p != NULL) {
169        struct stat64 sb;
170        uid_t uid, gid;
171        int res;
172        /* added missing initialization of the stat64 buffer */
173        memset(&sb, 0, sizeof(struct stat64));
174
175        /*
176         * Check that the path is owned by the effective uid/gid of this
177         * process. Also check that group/other access is not allowed.
178         */
179        uid = geteuid();
180        gid = getegid();
181
182        res = stat64(p, &sb);
183        if (res != 0) {
184            /* save errno */
185            res = errno;
186        }
187
188        if (res == 0) {
189            char msg[100];
190            jboolean isError = JNI_FALSE;
191            if (sb.st_uid != uid) {
192                jio_snprintf(msg, sizeof(msg)-1,
193                    "file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid);
194                isError = JNI_TRUE;
195            } else if (sb.st_gid != gid) {
196                jio_snprintf(msg, sizeof(msg)-1,
197                    "file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid);
198                isError = JNI_TRUE;
199            } else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) {
200                jio_snprintf(msg, sizeof(msg)-1,
201                    "file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777);
202                isError = JNI_TRUE;
203            }
204            if (isError) {
205                char buf[256];
206                jio_snprintf(buf, sizeof(buf)-1, "well-known file %s is not secure: %s", p, msg);
207                JNU_ThrowIOException(env, buf);
208            }
209        } else {
210            char* msg = strdup(strerror(res));
211            JNU_ThrowIOException(env, msg);
212            if (msg != NULL) {
213                free(msg);
214            }
215        }
216
217        if (isCopy) {
218            JNU_ReleaseStringPlatformChars(env, path, p);
219        }
220    }
221}
222
223/*
224 * Class:     sun_tools_attach_VirtualMachineImpl
225 * Method:    close
226 * Signature: (I)V
227 */
228JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_close
229  (JNIEnv *env, jclass cls, jint fd)
230{
231    int res;
232    /* Fixed deadlock when this call of close by the client is not seen by the attach server
233     * which has accepted the (very short) connection already and is waiting for the request. But read don't get a byte,
234     * because the close is lost without shutdown.
235     */
236    shutdown(fd, 2);
237    RESTARTABLE(close(fd), res);
238}
239
240/*
241 * Class:     sun_tools_attach_VirtualMachineImpl
242 * Method:    read
243 * Signature: (I[BI)I
244 */
245JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_read
246  (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
247{
248    unsigned char buf[128];
249    size_t len = sizeof(buf);
250    ssize_t n;
251
252    size_t remaining = (size_t)(baLen - off);
253    if (len > remaining) {
254        len = remaining;
255    }
256
257    RESTARTABLE(read(fd, buf, len), n);
258    if (n == -1) {
259        JNU_ThrowIOExceptionWithLastError(env, "read");
260    } else {
261        if (n == 0) {
262            n = -1;     // EOF
263        } else {
264            (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf));
265        }
266    }
267    return n;
268}
269
270/*
271 * Class:     sun_tools_attach_VirtualMachineImpl
272 * Method:    write
273 * Signature: (I[B)V
274 */
275JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_write
276  (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen)
277{
278    size_t remaining = bufLen;
279    do {
280        unsigned char buf[128];
281        size_t len = sizeof(buf);
282        int n;
283
284        if (len > remaining) {
285            len = remaining;
286        }
287        (*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf);
288
289        RESTARTABLE(write(fd, buf, len), n);
290        if (n > 0) {
291            off += n;
292            remaining -= n;
293        } else {
294            JNU_ThrowIOExceptionWithLastError(env, "write");
295            return;
296        }
297
298    } while (remaining > 0);
299}
300