jdk/src/share/transport/socket/socketTransport.c
changeset 2 90ce3da70b43
child 896 5c02031316bf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/transport/socket/socketTransport.c	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,723 @@
+/*
+ * Copyright 1998-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "jdwpTransport.h"
+#include "sysSocket.h"
+
+/*
+ * The Socket Transport Library.
+ *
+ * This module is an implementation of the Java Debug Wire Protocol Transport
+ * Service Provider Interface - see src/share/javavm/export/jdwpTransport.h.
+ */
+
+static int serverSocketFD;
+static int socketFD = -1;
+static jdwpTransportCallback *callback;
+static JavaVM *jvm;
+static int tlsIndex;
+static jboolean initialized;
+static struct jdwpTransportNativeInterface_ interface;
+static jdwpTransportEnv single_env = (jdwpTransportEnv)&interface;
+
+#define RETURN_ERROR(err, msg) \
+        if (1==1) { \
+            setLastError(err, msg); \
+            return err; \
+        }
+
+#define RETURN_IO_ERROR(msg)    RETURN_ERROR(JDWPTRANSPORT_ERROR_IO_ERROR, msg);
+
+#define RETURN_RECV_ERROR(n) \
+        if (n == 0) { \
+            RETURN_ERROR(JDWPTRANSPORT_ERROR_IO_ERROR, "premature EOF"); \
+        } else { \
+            RETURN_IO_ERROR("recv error"); \
+        }
+
+#define HEADER_SIZE     11
+#define MAX_DATA_SIZE 1000
+
+/*
+ * Record the last error for this thread.
+ */
+static void
+setLastError(jdwpTransportError err, char *newmsg) {
+    char buf[255];
+    char *msg;
+
+    /* get any I/O first in case any system calls override errno */
+    if (err == JDWPTRANSPORT_ERROR_IO_ERROR) {
+        dbgsysGetLastIOError(buf, sizeof(buf));
+    }
+
+    msg = (char *)dbgsysTlsGet(tlsIndex);
+    if (msg != NULL) {
+        (*callback->free)(msg);
+    }
+
+    if (err == JDWPTRANSPORT_ERROR_IO_ERROR) {
+        char *join_str = ": ";
+        int msg_len = strlen(newmsg) + strlen(join_str) + strlen(buf) + 3;
+        msg = (*callback->alloc)(msg_len);
+        if (msg != NULL) {
+            strcpy(msg, newmsg);
+            strcat(msg, join_str);
+            strcat(msg, buf);
+        }
+    } else {
+        msg = (*callback->alloc)(strlen(newmsg)+1);
+        if (msg != NULL) {
+            strcpy(msg, newmsg);
+        }
+    }
+
+    dbgsysTlsPut(tlsIndex, msg);
+}
+
+/*
+ * Return the last error for this thread (may be NULL)
+ */
+static char*
+getLastError() {
+    return (char *)dbgsysTlsGet(tlsIndex);
+}
+
+static jdwpTransportError
+setOptions(int fd)
+{
+    jvalue dontcare;
+    int err;
+
+    dontcare.i = 0;  /* keep compiler happy */
+
+    err = dbgsysSetSocketOption(fd, SO_REUSEADDR, JNI_TRUE, dontcare);
+    if (err < 0) {
+        RETURN_IO_ERROR("setsockopt SO_REUSEADDR failed");
+    }
+
+    err = dbgsysSetSocketOption(fd, TCP_NODELAY, JNI_TRUE, dontcare);
+    if (err < 0) {
+        RETURN_IO_ERROR("setsockopt TCPNODELAY failed");
+    }
+
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+static jdwpTransportError
+handshake(int fd, jlong timeout) {
+    char *hello = "JDWP-Handshake";
+    char b[16];
+    int rv, received, i;
+
+    if (timeout > 0) {
+        dbgsysConfigureBlocking(fd, JNI_FALSE);
+    }
+    received = 0;
+    while (received < (int)strlen(hello)) {
+        int n;
+        char *buf;
+        if (timeout > 0) {
+            rv = dbgsysPoll(fd, JNI_TRUE, JNI_FALSE, (long)timeout);
+            if (rv <= 0) {
+                setLastError(0, "timeout during handshake");
+                return JDWPTRANSPORT_ERROR_IO_ERROR;
+            }
+        }
+        buf = b;
+        buf += received;
+        n = dbgsysRecv(fd, buf, strlen(hello)-received, 0);
+        if (n == 0) {
+            setLastError(0, "handshake failed - connection prematurally closed");
+            return JDWPTRANSPORT_ERROR_IO_ERROR;
+        }
+        if (n < 0) {
+            RETURN_IO_ERROR("recv failed during handshake");
+        }
+        received += n;
+    }
+    if (timeout > 0) {
+        dbgsysConfigureBlocking(fd, JNI_TRUE);
+    }
+    for (i=0; i<(int)strlen(hello); i++) {
+        if (b[i] != hello[i]) {
+            char msg[64];
+            strcpy(msg, "handshake failed - received >");
+            strncat(msg, b, strlen(hello));
+            strcat(msg, "< - excepted >");
+            strcat(msg, hello);
+            strcat(msg, "<");
+            setLastError(0, msg);
+            return JDWPTRANSPORT_ERROR_IO_ERROR;
+        }
+    }
+
+    if (dbgsysSend(fd, hello, strlen(hello), 0) != (int)strlen(hello)) {
+        RETURN_IO_ERROR("send failed during handshake");
+    }
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+static jdwpTransportError
+parseAddress(const char *address, struct sockaddr_in *sa, UINT32 defaultHost) {
+    char *colon;
+
+    memset((void *)sa,0,sizeof(struct sockaddr_in));
+    sa->sin_family = AF_INET;
+
+    /* check for host:port or port */
+    colon = strchr(address, ':');
+    if (colon == NULL) {
+        u_short port = (u_short)atoi(address);
+        sa->sin_port = dbgsysHostToNetworkShort(port);
+        sa->sin_addr.s_addr = dbgsysHostToNetworkLong(defaultHost);
+    } else {
+        char *buf;
+        char *hostname;
+        u_short port;
+        UINT32 addr;
+
+        buf = (*callback->alloc)(strlen(address)+1);
+        if (buf == NULL) {
+            RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
+        }
+        strcpy(buf, address);
+        buf[colon - address] = '\0';
+        hostname = buf;
+        port = atoi(colon + 1);
+        sa->sin_port = dbgsysHostToNetworkShort(port);
+
+        /*
+         * First see if the host is a literal IP address.
+         * If not then try to resolve it.
+         */
+        addr = dbgsysInetAddr(hostname);
+        if (addr == 0xffffffff) {
+            struct hostent *hp = dbgsysGetHostByName(hostname);
+            if (hp == NULL) {
+                /* don't use RETURN_IO_ERROR as unknown host is normal */
+                setLastError(0, "gethostbyname: unknown host");
+                (*callback->free)(buf);
+                return JDWPTRANSPORT_ERROR_IO_ERROR;
+            }
+
+            /* lookup was successful */
+            memcpy(&(sa->sin_addr), hp->h_addr_list[0], hp->h_length);
+        } else {
+            sa->sin_addr.s_addr = addr;
+        }
+
+        (*callback->free)(buf);
+    }
+
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+
+static jdwpTransportError JNICALL
+socketTransport_getCapabilities(jdwpTransportEnv* env,
+        JDWPTransportCapabilities* capabilitiesPtr)
+{
+    JDWPTransportCapabilities result;
+
+    memset(&result, 0, sizeof(result));
+    result.can_timeout_attach = JNI_TRUE;
+    result.can_timeout_accept = JNI_TRUE;
+    result.can_timeout_handshake = JNI_TRUE;
+
+    *capabilitiesPtr = result;
+
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+
+static jdwpTransportError JNICALL
+socketTransport_startListening(jdwpTransportEnv* env, const char* address,
+                               char** actualAddress)
+{
+    struct sockaddr_in sa;
+    int err;
+
+    memset((void *)&sa,0,sizeof(struct sockaddr_in));
+    sa.sin_family = AF_INET;
+
+    /* no address provided */
+    if ((address == NULL) || (address[0] == '\0')) {
+        address = "0";
+    }
+
+    err = parseAddress(address, &sa, INADDR_ANY);
+    if (err != JDWPTRANSPORT_ERROR_NONE) {
+        return err;
+    }
+
+    serverSocketFD = dbgsysSocket(AF_INET, SOCK_STREAM, 0);
+    if (serverSocketFD < 0) {
+        RETURN_IO_ERROR("socket creation failed");
+    }
+
+    err = setOptions(serverSocketFD);
+    if (err) {
+        return err;
+    }
+
+    err = dbgsysBind(serverSocketFD, (struct sockaddr *)&sa, sizeof(sa));
+    if (err < 0) {
+        RETURN_IO_ERROR("bind failed");
+    }
+
+    err = dbgsysListen(serverSocketFD, 1);
+    if (err < 0) {
+        RETURN_IO_ERROR("listen failed");
+    }
+
+    {
+        char buf[20];
+        int len = sizeof(sa);
+        jint portNum;
+        err = dbgsysGetSocketName(serverSocketFD,
+                               (struct sockaddr *)&sa, &len);
+        portNum = dbgsysNetworkToHostShort(sa.sin_port);
+        sprintf(buf, "%d", portNum);
+        *actualAddress = (*callback->alloc)(strlen(buf) + 1);
+        if (*actualAddress == NULL) {
+            RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
+        } else {
+            strcpy(*actualAddress, buf);
+        }
+    }
+
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+static jdwpTransportError JNICALL
+socketTransport_accept(jdwpTransportEnv* env, jlong acceptTimeout, jlong handshakeTimeout)
+{
+    int socketLen, err;
+    struct sockaddr_in socket;
+    jlong startTime = (jlong)0;
+
+    /*
+     * Use a default handshake timeout if not specified - this avoids an indefinite
+     * hang in cases where something other than a debugger connects to our port.
+     */
+    if (handshakeTimeout == 0) {
+        handshakeTimeout = 2000;
+    }
+
+    do {
+        /*
+         * If there is an accept timeout then we put the socket in non-blocking
+         * mode and poll for a connection.
+         */
+        if (acceptTimeout > 0) {
+            int rv;
+            dbgsysConfigureBlocking(serverSocketFD, JNI_FALSE);
+            startTime = dbgsysCurrentTimeMillis();
+            rv = dbgsysPoll(serverSocketFD, JNI_TRUE, JNI_FALSE, (long)acceptTimeout);
+            if (rv <= 0) {
+                /* set the last error here as could be overridden by configureBlocking */
+                if (rv == 0) {
+                    setLastError(JDWPTRANSPORT_ERROR_IO_ERROR, "poll failed");
+                }
+                /* restore blocking state */
+                dbgsysConfigureBlocking(serverSocketFD, JNI_TRUE);
+                if (rv == 0) {
+                    RETURN_ERROR(JDWPTRANSPORT_ERROR_TIMEOUT, "timed out waiting for connection");
+                } else {
+                    return JDWPTRANSPORT_ERROR_IO_ERROR;
+                }
+            }
+        }
+
+        /*
+         * Accept the connection
+         */
+        memset((void *)&socket,0,sizeof(struct sockaddr_in));
+        socketLen = sizeof(socket);
+        socketFD = dbgsysAccept(serverSocketFD,
+                                (struct sockaddr *)&socket,
+                                &socketLen);
+        /* set the last error here as could be overridden by configureBlocking */
+        if (socketFD < 0) {
+            setLastError(JDWPTRANSPORT_ERROR_IO_ERROR, "accept failed");
+        }
+        /*
+         * Restore the blocking state - note that the accepted socket may be in
+         * blocking or non-blocking mode (platform dependent). However as there
+         * is a handshake timeout set then it will go into non-blocking mode
+         * anyway for the handshake.
+         */
+        if (acceptTimeout > 0) {
+            dbgsysConfigureBlocking(serverSocketFD, JNI_TRUE);
+        }
+        if (socketFD < 0) {
+            return JDWPTRANSPORT_ERROR_IO_ERROR;
+        }
+
+        /* handshake with the debugger */
+        err = handshake(socketFD, handshakeTimeout);
+
+        /*
+         * If the handshake fails then close the connection. If there if an accept
+         * timeout then we must adjust the timeout for the next poll.
+         */
+        if (err) {
+            fprintf(stderr, "Debugger failed to attach: %s\n", getLastError());
+            dbgsysSocketClose(socketFD);
+            socketFD = -1;
+            if (acceptTimeout > 0) {
+                long endTime = dbgsysCurrentTimeMillis();
+                acceptTimeout -= (endTime - startTime);
+                if (acceptTimeout <= 0) {
+                    setLastError(JDWPTRANSPORT_ERROR_IO_ERROR,
+                        "timeout waiting for debugger to connect");
+                    return JDWPTRANSPORT_ERROR_IO_ERROR;
+                }
+            }
+        }
+    } while (socketFD < 0);
+
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+static jdwpTransportError JNICALL
+socketTransport_stopListening(jdwpTransportEnv *env)
+{
+    if (serverSocketFD < 0) {
+        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_STATE, "connection not open");
+    }
+    if (dbgsysSocketClose(serverSocketFD) < 0) {
+        RETURN_IO_ERROR("close failed");
+    }
+    serverSocketFD = -1;
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+static jdwpTransportError JNICALL
+socketTransport_attach(jdwpTransportEnv* env, const char* addressString, jlong attachTimeout,
+                       jlong handshakeTimeout)
+{
+    struct sockaddr_in sa;
+    int err;
+
+    if (addressString == NULL || addressString[0] == '\0') {
+        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "address is missing");
+    }
+
+    err = parseAddress(addressString, &sa, 0x7f000001);
+    if (err != JDWPTRANSPORT_ERROR_NONE) {
+        return err;
+    }
+
+    socketFD = dbgsysSocket(AF_INET, SOCK_STREAM, 0);
+    if (socketFD < 0) {
+        RETURN_IO_ERROR("unable to create socket");
+    }
+
+    err = setOptions(socketFD);
+    if (err) {
+        return err;
+    }
+
+    /*
+     * To do a timed connect we make the socket non-blocking
+     * and poll with a timeout;
+     */
+    if (attachTimeout > 0) {
+        dbgsysConfigureBlocking(socketFD, JNI_FALSE);
+    }
+
+    err = dbgsysConnect(socketFD, (struct sockaddr *)&sa, sizeof(sa));
+    if (err == DBG_EINPROGRESS && attachTimeout > 0) {
+        err = dbgsysFinishConnect(socketFD, (long)attachTimeout);
+
+        if (err == DBG_ETIMEOUT) {
+            dbgsysConfigureBlocking(socketFD, JNI_TRUE);
+            RETURN_ERROR(JDWPTRANSPORT_ERROR_TIMEOUT, "connect timed out");
+        }
+    }
+
+    if (err < 0) {
+        RETURN_IO_ERROR("connect failed");
+    }
+
+    if (attachTimeout > 0) {
+        dbgsysConfigureBlocking(socketFD, JNI_TRUE);
+    }
+
+    err = handshake(socketFD, handshakeTimeout);
+    if (err) {
+        dbgsysSocketClose(socketFD);
+        socketFD = -1;
+        return err;
+    }
+
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+static jboolean JNICALL
+socketTransport_isOpen(jdwpTransportEnv* env)
+{
+    if (socketFD >= 0) {
+        return JNI_TRUE;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
+static jdwpTransportError JNICALL
+socketTransport_close(jdwpTransportEnv* env)
+{
+    int fd = socketFD;
+    socketFD = -1;
+    if (fd < 0) {
+        return JDWPTRANSPORT_ERROR_NONE;
+    }
+    if (dbgsysSocketClose(fd) < 0) {
+        /*
+         * close failed - it's pointless to restore socketFD here because
+         * any subsequent close will likely fail aswell.
+         */
+        RETURN_IO_ERROR("close failed");
+    }
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+static jdwpTransportError JNICALL
+socketTransport_writePacket(jdwpTransportEnv* env, const jdwpPacket *packet)
+{
+    jint len, data_len, id;
+    /*
+     * room for header and up to MAX_DATA_SIZE data bytes
+     */
+    char header[HEADER_SIZE + MAX_DATA_SIZE];
+    jbyte *data;
+
+    /* packet can't be null */
+    if (packet == NULL) {
+        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "packet is NULL");
+    }
+
+    len = packet->type.cmd.len;         /* includes header */
+    data_len = len - HEADER_SIZE;
+
+    /* bad packet */
+    if (data_len < 0) {
+        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "invalid length");
+    }
+
+    /* prepare the header for transmission */
+    len = (jint)dbgsysHostToNetworkLong(len);
+    id = (jint)dbgsysHostToNetworkLong(packet->type.cmd.id);
+
+    memcpy(header + 0, &len, 4);
+    memcpy(header + 4, &id, 4);
+    header[8] = packet->type.cmd.flags;
+    if (packet->type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) {
+        jshort errorCode =
+            dbgsysHostToNetworkShort(packet->type.reply.errorCode);
+        memcpy(header + 9, &errorCode, 2);
+    } else {
+        header[9] = packet->type.cmd.cmdSet;
+        header[10] = packet->type.cmd.cmd;
+    }
+
+    data = packet->type.cmd.data;
+    /* Do one send for short packets, two for longer ones */
+    if (data_len <= MAX_DATA_SIZE) {
+        memcpy(header + HEADER_SIZE, data, data_len);
+        if (dbgsysSend(socketFD, (char *)&header, HEADER_SIZE + data_len, 0) !=
+            HEADER_SIZE + data_len) {
+            RETURN_IO_ERROR("send failed");
+        }
+    } else {
+        memcpy(header + HEADER_SIZE, data, MAX_DATA_SIZE);
+        if (dbgsysSend(socketFD, (char *)&header, HEADER_SIZE + MAX_DATA_SIZE, 0) !=
+            HEADER_SIZE + MAX_DATA_SIZE) {
+            RETURN_IO_ERROR("send failed");
+        }
+        /* Send the remaining data bytes right out of the data area. */
+        if (dbgsysSend(socketFD, (char *)data + MAX_DATA_SIZE,
+                       data_len - MAX_DATA_SIZE, 0) != data_len - MAX_DATA_SIZE) {
+            RETURN_IO_ERROR("send failed");
+        }
+    }
+
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+static jint
+recv_fully(int f, char *buf, int len)
+{
+    int nbytes = 0;
+    while (nbytes < len) {
+        int res = dbgsysRecv(f, buf + nbytes, len - nbytes, 0);
+        if (res < 0) {
+            return res;
+        } else if (res == 0) {
+            break; /* eof, return nbytes which is less than len */
+        }
+        nbytes += res;
+    }
+    return nbytes;
+}
+
+static jdwpTransportError JNICALL
+socketTransport_readPacket(jdwpTransportEnv* env, jdwpPacket* packet) {
+    jint length, data_len;
+    jint n;
+
+    /* packet can't be null */
+    if (packet == NULL) {
+        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT, "packet is null");
+    }
+
+    /* read the length field */
+    n = recv_fully(socketFD, (char *)&length, sizeof(jint));
+
+    /* check for EOF */
+    if (n == 0) {
+        packet->type.cmd.len = 0;
+        return JDWPTRANSPORT_ERROR_NONE;
+    }
+    if (n != sizeof(jint)) {
+        RETURN_RECV_ERROR(n);
+    }
+
+    length = (jint)dbgsysNetworkToHostLong(length);
+    packet->type.cmd.len = length;
+
+
+    n = recv_fully(socketFD,(char *)&(packet->type.cmd.id),sizeof(jint));
+    if (n < (int)sizeof(jint)) {
+        RETURN_RECV_ERROR(n);
+    }
+
+    packet->type.cmd.id = (jint)dbgsysNetworkToHostLong(packet->type.cmd.id);
+
+    n = recv_fully(socketFD,(char *)&(packet->type.cmd.flags),sizeof(jbyte));
+    if (n < (int)sizeof(jbyte)) {
+        RETURN_RECV_ERROR(n);
+    }
+
+    if (packet->type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) {
+        n = recv_fully(socketFD,(char *)&(packet->type.reply.errorCode),sizeof(jbyte));
+        if (n < (int)sizeof(jshort)) {
+            RETURN_RECV_ERROR(n);
+        }
+
+        /* FIXME - should the error be converted to host order?? */
+
+
+    } else {
+        n = recv_fully(socketFD,(char *)&(packet->type.cmd.cmdSet),sizeof(jbyte));
+        if (n < (int)sizeof(jbyte)) {
+            RETURN_RECV_ERROR(n);
+        }
+
+        n = recv_fully(socketFD,(char *)&(packet->type.cmd.cmd),sizeof(jbyte));
+        if (n < (int)sizeof(jbyte)) {
+            RETURN_RECV_ERROR(n);
+        }
+    }
+
+    data_len = length - ((sizeof(jint) * 2) + (sizeof(jbyte) * 3));
+
+    if (data_len < 0) {
+        setLastError(0, "Badly formed packet received - invalid length");
+        return JDWPTRANSPORT_ERROR_IO_ERROR;
+    } else if (data_len == 0) {
+        packet->type.cmd.data = NULL;
+    } else {
+        packet->type.cmd.data= (*callback->alloc)(data_len);
+
+        if (packet->type.cmd.data == NULL) {
+            RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
+        }
+
+        n = recv_fully(socketFD,(char *)packet->type.cmd.data, data_len);
+        if (n < data_len) {
+            (*callback->free)(packet->type.cmd.data);
+            RETURN_RECV_ERROR(n);
+        }
+    }
+
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+static jdwpTransportError JNICALL
+socketTransport_getLastError(jdwpTransportEnv* env, char** msgP) {
+    char *msg = (char *)dbgsysTlsGet(tlsIndex);
+    if (msg == NULL) {
+        return JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE;
+    }
+    *msgP = (*callback->alloc)(strlen(msg)+1);
+    if (*msgP == NULL) {
+        return JDWPTRANSPORT_ERROR_OUT_OF_MEMORY;
+    }
+    strcpy(*msgP, msg);
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+JNIEXPORT jint JNICALL
+jdwpTransport_OnLoad(JavaVM *vm, jdwpTransportCallback* cbTablePtr,
+                     jint version, jdwpTransportEnv** result)
+{
+    if (version != JDWPTRANSPORT_VERSION_1_0) {
+        return JNI_EVERSION;
+    }
+    if (initialized) {
+        /*
+         * This library doesn't support multiple environments (yet)
+         */
+        return JNI_EEXIST;
+    }
+    initialized = JNI_TRUE;
+    jvm = vm;
+    callback = cbTablePtr;
+
+    /* initialize interface table */
+    interface.GetCapabilities = &socketTransport_getCapabilities;
+    interface.Attach = &socketTransport_attach;
+    interface.StartListening = &socketTransport_startListening;
+    interface.StopListening = &socketTransport_stopListening;
+    interface.Accept = &socketTransport_accept;
+    interface.IsOpen = &socketTransport_isOpen;
+    interface.Close = &socketTransport_close;
+    interface.ReadPacket = &socketTransport_readPacket;
+    interface.WritePacket = &socketTransport_writePacket;
+    interface.GetLastError = &socketTransport_getLastError;
+    *result = &single_env;
+
+    /* initialized TLS */
+    tlsIndex = dbgsysTlsAlloc();
+    return JNI_OK;
+}