1/*
2 * Copyright (c) 1999, 2008, 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#include <string.h>
26
27#include "jdwpTransport.h"
28#include "shmemBase.h"
29#include "sysShmem.h"
30#include "sys.h"
31
32/*
33 * The Shared Memory Transport Library.
34 *
35 * This module is an implementation of the Java Debug Wire Protocol Transport
36 * Service Provider Interface - see src/share/javavm/export/jdwpTransport.h.
37 */
38
39static SharedMemoryTransport *transport = NULL;  /* maximum of 1 transport */
40static SharedMemoryConnection *connection = NULL;  /* maximum of 1 connection */
41static jdwpTransportCallback *callbacks;
42static jboolean initialized;
43static struct jdwpTransportNativeInterface_ interface;
44static jdwpTransportEnv single_env = (jdwpTransportEnv)&interface;
45
46/*
47 * Thread-local index to the per-thread error message
48 */
49static int tlsIndex;
50
51/*
52 * Return an error and record the error message associated with
53 * the error. Note the if (1==1) { } usage here is to avoid
54 * compilers complaining that a statement isn't reached which
55 * will arise if the semicolon (;) appears after the macro,
56 */
57#define RETURN_ERROR(err, msg)          \
58        if (1==1) {                     \
59            setLastError(err, msg);     \
60            return err;                 \
61        }
62
63/*
64 * Return an I/O error and record the error message.
65 */
66#define RETURN_IO_ERROR(msg)    RETURN_ERROR(JDWPTRANSPORT_ERROR_IO_ERROR, msg);
67
68
69/*
70 * Set the error message for this thread. If the error is an I/O
71 * error then augment the supplied error message with the textual
72 * representation of the I/O error.
73 */
74static void
75setLastError(int err, char *newmsg) {
76    char buf[255];
77    char *msg;
78
79    /* get any I/O first in case any system calls override errno */
80    if (err == JDWPTRANSPORT_ERROR_IO_ERROR) {
81        if (shmemBase_getlasterror(buf, sizeof(buf)) != SYS_OK) {
82            buf[0] = '\0';
83        }
84    }
85
86    /* free any current error */
87    msg = (char *)sysTlsGet(tlsIndex);
88    if (msg != NULL) {
89        (*callbacks->free)(msg);
90    }
91
92    /*
93     * For I/O errors append the I/O error message with to the
94     * supplied message. For all other errors just use the supplied
95     * message.
96     */
97    if (err == JDWPTRANSPORT_ERROR_IO_ERROR) {
98        char *join_str = ": ";
99        int msg_len = (int)strlen(newmsg) + (int)strlen(join_str) +
100                      (int)strlen(buf) + 3;
101        msg = (*callbacks->alloc)(msg_len);
102        if (msg != NULL) {
103            strcpy(msg, newmsg);
104            strcat(msg, join_str);
105            strcat(msg, buf);
106        }
107    } else {
108        msg = (*callbacks->alloc)((int)strlen(newmsg)+1);
109        if (msg != NULL) {
110            strcpy(msg, newmsg);
111        }
112    }
113
114    /* Put a pointer to the message in TLS */
115    sysTlsPut(tlsIndex, msg);
116}
117
118static jdwpTransportError
119handshake()
120{
121    char *hello = "JDWP-Handshake";
122    unsigned int i;
123
124    for (i=0; i<strlen(hello); i++) {
125        jbyte b;
126        int rv = shmemBase_receiveByte(connection, &b);
127        if (rv != 0) {
128            RETURN_IO_ERROR("receive failed during handshake");
129        }
130        if ((char)b != hello[i]) {
131            RETURN_IO_ERROR("handshake failed - debugger sent unexpected message");
132        }
133    }
134
135    for (i=0; i<strlen(hello); i++) {
136        int rv = shmemBase_sendByte(connection, (jbyte)hello[i]);
137        if (rv != 0) {
138            RETURN_IO_ERROR("write failed during handshake");
139        }
140    }
141
142    return JDWPTRANSPORT_ERROR_NONE;
143}
144
145
146/*
147 * Return the capabilities of the shared memory transport. The shared
148 * memory transport supports both the attach and accept timeouts but
149 * doesn't support a handshake timeout.
150 */
151static jdwpTransportError JNICALL
152shmemGetCapabilities(jdwpTransportEnv* env, JDWPTransportCapabilities *capabilitiesPtr)
153{
154    JDWPTransportCapabilities result;
155
156    memset(&result, 0, sizeof(result));
157    result.can_timeout_attach = JNI_TRUE;
158    result.can_timeout_accept = JNI_TRUE;
159    result.can_timeout_handshake = JNI_FALSE;
160
161    *capabilitiesPtr = result;
162
163    return JDWPTRANSPORT_ERROR_NONE;
164}
165
166
167static jdwpTransportError JNICALL
168shmemStartListening(jdwpTransportEnv* env, const char *address, char **actualAddress)
169{
170    jint rc;
171
172    if (connection != NULL || transport != NULL) {
173        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "already connected or already listening");
174    }
175
176    rc = shmemBase_listen(address, &transport);
177
178    /*
179     * If a name was selected by the function above, find it and return
180     * it in place of the original arg.
181     */
182    if (rc == SYS_OK) {
183        char *name;
184        char *name2;
185        rc = shmemBase_name(transport, &name);
186        if (rc == SYS_OK) {
187            name2 = (callbacks->alloc)((int)strlen(name) + 1);
188            if (name2 == NULL) {
189                RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
190            } else {
191                strcpy(name2, name);
192                *actualAddress = name2;
193            }
194        }
195    } else {
196        RETURN_IO_ERROR("failed to create shared memory listener");
197    }
198    return JDWPTRANSPORT_ERROR_NONE;
199}
200
201static jdwpTransportError JNICALL
202shmemAccept(jdwpTransportEnv* env, jlong acceptTimeout, jlong handshakeTimeout)
203{
204    jint rc;
205
206    if (connection != NULL) {
207        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "already connected");
208    }
209    if (transport == NULL) {
210        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "transport not listening");
211    }
212
213    rc = shmemBase_accept(transport, (long)acceptTimeout, &connection);
214    if (rc != SYS_OK) {
215        if (rc == SYS_TIMEOUT) {
216            RETURN_ERROR(JDWPTRANSPORT_ERROR_TIMEOUT, "Timed out waiting for connection");
217        } else {
218            RETURN_IO_ERROR("failed to accept shared memory connection");
219        }
220    }
221
222    rc = handshake();
223    if (rc != JDWPTRANSPORT_ERROR_NONE) {
224        shmemBase_closeConnection(connection);
225        connection = NULL;
226    }
227    return rc;
228}
229
230static jdwpTransportError JNICALL
231shmemStopListening(jdwpTransportEnv* env)
232{
233    if (transport != NULL) {
234        shmemBase_closeTransport(transport);
235        transport = NULL;
236    }
237    return JDWPTRANSPORT_ERROR_NONE;
238}
239
240static jdwpTransportError JNICALL
241shmemAttach(jdwpTransportEnv* env, const char *address, jlong attachTimeout, jlong handshakeTimeout)
242{
243    jint rc;
244
245    if (connection != NULL) {
246        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "already connected");
247    }
248    rc = shmemBase_attach(address, (long)attachTimeout, &connection);
249    if (rc != SYS_OK) {
250        if (rc == SYS_NOMEM) {
251            RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
252        }
253        if (rc == SYS_TIMEOUT) {
254            RETURN_ERROR(JDWPTRANSPORT_ERROR_TIMEOUT, "Timed out waiting to attach");
255        }
256        RETURN_IO_ERROR("failed to attach to shared memory connection");
257    }
258
259    rc = handshake();
260    if (rc != JDWPTRANSPORT_ERROR_NONE) {
261        shmemBase_closeConnection(connection);
262        connection = NULL;
263    }
264    return rc;
265}
266
267static jdwpTransportError JNICALL
268shmemWritePacket(jdwpTransportEnv* env, const jdwpPacket *packet)
269{
270    if (packet == NULL) {
271        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "packet is null");
272    }
273    if (packet->type.cmd.len < 11) {
274        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "invalid length");
275    }
276    if (connection == NULL) {
277        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "not connected");
278    }
279    if (shmemBase_sendPacket(connection, packet) == SYS_OK) {
280        return JDWPTRANSPORT_ERROR_NONE;
281    } else {
282        RETURN_IO_ERROR("write packet failed");
283    }
284}
285
286static jdwpTransportError JNICALL
287shmemReadPacket(jdwpTransportEnv* env, jdwpPacket *packet)
288{
289    if (packet == NULL) {
290        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "packet is null");
291    }
292    if (connection == NULL) {
293        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "not connected");
294    }
295    if (shmemBase_receivePacket(connection, packet) == SYS_OK) {
296        return JDWPTRANSPORT_ERROR_NONE;
297    } else {
298        RETURN_IO_ERROR("receive packet failed");
299    }
300}
301
302static jboolean JNICALL
303shmemIsOpen(jdwpTransportEnv* env)
304{
305    if (connection != NULL) {
306        return JNI_TRUE;
307    } else {
308        return JNI_FALSE;
309    }
310}
311
312static jdwpTransportError JNICALL
313shmemClose(jdwpTransportEnv* env)
314{
315    SharedMemoryConnection* current_connection = connection;
316    if (current_connection != NULL) {
317        connection = NULL;
318        shmemBase_closeConnection(current_connection);
319    }
320    return JDWPTRANSPORT_ERROR_NONE;
321}
322
323/*
324 * Return the error message for this thread.
325 */
326static jdwpTransportError  JNICALL
327shmemGetLastError(jdwpTransportEnv* env, char **msgP)
328{
329    char *msg = (char *)sysTlsGet(tlsIndex);
330    if (msg == NULL) {
331        return JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE;
332    }
333    *msgP = (*callbacks->alloc)((int)strlen(msg)+1);
334    if (*msgP == NULL) {
335        return JDWPTRANSPORT_ERROR_OUT_OF_MEMORY;
336    }
337    strcpy(*msgP, msg);
338    return JDWPTRANSPORT_ERROR_NONE;
339}
340
341jint JNICALL
342jdwpTransport_OnLoad(JavaVM *vm, jdwpTransportCallback* cbTablePtr,
343                     jint version, jdwpTransportEnv** result)
344{
345    if (version != JDWPTRANSPORT_VERSION_1_0) {
346        return JNI_EVERSION;
347    }
348    if (initialized) {
349        /*
350         * This library doesn't support multiple environments (yet)
351         */
352        return JNI_EEXIST;
353    }
354    initialized = JNI_TRUE;
355
356    /* initialize base shared memory system */
357   (void) shmemBase_initialize(vm, cbTablePtr);
358
359    /* save callbacks */
360    callbacks = cbTablePtr;
361
362    /* initialize interface table */
363    interface.GetCapabilities = &shmemGetCapabilities;
364    interface.Attach = &shmemAttach;
365    interface.StartListening = &shmemStartListening;
366    interface.StopListening = &shmemStopListening;
367    interface.Accept = &shmemAccept;
368    interface.IsOpen = &shmemIsOpen;
369    interface.Close = &shmemClose;
370    interface.ReadPacket = &shmemReadPacket;
371    interface.WritePacket = &shmemWritePacket;
372    interface.GetLastError = &shmemGetLastError;
373    *result = &single_env;
374
375    /* initialized TLS */
376    tlsIndex = sysTlsAlloc();
377
378    return JNI_OK;
379}
380