attachListener_solaris.cpp revision 1472:c18cbe5936b8
1139826Simp/*
253541Sshin * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
353541Sshin * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
453541Sshin *
553541Sshin * This code is free software; you can redistribute it and/or modify it
653541Sshin * under the terms of the GNU General Public License version 2 only, as
753541Sshin * published by the Free Software Foundation.
853541Sshin *
953541Sshin * This code is distributed in the hope that it will be useful, but WITHOUT
1053541Sshin * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1153541Sshin * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1253541Sshin * version 2 for more details (a copy is included in the LICENSE file that
1353541Sshin * accompanied this code).
1453541Sshin *
1553541Sshin * You should have received a copy of the GNU General Public License version
1653541Sshin * 2 along with this work; if not, write to the Free Software Foundation,
1753541Sshin * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1853541Sshin *
1953541Sshin * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2053541Sshin * or visit www.oracle.com if you need additional information or have any
2153541Sshin * questions.
2253541Sshin *
2353541Sshin */
2453541Sshin
2553541Sshin# include "incls/_precompiled.incl"
2653541Sshin# include "incls/_attachListener_solaris.cpp.incl"
2753541Sshin
28174510Sobrien#include <door.h>
29174510Sobrien#include <string.h>
3053541Sshin#include <signal.h>
3153541Sshin#include <sys/types.h>
32174510Sobrien#include <sys/socket.h>
33174510Sobrien#include <sys/stat.h>
34174510Sobrien
3562587Sitojun// stropts.h uses STR in stream ioctl defines
3662587Sitojun#undef STR
37142215Sglebius#include <stropts.h>
38142215Sglebius#undef STR
39178167Sqingli#define STR(a) #a
4055009Sshin
4153541Sshin// The attach mechanism on Solaris is implemented using the Doors IPC
4253541Sshin// mechanism. The first tool to attempt to attach causes the attach
4353541Sshin// listener thread to startup. This thread creats a door that is
4453541Sshin// associated with a function that enqueues an operation to the attach
4553541Sshin// listener. The door is attached to a file in the file system so that
4653541Sshin// client (tools) can locate it. To enqueue an operation to the VM the
4753541Sshin// client calls through the door which invokes the enqueue function in
4853541Sshin// this process. The credentials of the client are checked and if the
4953541Sshin// effective uid matches this process then the operation is enqueued.
5053541Sshin// When an operation completes the attach listener is required to send the
5153541Sshin// operation result and any result data to the client. In this implementation
5278064Sume// the result is returned via a UNIX domain socket. A pair of connected
53181803Sbz// sockets (socketpair) is created in the enqueue function and the file
5453541Sshin// descriptor for one of the sockets is returned to the client as the
5553541Sshin// return from the door call. The other end is retained in this process.
5653541Sshin// When the operation completes the result is sent to the client and
5753541Sshin// the socket is closed.
58147306Sbrooks
5953541Sshin// forward reference
60178167Sqingliclass SolarisAttachOperation;
61178167Sqingli
62178167Sqingliclass SolarisAttachListener: AllStatic {
6353541Sshin private:
6453541Sshin
6553541Sshin  // the path to which we attach the door file descriptor
6653541Sshin  static char _door_path[PATH_MAX+1];
67151477Ssuz  static volatile bool _has_door_path;
6862587Sitojun
6953541Sshin  // door descriptor returned by door_create
70148385Sume  static int _door_descriptor;
7153541Sshin
7262587Sitojun  static void set_door_path(char* path) {
7353541Sshin    if (path == NULL) {
74142215Sglebius      _has_door_path = false;
75142215Sglebius    } else {
76142215Sglebius      strncpy(_door_path, path, PATH_MAX);
77142215Sglebius      _door_path[PATH_MAX] = '\0';      // ensure it's nul terminated
7862587Sitojun      _has_door_path = true;
7953541Sshin    }
8062587Sitojun  }
81175162Sobrien
82175162Sobrien  static void set_door_descriptor(int dd)               { _door_descriptor = dd; }
83175162Sobrien
84175162Sobrien  // mutex to protect operation list
85175162Sobrien  static mutex_t _mutex;
86175162Sobrien
87175162Sobrien  // semaphore to wakeup listener thread
8853541Sshin  static sema_t _wakeup;
8962587Sitojun
9062587Sitojun  static mutex_t* mutex()                               { return &_mutex; }
9153541Sshin  static sema_t* wakeup()                               { return &_wakeup; }
9253541Sshin
93108470Sschweikh  // enqueued operation list
9453541Sshin  static SolarisAttachOperation* _head;
9553541Sshin  static SolarisAttachOperation* _tail;
96148987Sume
9753541Sshin  static SolarisAttachOperation* head()                 { return _head; }
9853541Sshin  static void set_head(SolarisAttachOperation* head)    { _head = head; }
99171259Sdelphij
10053541Sshin  static SolarisAttachOperation* tail()                 { return _tail; }
10153541Sshin  static void set_tail(SolarisAttachOperation* tail)    { _tail = tail; }
10253541Sshin
10362587Sitojun  // create the door
10453541Sshin  static int create_door();
10553541Sshin
10662587Sitojun public:
10753541Sshin  enum {
10853541Sshin    ATTACH_PROTOCOL_VER = 1                             // protocol version
109142215Sglebius  };
11053541Sshin  enum {
11153541Sshin    ATTACH_ERROR_BADREQUEST     = 100,                  // error code returned by
11253541Sshin    ATTACH_ERROR_BADVERSION     = 101,                  // the door call
11353541Sshin    ATTACH_ERROR_RESOURCE       = 102,
11462587Sitojun    ATTACH_ERROR_INTERNAL       = 103,
115165118Sbz    ATTACH_ERROR_DENIED         = 104
11653541Sshin  };
11778064Sume
11878064Sume  // initialize the listener
11978064Sume  static int init();
12078064Sume
12178064Sume  static bool has_door_path()                           { return _has_door_path; }
12278064Sume  static char* door_path()                              { return _door_path; }
123181803Sbz  static int door_descriptor()                          { return _door_descriptor; }
12478064Sume
12578064Sume  // enqueue an operation
12678064Sume  static void enqueue(SolarisAttachOperation* op);
12778064Sume
12878064Sume  // dequeue an operation
129148385Sume  static SolarisAttachOperation* dequeue();
130148385Sume};
13178064Sume
13253541Sshin
13378064Sume// SolarisAttachOperation is an AttachOperation that additionally encapsulates
13478064Sume// a socket connection to the requesting client/tool. SolarisAttachOperation
135165118Sbz// can additionally be held in a linked list.
136165118Sbz
13778064Sumeclass SolarisAttachOperation: public AttachOperation {
13853541Sshin private:
13953541Sshin  friend class SolarisAttachListener;
14053541Sshin
141148987Sume  // connection to client
142120941Sume  int _socket;
14395023Ssuz
144120941Sume  // linked list support
145120941Sume  SolarisAttachOperation* _next;
146120941Sume
14795023Ssuz  SolarisAttachOperation* next()                         { return _next; }
14853541Sshin  void set_next(SolarisAttachOperation* next)            { _next = next; }
14978064Sume
150120941Sume public:
15153541Sshin  void complete(jint res, bufferedStream* st);
15253541Sshin
15353541Sshin  int socket() const                                     { return _socket; }
15453541Sshin  void set_socket(int s)                                 { _socket = s; }
15553541Sshin
15678064Sume  SolarisAttachOperation(char* name) : AttachOperation(name) {
15753541Sshin    set_socket(-1);
15853541Sshin    set_next(NULL);
15953541Sshin  }
16053541Sshin};
16153541Sshin
16253541Sshin// statics
16378064Sumechar SolarisAttachListener::_door_path[PATH_MAX+1];
16478064Sumevolatile bool SolarisAttachListener::_has_door_path;
16578064Sumeint SolarisAttachListener::_door_descriptor = -1;
16678064Sumemutex_t SolarisAttachListener::_mutex;
16753541Sshinsema_t SolarisAttachListener::_wakeup;
16853541SshinSolarisAttachOperation* SolarisAttachListener::_head = NULL;
16953541SshinSolarisAttachOperation* SolarisAttachListener::_tail = NULL;
17095023Ssuz
17153541Sshin// Supporting class to help split a buffer into individual components
17253541Sshinclass ArgumentIterator : public StackObj {
173120941Sume private:
17453541Sshin  char* _pos;
17578064Sume  char* _end;
17678064Sume public:
17753541Sshin  ArgumentIterator(char* arg_buffer, size_t arg_size) {
17853541Sshin    _pos = arg_buffer;
17953541Sshin    _end = _pos + arg_size - 1;
18053541Sshin  }
18153541Sshin  char* next() {
18253541Sshin    if (*_pos == '\0') {
18353541Sshin      return NULL;
18453541Sshin    }
18553541Sshin    char* res = _pos;
18653541Sshin    char* next_pos = strchr(_pos, '\0');
18753541Sshin    if (next_pos < _end)  {
18853541Sshin      next_pos++;
18953541Sshin    }
19053541Sshin    _pos = next_pos;
19153541Sshin    return res;
19253541Sshin  }
19353541Sshin};
19453541Sshin
19553541Sshin// Calls from the door function to check that the client credentials
19653541Sshin// match this process. Returns 0 if credentials okay, otherwise -1.
19753541Sshinstatic int check_credentials() {
19853541Sshin  door_cred_t cred_info;
19953541Sshin
20053541Sshin  // get client credentials
20153541Sshin  if (door_cred(&cred_info) == -1) {
202142215Sglebius    return -1; // unable to get them
203142215Sglebius  }
204142215Sglebius
205151465Ssuz  // get our euid/eguid (probably could cache these)
206142215Sglebius  uid_t euid = geteuid();
207142215Sglebius  gid_t egid = getegid();
20853541Sshin
209142215Sglebius  // check that the effective uid/gid matches - discuss this with Jeff.
21053541Sshin  if (cred_info.dc_euid == euid && cred_info.dc_egid == egid) {
21153541Sshin    return 0;  // okay
212151465Ssuz  } else {
21353541Sshin    return -1; // denied
21453541Sshin  }
215121765Ssam}
216178167Sqingli
217178167Sqingli
218178167Sqingli// Parses the argument buffer to create an AttachOperation that we should
21953541Sshin// enqueue to the attach listener.
220171260Sdelphij// The buffer is expected to be formatted as follows:
22153541Sshin// <ver>0<cmd>0<arg>0<arg>0<arg>0
22253541Sshin// where <ver> is the version number (must be "1"), <cmd> is the command
22353541Sshin// name ("load, "datadump", ...) and <arg> is an argument.
22453541Sshin//
225178167Sqinglistatic SolarisAttachOperation* create_operation(char* argp, size_t arg_size, int* err) {
226178167Sqingli  // assume bad request until parsed
227178167Sqingli  *err = SolarisAttachListener::ATTACH_ERROR_BADREQUEST;
228178167Sqingli
229178167Sqingli  if (arg_size < 2 || argp[arg_size-1] != '\0') {
230178167Sqingli    return NULL;   // no ver or not null terminated
23153541Sshin  }
232178167Sqingli
233121765Ssam  // Use supporting class to iterate over the buffer
234121765Ssam  ArgumentIterator args(argp, arg_size);
235121765Ssam
236121765Ssam  // First check the protocol version
237121765Ssam  char* ver = args.next();
23853541Sshin  if (ver == NULL) {
23962587Sitojun    return NULL;
24053541Sshin  }
24162587Sitojun  if (atoi(ver) != SolarisAttachListener::ATTACH_PROTOCOL_VER) {
24262587Sitojun    *err = SolarisAttachListener::ATTACH_ERROR_BADVERSION;
24362587Sitojun    return NULL;
24453541Sshin  }
24562587Sitojun
24662587Sitojun  // Get command name and create the operation
24753541Sshin  char* name = args.next();
24853541Sshin  if (name == NULL || strlen(name) > AttachOperation::name_length_max) {
249151479Ssuz    return NULL;
25053541Sshin  }
25178064Sume  SolarisAttachOperation* op = new SolarisAttachOperation(name);
25253541Sshin
25353541Sshin  // Iterate over the arguments
25453541Sshin  for (int i=0; i<AttachOperation::arg_count_max; i++) {
25562587Sitojun    char* arg = args.next();
25653541Sshin    if (arg == NULL) {
25753541Sshin      op->set_arg(i, NULL);
25853541Sshin    } else {
25953541Sshin      if (strlen(arg) > AttachOperation::arg_length_max) {
26053541Sshin        delete op;
26162587Sitojun        return NULL;
26253541Sshin      }
26353541Sshin      op->set_arg(i, arg);
264120941Sume    }
26553541Sshin  }
266165118Sbz
267120941Sume  // return operation
26878064Sume  *err = 0;
26953541Sshin  return op;
27053541Sshin}
27153541Sshin
272120941Sume// create special operation to indicate all clients have detached
273165118Sbzstatic SolarisAttachOperation* create_detachall_operation() {
27462587Sitojun  return new SolarisAttachOperation(AttachOperation::detachall_operation_name());
27553541Sshin}
27653541Sshin
27753541Sshin// This is door function which the client executes via a door_call.
27853541Sshinextern "C" {
27953541Sshin  static void enqueue_proc(void* cookie, char* argp, size_t arg_size,
28053541Sshin                           door_desc_t* dt, uint_t n_desc)
28153541Sshin  {
28253541Sshin    int return_fd = -1;
28353541Sshin    SolarisAttachOperation* op = NULL;
28453541Sshin
28553541Sshin    // no listener
28653541Sshin    jint res = 0;
28753541Sshin    if (!AttachListener::is_initialized()) {
28853541Sshin      // how did we get here?
28953541Sshin      debug_only(warning("door_call when not enabled"));
29053541Sshin      res = (jint)SolarisAttachListener::ATTACH_ERROR_INTERNAL;
29153541Sshin    }
292148987Sume
29353541Sshin    // check client credentials
29453541Sshin    if (res == 0) {
29553541Sshin      if (check_credentials() != 0) {
29653541Sshin        res = (jint)SolarisAttachListener::ATTACH_ERROR_DENIED;
29753541Sshin      }
29853541Sshin    }
29953541Sshin
30062587Sitojun    // if we are stopped at ShowMessageBoxOnError then maybe we can
30153541Sshin    // load a diagnostic library
30253541Sshin    if (res == 0 && is_error_reported()) {
30353541Sshin      if (ShowMessageBoxOnError) {
30453541Sshin        // TBD - support loading of diagnostic library here
30553541Sshin      }
30653541Sshin
30753541Sshin      // can't enqueue operation after fatal error
30853541Sshin      res = (jint)SolarisAttachListener::ATTACH_ERROR_RESOURCE;
30953541Sshin    }
31053541Sshin
31153541Sshin    // create the operation
312148385Sume    if (res == 0) {
313148385Sume      int err;
314148385Sume      op = create_operation(argp, arg_size, &err);
315148385Sume      res = (op == NULL) ? (jint)err : 0;
316148385Sume    }
317148385Sume
318120941Sume    // create a pair of connected sockets. Store the file descriptor
319181803Sbz    // for one end in the operation and enqueue the operation. The
320120941Sume    // file descriptor for the other end wil be returned to the client.
32162587Sitojun    if (res == 0) {
32253541Sshin      int s[2];
32353541Sshin      if (socketpair(PF_UNIX, SOCK_STREAM, 0, s) < 0) {
324120941Sume        delete op;
325120941Sume        res = (jint)SolarisAttachListener::ATTACH_ERROR_RESOURCE;
32653541Sshin      } else {
32753541Sshin        op->set_socket(s[0]);
328120941Sume        return_fd = s[1];
329181803Sbz        SolarisAttachListener::enqueue(op);
330120941Sume      }
33162587Sitojun    }
33262587Sitojun
33353541Sshin    // Return 0 (success) + file descriptor, or non-0 (error)
33453541Sshin    if (res == 0) {
33553541Sshin      door_desc_t desc;
336165118Sbz      desc.d_attributes = DOOR_DESCRIPTOR;
337165118Sbz      desc.d_data.d_desc.d_descriptor = return_fd;
338165118Sbz      door_return((char*)&res, sizeof(res), &desc, 1);
339165118Sbz    } else {
340165118Sbz      door_return((char*)&res, sizeof(res), NULL, 0);
341165118Sbz    }
342181803Sbz  }
34362587Sitojun}
34453541Sshin
34553541Sshin// atexit hook to detach the door and remove the file
34653541Sshinextern "C" {
347108470Sschweikh  static void listener_cleanup() {
34853541Sshin    static int cleanup_done;
34953541Sshin    if (!cleanup_done) {
35053541Sshin      cleanup_done = 1;
35153541Sshin      int dd = SolarisAttachListener::door_descriptor();
35253541Sshin      if (dd >= 0) {
353148987Sume        ::close(dd);
354171259Sdelphij      }
355171259Sdelphij      if (SolarisAttachListener::has_door_path()) {
356171259Sdelphij        char* path = SolarisAttachListener::door_path();
35753541Sshin        ::fdetach(path);
35853541Sshin        ::unlink(path);
359171259Sdelphij      }
360171259Sdelphij    }
36153541Sshin  }
36253541Sshin}
36353541Sshin
36453541Sshin// Create the door
365148385Sumeint SolarisAttachListener::create_door() {
36653541Sshin  char door_path[PATH_MAX+1];
36753541Sshin  int fd, res;
36862587Sitojun
36953541Sshin  // register exit function
370148385Sume  ::atexit(listener_cleanup);
371120941Sume
372148385Sume  // create the door descriptor
373148385Sume  int dd = ::door_create(enqueue_proc, NULL, 0);
37453541Sshin  if (dd < 0) {
37553541Sshin    return -1;
37653541Sshin  }
37762587Sitojun
37862587Sitojun  snprintf(door_path, sizeof(door_path), "%s/.java_pid%d",
37962587Sitojun           os::get_temp_directory(), os::current_process_id());
38062587Sitojun  RESTARTABLE(::creat(door_path, S_IRUSR | S_IWUSR), fd);
38162587Sitojun
38262587Sitojun  if (fd == -1) {
38362587Sitojun    debug_only(warning("attempt to create %s failed", door_path));
38462587Sitojun    return -1;
38553541Sshin  }
38662587Sitojun  assert(fd >= 0, "bad file descriptor");
38753541Sshin  set_door_path(door_path);
388111119Simp  RESTARTABLE(::close(fd), res);
38962587Sitojun
390111119Simp  // attach the door descriptor to the file
39162587Sitojun  if ((res = ::fattach(dd, door_path)) == -1) {
39262587Sitojun    // if busy then detach and try again
39362587Sitojun    if (errno == EBUSY) {
39462587Sitojun      ::fdetach(door_path);
39562587Sitojun      res = ::fattach(dd, door_path);
39662587Sitojun    }
39762587Sitojun    if (res == -1) {
39878064Sume      ::door_revoke(dd);
39962587Sitojun      dd = -1;
40053541Sshin    }
40153541Sshin  }
40253541Sshin  if (dd >= 0) {
40353541Sshin    set_door_descriptor(dd);
40453541Sshin  } else {
40553541Sshin    // unable to create door or attach it to the file
40653541Sshin    ::unlink(door_path);
40753541Sshin    set_door_path(NULL);
40853541Sshin    return -1;
40995023Ssuz  }
41053541Sshin
41153541Sshin  return 0;
41253541Sshin}
41353541Sshin
41462587Sitojun// Initialization - create the door, locks, and other initialization
41562587Sitojunint SolarisAttachListener::init() {
41653541Sshin  if (create_door()) {
41753541Sshin    return -1;
41853541Sshin  }
41953541Sshin
42053541Sshin  int status = os::Solaris::mutex_init(&_mutex);
42153541Sshin  assert_status(status==0, status, "mutex_init");
42253541Sshin
423148385Sume  status = ::sema_init(&_wakeup, 0, NULL, NULL);
42453541Sshin  assert_status(status==0, status, "sema_init");
42553541Sshin
42653541Sshin  set_head(NULL);
42753541Sshin  set_tail(NULL);
428148385Sume
429148385Sume  return 0;
43053541Sshin}
43153541Sshin
43253541Sshin// Dequeue an operation
43353541SshinSolarisAttachOperation* SolarisAttachListener::dequeue() {
43453541Sshin  for (;;) {
43553541Sshin    int res;
43653541Sshin
43753541Sshin    // wait for somebody to enqueue something
43853541Sshin    while ((res = ::sema_wait(wakeup())) == EINTR)
43953541Sshin      ;
44053541Sshin    if (res) {
44153541Sshin      warning("sema_wait failed: %s", strerror(res));
44253541Sshin      return NULL;
44353541Sshin    }
44453541Sshin
445148385Sume    // lock the list
44653541Sshin    res = os::Solaris::mutex_lock(mutex());
44795023Ssuz    assert(res == 0, "mutex_lock failed");
448148385Sume
44953541Sshin    // remove the head of the list
45053541Sshin    SolarisAttachOperation* op = head();
451148987Sume    if (op != NULL) {
452148987Sume      set_head(op->next());
453148987Sume      if (head() == NULL) {
454148987Sume        set_tail(NULL);
45553541Sshin      }
45653541Sshin    }
45753541Sshin
458148385Sume    // unlock
45953541Sshin    os::Solaris::mutex_unlock(mutex());
460148385Sume
461148385Sume    // if we got an operation when return it.
462148385Sume    if (op != NULL) {
463148385Sume      return op;
46453541Sshin    }
465148385Sume  }
466148385Sume}
467148385Sume
468148385Sume// Enqueue an operation
469148385Sumevoid SolarisAttachListener::enqueue(SolarisAttachOperation* op) {
470148385Sume  // lock list
471148385Sume  int res = os::Solaris::mutex_lock(mutex());
472148385Sume  assert(res == 0, "mutex_lock failed");
473148385Sume
474148385Sume  // enqueue at tail
475148385Sume  op->set_next(NULL);
476165118Sbz  if (head() == NULL) {
477148385Sume    set_head(op);
478148385Sume  } else {
479148385Sume    tail()->set_next(op);
480165118Sbz  }
481165118Sbz  set_tail(op);
482148385Sume
48353541Sshin  // wakeup the attach listener
48453541Sshin  RESTARTABLE(::sema_post(wakeup()), res);
48553541Sshin  assert(res == 0, "sema_post failed");
48653541Sshin
48753541Sshin  // unlock
48853541Sshin  os::Solaris::mutex_unlock(mutex());
489148385Sume}
490148385Sume
491148385Sume
49253541Sshin// support function - writes the (entire) buffer to a socket
493148385Sumestatic int write_fully(int s, char* buf, int len) {
494148385Sume  do {
49553541Sshin    int n = ::write(s, buf, len);
496148385Sume    if (n == -1) {
49753541Sshin      if (errno != EINTR) return -1;
49853541Sshin    } else {
49953541Sshin      buf += n;
50053541Sshin      len -= n;
50153541Sshin    }
502121315Sume  }
50353541Sshin  while (len > 0);
50453541Sshin  return 0;
50553541Sshin}
50653541Sshin
50753541Sshin// Complete an operation by sending the operation result and any result
50853541Sshin// output to the client. At this time the socket is in blocking mode so
50953541Sshin// potentially we can block if there is a lot of data and the client is
51053541Sshin// non-responsive. For most operations this is a non-issue because the
51153541Sshin// default send buffer is sufficient to buffer everything. In the future
51253541Sshin// if there are operations that involves a very big reply then it the
51353541Sshin// socket could be made non-blocking and a timeout could be used.
51453541Sshin
51553541Sshinvoid SolarisAttachOperation::complete(jint res, bufferedStream* st) {
51653541Sshin  if (this->socket() >= 0) {
51753541Sshin    JavaThread* thread = JavaThread::current();
51853541Sshin    ThreadBlockInVM tbivm(thread);
51953541Sshin
52053541Sshin    thread->set_suspend_equivalent();
521120941Sume    // cleared by handle_special_suspend_equivalent_condition() or
52253541Sshin    // java_suspend_self() via check_and_wait_while_suspended()
52353541Sshin
52453541Sshin    // write operation result
52553541Sshin    char msg[32];
52653541Sshin    sprintf(msg, "%d\n", res);
52753541Sshin    int rc = write_fully(this->socket(), msg, strlen(msg));
52853541Sshin
52953541Sshin    // write any result data
53053541Sshin    if (rc == 0) {
53153541Sshin      write_fully(this->socket(), (char*) st->base(), st->size());
53253541Sshin      ::shutdown(this->socket(), 2);
533120941Sume    }
534120941Sume
53553541Sshin    // close socket and we're done
536151536Ssuz    RESTARTABLE(::close(this->socket()), rc);
537148385Sume
538148385Sume    // were we externally suspended while we were waiting?
539181803Sbz    thread->check_and_wait_while_suspended();
540148385Sume  }
541148385Sume  delete this;
542148385Sume}
54353541Sshin
544148385Sume
545148385Sume// AttachListener functions
546148385Sume
547148385SumeAttachOperation* AttachListener::dequeue() {
548148385Sume  JavaThread* thread = JavaThread::current();
549148385Sume  ThreadBlockInVM tbivm(thread);
550148385Sume
551148385Sume  thread->set_suspend_equivalent();
55253541Sshin  // cleared by handle_special_suspend_equivalent_condition() or
55353541Sshin  // java_suspend_self() via check_and_wait_while_suspended()
55453541Sshin
55553541Sshin  AttachOperation* op = SolarisAttachListener::dequeue();
55653541Sshin
55753541Sshin  // were we externally suspended while we were waiting?
558148987Sume  thread->check_and_wait_while_suspended();
55962587Sitojun
56062587Sitojun  return op;
56162587Sitojun}
56262587Sitojun
56353541Sshinint AttachListener::pd_init() {
56453541Sshin  JavaThread* thread = JavaThread::current();
565171259Sdelphij  ThreadBlockInVM tbivm(thread);
56653541Sshin
56753541Sshin  thread->set_suspend_equivalent();
56853541Sshin  // cleared by handle_special_suspend_equivalent_condition() or
56962587Sitojun  // java_suspend_self()
57053541Sshin
57162587Sitojun  int ret_code = SolarisAttachListener::init();
57262587Sitojun
57362587Sitojun  // were we externally suspended while we were waiting?
57462587Sitojun  thread->check_and_wait_while_suspended();
57562587Sitojun
57653541Sshin  return ret_code;
57753541Sshin}
57853541Sshin
57953541Sshin// Attach Listener is started lazily except in the case when
58053541Sshin// +ReduseSignalUsage is used
58153541Sshinbool AttachListener::init_at_startup() {
58253541Sshin  if (ReduceSignalUsage) {
583165118Sbz    return true;
58453541Sshin  } else {
58553541Sshin    return false;
58678064Sume  }
58778064Sume}
588165118Sbz
589165118Sbz// If the file .attach_pid<pid> exists in the working directory
59078064Sume// or /tmp then this is the trigger to start the attach mechanism
59162587Sitojunbool AttachListener::is_init_trigger() {
59262587Sitojun  if (init_at_startup() || is_initialized()) {
59362587Sitojun    return false;               // initialized at startup or already initialized
59462587Sitojun  }
59562587Sitojun  char fn[PATH_MAX+1];
59662587Sitojun  sprintf(fn, ".attach_pid%d", os::current_process_id());
59762587Sitojun  int ret;
59862587Sitojun  struct stat64 st;
599181803Sbz  RESTARTABLE(::stat64(fn, &st), ret);
60053541Sshin  if (ret == -1) {
60153541Sshin    snprintf(fn, sizeof(fn), "%s/.attach_pid%d",
60262587Sitojun             os::get_temp_directory(), os::current_process_id());
603148385Sume    RESTARTABLE(::stat64(fn, &st), ret);
60462587Sitojun  }
60562587Sitojun  if (ret == 0) {
60662587Sitojun    // simple check to avoid starting the attach mechanism when
60762587Sitojun    // a bogus user creates the file
60853541Sshin    if (st.st_uid == geteuid()) {
609148385Sume      init();
610148385Sume      return true;
611150202Ssuz    }
61253541Sshin  }
61353541Sshin  return false;
61478064Sume}
61553541Sshin
616165118Sbz// if VM aborts then detach/cleanup
61778064Sumevoid AttachListener::abort() {
61853541Sshin  listener_cleanup();
61953541Sshin}
62053541Sshin
62178064Sumevoid AttachListener::pd_data_dump() {
62278064Sume  os::signal_notify(SIGQUIT);
62378064Sume}
62453541Sshin
62553541Sshinstatic jint enable_dprobes(AttachOperation* op, outputStream* out) {
62653541Sshin  const char* probe = op->arg(0);
62753541Sshin  if (probe == NULL || probe[0] == '\0') {
62853541Sshin    out->print_cr("No probe specified");
62978064Sume    return JNI_ERR;
63078064Sume  } else {
63178064Sume    int probe_typess = atoi(probe);
63262587Sitojun    if (errno) {
63353541Sshin      out->print_cr("invalid probe type");
63453541Sshin      return JNI_ERR;
63553541Sshin    } else {
63653541Sshin      DTrace::enable_dprobes(probe_typess);
63753541Sshin      return JNI_OK;
63853541Sshin    }
63953541Sshin  }
64053541Sshin}
64153541Sshin
64253541Sshin// platform specific operations table
64353541Sshinstatic AttachOperationFunctionInfo funcs[] = {
64453541Sshin  { "enabledprobes", enable_dprobes },
64553541Sshin  { NULL, NULL }
64653541Sshin};
64753541Sshin
64853541SshinAttachOperationFunctionInfo* AttachListener::pd_find_operation(const char* name) {
64953541Sshin  int i;
65053541Sshin  for (i = 0; funcs[i].name != NULL; i++) {
65153541Sshin    if (strcmp(funcs[i].name, name) == 0) {
65253541Sshin      return &funcs[i];
65353541Sshin    }
65462587Sitojun  }
65553541Sshin  return NULL;
65653541Sshin}
65795023Ssuz
65853541Sshin// Solaris specific global flag set. Currently, we support only
65953541Sshin// changing ExtendedDTraceProbes flag.
66053541Sshinjint AttachListener::pd_set_flag(AttachOperation* op, outputStream* out) {
661165118Sbz  const char* name = op->arg(0);
66262587Sitojun  assert(name != NULL, "flag name should not be null");
66353541Sshin  bool flag = true;
66453541Sshin  const char* arg1;
66553541Sshin  if ((arg1 = op->arg(1)) != NULL) {
666120941Sume    flag = (atoi(arg1) != 0);
667165118Sbz    if (errno) {
668120941Sume      out->print_cr("flag value has to be an integer");
66978064Sume      return JNI_ERR;
67053541Sshin    }
67153541Sshin  }
67253541Sshin
673120941Sume  if (strcmp(name, "ExtendedDTraceProbes") == 0) {
674120941Sume    DTrace::set_extended_dprobes(flag);
67553541Sshin    return JNI_OK;
67653541Sshin  }
67753541Sshin
67853541Sshin  if (strcmp(name, "DTraceMonitorProbes") == 0) {
67953541Sshin    DTrace::set_monitor_dprobes(flag);
68062587Sitojun    return JNI_OK;
68153541Sshin  }
68253541Sshin
68353541Sshin  out->print_cr("flag '%s' cannot be changed", name);
68453541Sshin  return JNI_ERR;
68553541Sshin}
68653541Sshin
687151465Ssuzvoid AttachListener::pd_detachall() {
68862587Sitojun  DTrace::detach_all_clients();
68953541Sshin}
69053541Sshin