blob: fe28cb8b87feaad35012e5dd1c74c0ec485b2c5d [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <door.h>
28#include <stdlib.h>
29#include <unistd.h>
30#include <signal.h>
31#include <string.h>
32#include <fcntl.h>
33#include <errno.h>
34#include <limits.h>
35
36#include "jni.h"
37#include "jni_util.h"
38
39#include "sun_tools_attach_SolarisVirtualMachine.h"
40
41#define RESTARTABLE(_cmd, _result) do { \
42 do { \
43 _result = _cmd; \
44 } while((_result == -1) && (errno == EINTR)); \
45} while(0)
46
47/*
48 * Class: sun_tools_attach_SolarisVirtualMachine
49 * Method: open
50 * Signature: (Ljava/lang/String;)I
51 */
52JNIEXPORT jint JNICALL Java_sun_tools_attach_SolarisVirtualMachine_open
53 (JNIEnv *env, jclass cls, jstring path)
54{
55 jboolean isCopy;
56 const char* p = GetStringPlatformChars(env, path, &isCopy);
57 if (p == NULL) {
58 return 0;
59 } else {
60 int fd;
61 int err = 0;
62
63 fd = open(p, O_RDWR);
64 if (fd == -1) {
65 err = errno;
66 }
67
68 if (isCopy) {
69 JNU_ReleaseStringPlatformChars(env, path, p);
70 }
71
72 if (fd == -1) {
73 if (err == ENOENT) {
74 JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
75 } else {
76 char* msg = strdup(strerror(err));
77 JNU_ThrowIOException(env, msg);
78 if (msg != NULL) {
79 free(msg);
80 }
81 }
82 }
83 return fd;
84 }
85}
86
87/*
88 * Class: sun_tools_attach_SolarisVirtualMachine
89 * Method: checkPermissions
90 * Signature: (Ljava/lang/String;)V
91 */
92JNIEXPORT void JNICALL Java_sun_tools_attach_SolarisVirtualMachine_checkPermissions
93 (JNIEnv *env, jclass cls, jstring path)
94{
95 jboolean isCopy;
96 const char* p = GetStringPlatformChars(env, path, &isCopy);
97 if (p != NULL) {
98 struct stat64 sb;
99 uid_t uid, gid;
100 int res;
101
102 /*
103 * Check that the path is owned by the effective uid/gid of this
104 * process. Also check that group/other access is not allowed.
105 */
106 uid = geteuid();
107 gid = getegid();
108
109 res = stat64(p, &sb);
110 if (res != 0) {
111 /* save errno */
112 res = errno;
113 }
114
115 /* release p here before we throw an I/O exception */
116 if (isCopy) {
117 JNU_ReleaseStringPlatformChars(env, path, p);
118 }
119
120 if (res == 0) {
121 if ( (sb.st_uid != uid) || (sb.st_gid != gid) ||
122 ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) ) {
123 JNU_ThrowIOException(env, "well-known file is not secure");
124 }
125 } else {
126 char* msg = strdup(strerror(res));
127 JNU_ThrowIOException(env, msg);
128 if (msg != NULL) {
129 free(msg);
130 }
131 }
132 }
133}
134
135/*
136 * Class: sun_tools_attach_SolarisVirtualMachine
137 * Method: close
138 * Signature: (I)V
139 */
140JNIEXPORT void JNICALL Java_sun_tools_attach_SolarisVirtualMachine_close
141 (JNIEnv *env, jclass cls, jint fd)
142{
143 int ret;
144 RESTARTABLE(close(fd), ret);
145}
146
147/*
148 * Class: sun_tools_attach_SolarisVirtualMachine
149 * Method: read
150 * Signature: (I[BI)I
151 */
152JNIEXPORT jint JNICALL Java_sun_tools_attach_SolarisVirtualMachine_read
153 (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
154{
155 unsigned char buf[128];
156 size_t len = sizeof(buf);
157 ssize_t n;
158
159 size_t remaining = (size_t)(baLen - off);
160 if (len > remaining) {
161 len = remaining;
162 }
163
164 RESTARTABLE(read(fd, buf+off, len), n);
165 if (n == -1) {
166 JNU_ThrowIOExceptionWithLastError(env, "read");
167 } else {
168 if (n == 0) {
169 n = -1; // EOF
170 } else {
171 (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf+off));
172 }
173 }
174 return n;
175}
176
177/*
178 * Class: sun_tools_attach_SolarisVirtualMachine
179 * Method: sigquit
180 * Signature: (I)V
181 */
182JNIEXPORT void JNICALL Java_sun_tools_attach_SolarisVirtualMachine_sigquit
183 (JNIEnv *env, jclass cls, jint pid)
184{
185 if (kill((pid_t)pid, SIGQUIT) == -1) {
186 JNU_ThrowIOExceptionWithLastError(env, "kill");
187 }
188}
189
190/*
191 * A simple table to translate some known errors into reasonable
192 * error messages
193 */
194static struct {
195 jint err;
196 const char* msg;
197} const error_messages[] = {
198 { 100, "Bad request" },
199 { 101, "Protocol mismatch" },
200 { 102, "Resource failure" },
201 { 103, "Internal error" },
202 { 104, "Permission denied" },
203};
204
205/*
206 * Lookup the given error code and return the appropriate
207 * message. If not found return NULL.
208 */
209static const char* translate_error(jint err) {
210 int table_size = sizeof(error_messages) / sizeof(error_messages[0]);
211 int i;
212
213 for (i=0; i<table_size; i++) {
214 if (err == error_messages[i].err) {
215 return error_messages[i].msg;
216 }
217 }
218 return NULL;
219}
220
221/*
222 * Current protocol version
223 */
224static const char* PROTOCOL_VERSION = "1";
225
226/*
227 * Class: sun_tools_attach_SolarisVirtualMachine
228 * Method: enqueue
229 * Signature: (JILjava/lang/String;[Ljava/lang/Object;)V
230 */
231JNIEXPORT jint JNICALL Java_sun_tools_attach_SolarisVirtualMachine_enqueue
232 (JNIEnv *env, jclass cls, jint fd, jstring cmd, jobjectArray args)
233{
234 jint arg_count, i;
235 size_t size;
236 jboolean isCopy;
237 door_arg_t door_args;
238 char res_buffer[128];
239 jint result = -1;
240 int rc;
241 const char* cstr;
242 char* buf;
243
244 /*
245 * First we get the command string and create the start of the
246 * argument string to send to the target VM:
247 * <ver>\0<cmd>\0
248 */
249 cstr = JNU_GetStringPlatformChars(env, cmd, &isCopy);
250 if (cstr == NULL) {
251 return -1; /* pending exception */
252 }
253 size = strlen(PROTOCOL_VERSION) + strlen(cstr) + 2;
254 buf = (char*)malloc(size);
255 if (buf != NULL) {
256 char* pos = buf;
257 strcpy(buf, PROTOCOL_VERSION);
258 pos += strlen(PROTOCOL_VERSION)+1;
259 strcpy(pos, cstr);
260 }
261 if (isCopy) {
262 JNU_ReleaseStringPlatformChars(env, cmd, cstr);
263 }
264 if (buf == NULL) {
265 JNU_ThrowOutOfMemoryError(env, "malloc failed");
266 return -1;
267 }
268
269 /*
270 * Next we iterate over the arguments and extend the buffer
271 * to include them.
272 */
273 arg_count = (*env)->GetArrayLength(env, args);
274
275 for (i=0; i<arg_count; i++) {
276 jobject obj = (*env)->GetObjectArrayElement(env, args, i);
277 if (obj != NULL) {
278 cstr = JNU_GetStringPlatformChars(env, obj, &isCopy);
279 if (cstr != NULL) {
280 size_t len = strlen(cstr);
281 char* newbuf = (char*)realloc(buf, size+len+1);
282 if (newbuf != NULL) {
283 buf = newbuf;
284 strcpy(buf+size, cstr);
285 size += len+1;
286 }
287 if (isCopy) {
288 JNU_ReleaseStringPlatformChars(env, obj, cstr);
289 }
290 if (newbuf == NULL) {
291 free(buf);
292 JNU_ThrowOutOfMemoryError(env, "realloc failed");
293 return -1;
294 }
295 }
296 }
297 if ((*env)->ExceptionOccurred(env)) {
298 free(buf);
299 return -1;
300 }
301 }
302
303 /*
304 * The arguments to the door function are in 'buf' so we now
305 * do the door call
306 */
307 door_args.data_ptr = buf;
308 door_args.data_size = size;
309 door_args.desc_ptr = NULL;
310 door_args.desc_num = 0;
311 door_args.rbuf = (char*)&res_buffer;
312 door_args.rsize = sizeof(res_buffer);
313
314 RESTARTABLE(door_call(fd, &door_args), rc);
315
316 /*
317 * door_call failed
318 */
319 if (rc == -1) {
320 JNU_ThrowIOExceptionWithLastError(env, "door_call");
321 } else {
322 /*
323 * door_call succeeded but the call didn't return the the expected jint.
324 */
325 if (door_args.data_size < sizeof(jint)) {
326 JNU_ThrowIOException(env, "Enqueue error - reason unknown as result is truncated!");
327 } else {
328 jint* res = (jint*)(door_args.data_ptr);
329 if (*res != JNI_OK) {
330 const char* msg = translate_error(*res);
331 char buf[255];
332 if (msg == NULL) {
333 sprintf(buf, "Unable to enqueue command to target VM: %d", *res);
334 } else {
335 sprintf(buf, "Unable to enqueue command to target VM: %s", msg);
336 }
337 JNU_ThrowIOException(env, buf);
338 } else {
339 /*
340 * The door call should return a file descriptor to one end of
341 * a socket pair
342 */
343 if ((door_args.desc_ptr != NULL) &&
344 (door_args.desc_num == 1) &&
345 (door_args.desc_ptr->d_attributes & DOOR_DESCRIPTOR)) {
346 result = door_args.desc_ptr->d_data.d_desc.d_descriptor;
347 } else {
348 JNU_ThrowIOException(env, "Reply from enqueue missing descriptor!");
349 }
350 }
351 }
352 }
353
354 free(buf);
355 return result;
356}