1 /*
   2  * Copyright (c) 1998, 2018, 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 
  26 #include "util.h"
  27 #include "utf_util.h"
  28 #include "transport.h"
  29 #include "debugLoop.h"
  30 #include "sys.h"
  31 
  32 static jdwpTransportEnv *transport = NULL;
  33 static unsigned transportVersion = JDWPTRANSPORT_VERSION_1_0;
  34 
  35 static jrawMonitorID listenerLock;
  36 static jrawMonitorID sendLock;
  37 
  38 /*
  39  * data structure used for passing transport info from thread to thread
  40  */
  41 typedef struct TransportInfo {
  42     char *name;
  43     jdwpTransportEnv *transport;
  44     char *address;
  45     long timeout;
  46     char *allowed_peers;
  47     unsigned transportVersion;
  48 } TransportInfo;
  49 
  50 static struct jdwpTransportCallback callback = {jvmtiAllocate, jvmtiDeallocate};
  51 
  52 /*
  53  * Print the last transport error
  54  */
  55 static void
  56 printLastError(jdwpTransportEnv *t, jdwpTransportError err)
  57 {
  58     char  *msg;
  59     jbyte *utf8msg;
  60     jdwpTransportError rv;
  61 
  62     msg     = NULL;
  63     utf8msg = NULL;
  64     rv = (*t)->GetLastError(t, &msg); /* This is a platform encoded string */
  65     if ( msg != NULL ) {
  66         int len;
  67         int maxlen;
  68 
  69         /* Convert this string to UTF8 */
  70         len = (int)strlen(msg);
  71         maxlen = len+len/2+2; /* Should allow for plenty of room */
  72         utf8msg = (jbyte*)jvmtiAllocate(maxlen+1);
  73         if (utf8msg != NULL) {
  74            (void)utf8FromPlatform(msg, len, utf8msg, maxlen+1);
  75         }
  76     }
  77     if (rv == JDWPTRANSPORT_ERROR_NONE) {
  78         ERROR_MESSAGE(("transport error %d: %s",err, utf8msg));
  79     } else if ( msg!=NULL ) {
  80         ERROR_MESSAGE(("transport error %d: %s",err, utf8msg));
  81     } else {
  82         ERROR_MESSAGE(("transport error %d: %s",err, "UNKNOWN"));
  83     }
  84     jvmtiDeallocate(msg);
  85     jvmtiDeallocate(utf8msg);
  86 }
  87 
  88 /* Find OnLoad symbol */
  89 static jdwpTransport_OnLoad_t
  90 findTransportOnLoad(void *handle)
  91 {
  92     jdwpTransport_OnLoad_t onLoad;
  93 
  94     onLoad = (jdwpTransport_OnLoad_t)NULL;
  95     if (handle == NULL) {
  96         return onLoad;
  97     }
  98 #if defined(_WIN32) && !defined(_WIN64)
  99     onLoad = (jdwpTransport_OnLoad_t)
 100                  dbgsysFindLibraryEntry(handle, "_jdwpTransport_OnLoad@16");
 101     if (onLoad != NULL) {
 102         return onLoad;
 103     }
 104 #endif
 105     onLoad = (jdwpTransport_OnLoad_t)
 106                  dbgsysFindLibraryEntry(handle, "jdwpTransport_OnLoad");
 107     return onLoad;
 108 }
 109 
 110 /* Load transport library (directory=="" means do system search) */
 111 static void *
 112 loadTransportLibrary(const char *libdir, const char *name)
 113 {
 114     char buf[MAXPATHLEN*2+100];
 115 #ifndef STATIC_BUILD
 116     void *handle;
 117     char libname[MAXPATHLEN+2];
 118     const char *plibdir;
 119 
 120     /* Convert libdir from UTF-8 to platform encoding */
 121     plibdir = NULL;
 122     if ( libdir != NULL ) {
 123         int  len;
 124 
 125         len = (int)strlen(libdir);
 126         (void)utf8ToPlatform((jbyte*)libdir, len, buf, (int)sizeof(buf));
 127         plibdir = buf;
 128     }
 129 
 130     /* Construct library name (simple name or full path) */
 131     dbgsysBuildLibName(libname, sizeof(libname), plibdir, name);
 132     if (strlen(libname) == 0) {
 133         return NULL;
 134     }
 135 
 136     /* dlopen (unix) / LoadLibrary (windows) the transport library */
 137     handle = dbgsysLoadLibrary(libname, buf, sizeof(buf));
 138     return handle;
 139 #else
 140     return (dbgsysLoadLibrary(NULL, buf, sizeof(buf)));
 141 #endif
 142 }
 143 
 144 /*
 145  * loadTransport() is adapted from loadJVMHelperLib() in
 146  * JDK 1.2 javai.c v1.61
 147  */
 148 static jdwpError
 149 loadTransport(const char *name, TransportInfo *info)
 150 {
 151     JNIEnv                 *env;
 152     jdwpTransport_OnLoad_t  onLoad;
 153     void                   *handle;
 154     const char             *libdir;
 155 
 156     /* Make sure library name is not empty */
 157     if (name == NULL) {
 158         ERROR_MESSAGE(("library name is empty"));
 159         return JDWP_ERROR(TRANSPORT_LOAD);
 160     }
 161     if (info == NULL) {
 162         ERROR_MESSAGE(("internal error: info should not be NULL"));
 163         return JDWP_ERROR(TRANSPORT_LOAD);
 164     }
 165 
 166     /* First, look in sun.boot.library.path. This should find the standard
 167      *  dt_socket and dt_shmem transport libraries, or any library
 168      *  that was delivered with the J2SE.
 169      *  Note: Since 6819213 fixed, Java property sun.boot.library.path can
 170      *  contain multiple paths. Dll_dir is the first entry and
 171      *  -Dsun.boot.library.path entries are appended.
 172      */
 173     libdir = gdata->property_sun_boot_library_path;
 174     if (libdir == NULL) {
 175         ERROR_MESSAGE(("Java property sun.boot.library.path is not set"));
 176         return JDWP_ERROR(TRANSPORT_LOAD);
 177     }
 178     handle = loadTransportLibrary(libdir, name);
 179     if (handle == NULL) {
 180         /* Second, look along the path used by the native dlopen/LoadLibrary
 181          *  functions. This should effectively try and load the simple
 182          *  library name, which will cause the default system library
 183          *  search technique to happen.
 184          *  We should only reach here if the transport library wasn't found
 185          *  in the J2SE directory, e.g. it's a custom transport library
 186          *  not installed in the J2SE like dt_socket and dt_shmem is.
 187          *
 188          *  Note: Why not use java.library.path? Several reasons:
 189          *        a) This matches existing agentlib search
 190          *        b) These are technically not JNI libraries
 191          */
 192         handle = loadTransportLibrary("", name);
 193     }
 194 
 195     /* See if a library was found with this name */
 196     if (handle == NULL) {
 197         ERROR_MESSAGE(("transport library not found: %s", name));
 198         return JDWP_ERROR(TRANSPORT_LOAD);
 199     }
 200 
 201     /* Find the onLoad address */
 202     onLoad = findTransportOnLoad(handle);
 203     if (onLoad == NULL) {
 204         ERROR_MESSAGE(("transport library missing onLoad entry: %s", name));
 205         return JDWP_ERROR(TRANSPORT_LOAD);
 206     }
 207 
 208     /* Get transport interface */
 209     env = getEnv();
 210     if (env != NULL) {
 211         jdwpTransportEnv *t = NULL;
 212         JavaVM           *jvm = NULL;
 213         jint              rc;
 214         size_t            i;
 215         /* If a new version is added here, update 'case JNI_EVERSION' below. */
 216         jint supported_versions[2] = {JDWPTRANSPORT_VERSION_1_1, JDWPTRANSPORT_VERSION_1_0};
 217 
 218         JNI_FUNC_PTR(env,GetJavaVM)(env, &jvm);
 219 
 220         /* Try version 1.1 first, fallback to 1.0 on error */
 221         for (i = 0; i < sizeof(supported_versions)/sizeof(jint); ++i) {
 222             rc = (*onLoad)(jvm, &callback, supported_versions[i], &t);
 223             if (rc != JNI_EVERSION) {
 224                 info->transportVersion = supported_versions[i];
 225                 break;
 226             }
 227         }
 228 
 229         if (rc != JNI_OK) {
 230             switch (rc) {
 231                 case JNI_ENOMEM :
 232                     ERROR_MESSAGE(("insufficient memory to complete initialization"));
 233                     break;
 234 
 235                 case JNI_EVERSION :
 236                     ERROR_MESSAGE(("transport doesn't recognize all supported versions: "
 237                                    "{ 1_1, 1_0 }"));
 238                     break;
 239 
 240                 case JNI_EEXIST :
 241                     ERROR_MESSAGE(("transport doesn't support multiple environments"));
 242                     break;
 243 
 244                 default:
 245                     ERROR_MESSAGE(("unrecognized error %d from transport", rc));
 246                     break;
 247             }
 248 
 249             return JDWP_ERROR(TRANSPORT_INIT);
 250         }
 251 
 252         /* Store transport version to global variable to be able to
 253          * set correct transport version for subsequent connect,
 254          * even if info is already deallocated.
 255          */
 256         transportVersion = info->transportVersion;
 257         info->transport = t;
 258     } else {
 259         return JDWP_ERROR(TRANSPORT_LOAD);
 260     }
 261 
 262     return JDWP_ERROR(NONE);
 263 }
 264 
 265 static void
 266 connectionInitiated(jdwpTransportEnv *t)
 267 {
 268     jint isValid = JNI_FALSE;
 269 
 270     debugMonitorEnter(listenerLock);
 271 
 272     /*
 273      * Don't allow a connection until initialization is complete
 274      */
 275     debugInit_waitInitComplete();
 276 
 277     /* Are we the first transport to get a connection? */
 278 
 279     if (transport == NULL) {
 280         transport = t;
 281         isValid = JNI_TRUE;
 282     } else {
 283         if (transport == t) {
 284             /* connected with the same transport as before */
 285             isValid = JNI_TRUE;
 286         } else {
 287             /*
 288              * Another transport got a connection - multiple transports
 289              * not fully supported yet so shouldn't get here.
 290              */
 291             (*t)->Close(t);
 292             JDI_ASSERT(JNI_FALSE);
 293         }
 294     }
 295 
 296     if (isValid) {
 297         debugMonitorNotifyAll(listenerLock);
 298     }
 299 
 300     debugMonitorExit(listenerLock);
 301 
 302     if (isValid) {
 303         debugLoop_run();
 304     }
 305 
 306 }
 307 
 308 /*
 309  * Set the transport property (sun.jdwp.listenerAddress) to the
 310  * specified value.
 311  */
 312 static void
 313 setTransportProperty(JNIEnv* env, char* value) {
 314     char* prop_value = (value == NULL) ? "" : value;
 315     setAgentPropertyValue(env, "sun.jdwp.listenerAddress", prop_value);
 316 }
 317 
 318 void
 319 transport_waitForConnection(void)
 320 {
 321     /*
 322      * If the VM is suspended on debugger initialization, we wait
 323      * for a connection before continuing. This ensures that all
 324      * events are delivered to the debugger. (We might as well do this
 325      * this since the VM won't continue until a remote debugger attaches
 326      * and resumes it.) If not suspending on initialization, we must
 327      * just drop any packets (i.e. events) so that the VM can continue
 328      * to run. The debugger may not attach until much later.
 329      */
 330     if (debugInit_suspendOnInit()) {
 331         debugMonitorEnter(listenerLock);
 332         while (transport == NULL) {
 333             debugMonitorWait(listenerLock);
 334         }
 335         debugMonitorExit(listenerLock);
 336     }
 337 }
 338 
 339 static void JNICALL
 340 acceptThread(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
 341 {
 342     TransportInfo *info;
 343     jdwpTransportEnv *t;
 344     jdwpTransportError rc;
 345 
 346     LOG_MISC(("Begin accept thread"));
 347 
 348     info = (TransportInfo*)(void*)arg;
 349     t = info->transport;
 350     rc = (*t)->Accept(t, info->timeout, 0);
 351 
 352     /* System property no longer needed */
 353     setTransportProperty(jni_env, NULL);
 354 
 355     if (rc != JDWPTRANSPORT_ERROR_NONE) {
 356         /*
 357          * If accept fails it probably means a timeout, or another fatal error
 358          * We thus exit the VM after stopping the listener.
 359          */
 360         printLastError(t, rc);
 361         (*t)->StopListening(t);
 362         EXIT_ERROR(JVMTI_ERROR_NONE, "could not connect, timeout or fatal error");
 363     } else {
 364         (*t)->StopListening(t);
 365         connectionInitiated(t);
 366     }
 367 
 368     LOG_MISC(("End accept thread"));
 369 }
 370 
 371 static void JNICALL
 372 attachThread(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
 373 {
 374     TransportInfo *info = (TransportInfo*)(void*)arg;
 375 
 376     LOG_MISC(("Begin attach thread"));
 377     connectionInitiated(info->transport);
 378     LOG_MISC(("End attach thread"));
 379 }
 380 
 381 void
 382 transport_initialize(void)
 383 {
 384     transport = NULL;
 385     listenerLock = debugMonitorCreate("JDWP Transport Listener Monitor");
 386     sendLock = debugMonitorCreate("JDWP Transport Send Monitor");
 387 }
 388 
 389 void
 390 transport_reset(void)
 391 {
 392     /*
 393      * Reset the transport by closing any listener (will silently fail
 394      * with JDWPTRANSPORT_ERROR_ILLEGAL_STATE if not listening), and
 395      * closing any connection (will also fail silently if not
 396      * connected).
 397      *
 398      * Note: There's an assumption here that we don't yet support
 399      * multiple transports. When we do then we need a clear transition
 400      * from the current transport to the new transport.
 401      */
 402     if (transport != NULL) {
 403         setTransportProperty(getEnv(), NULL);
 404         (*transport)->StopListening(transport);
 405         (*transport)->Close(transport);
 406     }
 407 }
 408 
 409 static jdwpError
 410 launch(char *command, char *name, char *address)
 411 {
 412     jint rc;
 413     char *buf;
 414     char *commandLine;
 415     int  len;
 416 
 417     /* Construct complete command line (all in UTF-8) */
 418     commandLine = jvmtiAllocate((int)strlen(command) +
 419                                  (int)strlen(name) +
 420                                  (int)strlen(address) + 3);
 421     if (commandLine == NULL) {
 422         return JDWP_ERROR(OUT_OF_MEMORY);
 423     }
 424     (void)strcpy(commandLine, command);
 425     (void)strcat(commandLine, " ");
 426     (void)strcat(commandLine, name);
 427     (void)strcat(commandLine, " ");
 428     (void)strcat(commandLine, address);
 429 
 430     /* Convert commandLine from UTF-8 to platform encoding */
 431     len = (int)strlen(commandLine);
 432     buf = jvmtiAllocate(len*3+3);
 433     if (buf == NULL) {
 434         jvmtiDeallocate(commandLine);
 435         return JDWP_ERROR(OUT_OF_MEMORY);
 436     }
 437     (void)utf8ToPlatform((jbyte*)commandLine, len, buf, len*3+3);
 438 
 439     /* Exec commandLine */
 440     rc = dbgsysExec(buf);
 441 
 442     /* Free up buffers */
 443     jvmtiDeallocate(buf);
 444     jvmtiDeallocate(commandLine);
 445 
 446     /* And non-zero exit status means we had an error */
 447     if (rc != SYS_OK) {
 448         return JDWP_ERROR(TRANSPORT_INIT);
 449     }
 450     return JDWP_ERROR(NONE);
 451 }
 452 
 453 jdwpError
 454 transport_startTransport(jboolean isServer, char *name, char *address,
 455                          long timeout, char *allowed_peers)
 456 {
 457     jvmtiStartFunction func;
 458     char threadName[MAXPATHLEN + 100];
 459     jint err;
 460     jdwpError serror;
 461     jdwpTransportConfiguration cfg = {0};
 462     TransportInfo *info;
 463     jdwpTransportEnv *trans;
 464 
 465     info = jvmtiAllocate(sizeof(*info));
 466     if (info == NULL) {
 467         return JDWP_ERROR(OUT_OF_MEMORY);
 468     }
 469 
 470     info->transport = transport;
 471     info->transportVersion = transportVersion;
 472     info->name = NULL;
 473     info->address = NULL;
 474     info->allowed_peers = NULL;
 475 
 476     /*
 477      * If the transport is already loaded then use it
 478      * Note: We're assuming here that we don't support multiple
 479      * transports - when we do then we need to handle the case
 480      * where the transport library only supports a single environment.
 481      * That probably means we have a bag a transport environments
 482      * to correspond to the transports bag.
 483      */
 484     if (info->transport == NULL) {
 485         serror = loadTransport(name, info);
 486         if (serror != JDWP_ERROR(NONE)) {
 487             jvmtiDeallocate(info);
 488             return serror;
 489         }
 490     }
 491 
 492     // Cache the value
 493     trans = info->transport;
 494 
 495     if (isServer) {
 496         char *retAddress;
 497         char *launchCommand;
 498         jvmtiError error;
 499         int len;
 500         char* prop_value;
 501 
 502         info->timeout = timeout;
 503 
 504         info->name = jvmtiAllocate((int)strlen(name)+1);
 505         if (info->name == NULL) {
 506             serror = JDWP_ERROR(OUT_OF_MEMORY);
 507             goto handleError;
 508         }
 509         (void)strcpy(info->name, name);
 510 
 511         if (address != NULL) {
 512             info->address = jvmtiAllocate((int)strlen(address)+1);
 513             if (info->address == NULL) {
 514                 serror = JDWP_ERROR(OUT_OF_MEMORY);
 515                 goto handleError;
 516             }
 517             (void)strcpy(info->address, address);
 518         }
 519 
 520         if (info->transportVersion == JDWPTRANSPORT_VERSION_1_0) {
 521             if (allowed_peers != NULL) {
 522                 ERROR_MESSAGE(("Allow parameter is specified but transport doesn't support it"));
 523                 serror = JDWP_ERROR(TRANSPORT_INIT);
 524                 goto handleError;
 525             }
 526         } else {
 527             /* Memory is allocated only for transport versions > 1.0
 528              * as the version 1.0 does not support the 'allow' option.
 529              */
 530             if (allowed_peers != NULL) {
 531                 info->allowed_peers = jvmtiAllocate((int)strlen(allowed_peers) + 1);
 532                 if (info->allowed_peers == NULL) {
 533                     serror = JDWP_ERROR(OUT_OF_MEMORY);
 534                     goto handleError;
 535                 }
 536                 (void)strcpy(info->allowed_peers, allowed_peers);
 537             }
 538             cfg.allowed_peers = info->allowed_peers;
 539             err = (*trans)->SetTransportConfiguration(trans, &cfg);
 540             if (err != JDWPTRANSPORT_ERROR_NONE) {
 541                 printLastError(trans, err);
 542                 serror = JDWP_ERROR(TRANSPORT_INIT);
 543                 goto handleError;
 544             }
 545         }
 546 
 547         err = (*trans)->StartListening(trans, address, &retAddress);
 548         if (err != JDWPTRANSPORT_ERROR_NONE) {
 549             printLastError(trans, err);
 550             serror = JDWP_ERROR(TRANSPORT_INIT);
 551             goto handleError;
 552         }
 553 
 554         /*
 555          * Record listener address in a system property
 556          */
 557         len = (int)strlen(name) + (int)strlen(retAddress) + 2; /* ':' and '\0' */
 558         prop_value = (char*)jvmtiAllocate(len);
 559         if (prop_value == NULL) {
 560             serror = JDWP_ERROR(OUT_OF_MEMORY);
 561             goto handleError;
 562         }
 563         strcpy(prop_value, name);
 564         strcat(prop_value, ":");
 565         strcat(prop_value, retAddress);
 566         setTransportProperty(getEnv(), prop_value);
 567         jvmtiDeallocate(prop_value);
 568 
 569 
 570         (void)strcpy(threadName, "JDWP Transport Listener: ");
 571         (void)strcat(threadName, name);
 572 
 573         func = &acceptThread;
 574         error = spawnNewThread(func, (void*)info, threadName);
 575         if (error != JVMTI_ERROR_NONE) {
 576             serror = map2jdwpError(error);
 577             goto handleError;
 578         }
 579 
 580         launchCommand = debugInit_launchOnInit();
 581         if (launchCommand != NULL) {
 582             serror = launch(launchCommand, name, retAddress);
 583             if (serror != JDWP_ERROR(NONE)) {
 584                 goto handleError;
 585             }
 586         } else {
 587             if ( ! gdata->quiet ) {
 588                 TTY_MESSAGE(("Listening for transport %s at address: %s",
 589                     name, retAddress));
 590             }
 591         }
 592         return JDWP_ERROR(NONE);
 593 
 594 handleError:
 595         jvmtiDeallocate(info->name);
 596         jvmtiDeallocate(info->address);
 597         jvmtiDeallocate(info->allowed_peers);
 598         jvmtiDeallocate(info);
 599     } else {
 600         /*
 601          * Note that we don't attempt to do a launch here. Launching
 602          * is currently supported only in server mode.
 603          */
 604 
 605         /*
 606          * If we're connecting to another process, there shouldn't be
 607          * any concurrent listens, so its ok if we block here in this
 608          * thread, waiting for the attach to finish.
 609          */
 610          err = (*trans)->Attach(trans, address, timeout, 0);
 611          if (err != JDWPTRANSPORT_ERROR_NONE) {
 612              printLastError(trans, err);
 613              serror = JDWP_ERROR(TRANSPORT_INIT);
 614              /* The name, address and allowed_peers fields in 'info'
 615               * are not allocated in the non-server case so
 616               * they do not need to be freed. */
 617              jvmtiDeallocate(info);
 618              return serror;
 619          }
 620 
 621          /*
 622           * Start the transport loop in a separate thread
 623           */
 624          (void)strcpy(threadName, "JDWP Transport Listener: ");
 625          (void)strcat(threadName, name);
 626 
 627          func = &attachThread;
 628          err = spawnNewThread(func, (void*)info, threadName);
 629          serror = map2jdwpError(err);
 630     }
 631     return serror;
 632 }
 633 
 634 void
 635 transport_close(void)
 636 {
 637     if ( transport != NULL ) {
 638         (*transport)->Close(transport);
 639     }
 640 }
 641 
 642 jboolean
 643 transport_is_open(void)
 644 {
 645     jboolean is_open = JNI_FALSE;
 646 
 647     if ( transport != NULL ) {
 648         is_open = (*transport)->IsOpen(transport);
 649     }
 650     return is_open;
 651 }
 652 
 653 jint
 654 transport_sendPacket(jdwpPacket *packet)
 655 {
 656     jdwpTransportError err = JDWPTRANSPORT_ERROR_NONE;
 657     jint rc = 0;
 658 
 659     if (transport != NULL) {
 660         if ( (*transport)->IsOpen(transport) ) {
 661             debugMonitorEnter(sendLock);
 662             err = (*transport)->WritePacket(transport, packet);
 663             debugMonitorExit(sendLock);
 664         }
 665         if (err != JDWPTRANSPORT_ERROR_NONE) {
 666             if ((*transport)->IsOpen(transport)) {
 667                 printLastError(transport, err);
 668             }
 669 
 670             /*
 671              * The users of transport_sendPacket except 0 for
 672              * success; non-0 otherwise.
 673              */
 674             rc = (jint)-1;
 675         }
 676 
 677     } /* else, bit bucket */
 678 
 679     return rc;
 680 }
 681 
 682 jint
 683 transport_receivePacket(jdwpPacket *packet)
 684 {
 685     jdwpTransportError err;
 686 
 687     err = (*transport)->ReadPacket(transport, packet);
 688     if (err != JDWPTRANSPORT_ERROR_NONE) {
 689         /*
 690          * If transport has been closed return EOF
 691          */
 692         if (!(*transport)->IsOpen(transport)) {
 693             packet->type.cmd.len = 0;
 694             return 0;
 695         }
 696 
 697         printLastError(transport, err);
 698 
 699         /*
 700          * Users of transport_receivePacket expect 0 for success,
 701          * non-0 otherwise.
 702          */
 703         return (jint)-1;
 704     }
 705     return 0;
 706 }