1 /* 2 * Copyright (c) 1999, 2017, 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 39 static SharedMemoryTransport *transport = NULL; /* maximum of 1 transport */ 40 static SharedMemoryConnection *connection = NULL; /* maximum of 1 connection */ 41 static jdwpTransportCallback *callbacks; 42 static jboolean initialized; 43 static struct jdwpTransportNativeInterface_ interface; 44 static jdwpTransportEnv single_env = (jdwpTransportEnv)&interface; 45 46 /* 47 * Thread-local index to the per-thread error message 48 */ 49 static 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 */ 74 static void 75 setLastError(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 118 static jdwpTransportError 119 handshake() 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 */ 151 static jdwpTransportError JNICALL 152 shmemGetCapabilities(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 167 static jdwpTransportError JNICALL 168 shmemStartListening(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 201 static jdwpTransportError JNICALL 202 shmemAccept(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 230 static jdwpTransportError JNICALL 231 shmemStopListening(jdwpTransportEnv* env) 232 { 233 if (transport != NULL) { 234 shmemBase_closeTransport(transport); 235 transport = NULL; 236 } 237 return JDWPTRANSPORT_ERROR_NONE; 238 } 239 240 static jdwpTransportError JNICALL 241 shmemAttach(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 267 static jdwpTransportError JNICALL 268 shmemWritePacket(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 < JDWP_HEADER_SIZE) { 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 286 static jdwpTransportError JNICALL 287 shmemReadPacket(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 302 static jboolean JNICALL 303 shmemIsOpen(jdwpTransportEnv* env) 304 { 305 if (connection != NULL) { 306 return JNI_TRUE; 307 } else { 308 return JNI_FALSE; 309 } 310 } 311 312 static jdwpTransportError JNICALL 313 shmemClose(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 */ 326 static jdwpTransportError JNICALL 327 shmemGetLastError(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 341 jint JNICALL 342 jdwpTransport_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 }