aoqi@0: /* aoqi@0: * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: * aoqi@0: */ aoqi@0: aoqi@0: #include "precompiled.hpp" aoqi@0: #include "runtime/interfaceSupport.hpp" aoqi@0: #include "runtime/os.hpp" aoqi@0: #include "services/attachListener.hpp" aoqi@0: #include "services/dtraceAttacher.hpp" aoqi@0: aoqi@0: #include aoqi@0: #include aoqi@0: #include aoqi@0: #include aoqi@0: #include aoqi@0: #include aoqi@0: aoqi@0: #ifndef UNIX_PATH_MAX aoqi@0: #define UNIX_PATH_MAX sizeof(((struct sockaddr_un *)0)->sun_path) aoqi@0: #endif aoqi@0: aoqi@0: // The attach mechanism on Linux uses a UNIX domain socket. An attach listener aoqi@0: // thread is created at startup or is created on-demand via a signal from aoqi@0: // the client tool. The attach listener creates a socket and binds it to a file aoqi@0: // in the filesystem. The attach listener then acts as a simple (single- aoqi@0: // threaded) server - it waits for a client to connect, reads the request, aoqi@0: // executes it, and returns the response to the client via the socket aoqi@0: // connection. aoqi@0: // aoqi@0: // As the socket is a UNIX domain socket it means that only clients on the aoqi@0: // local machine can connect. In addition there are two other aspects to aoqi@0: // the security: aoqi@0: // 1. The well known file that the socket is bound to has permission 400 aoqi@0: // 2. When a client connect, the SO_PEERCRED socket option is used to aoqi@0: // obtain the credentials of client. We check that the effective uid aoqi@0: // of the client matches this process. aoqi@0: aoqi@0: // forward reference aoqi@0: class LinuxAttachOperation; aoqi@0: aoqi@0: class LinuxAttachListener: AllStatic { aoqi@0: private: aoqi@0: // the path to which we bind the UNIX domain socket aoqi@0: static char _path[UNIX_PATH_MAX]; aoqi@0: static bool _has_path; aoqi@0: aoqi@0: // the file descriptor for the listening socket aoqi@0: static int _listener; aoqi@0: aoqi@0: static void set_path(char* path) { aoqi@0: if (path == NULL) { aoqi@0: _has_path = false; aoqi@0: } else { aoqi@0: strncpy(_path, path, UNIX_PATH_MAX); aoqi@0: _path[UNIX_PATH_MAX-1] = '\0'; aoqi@0: _has_path = true; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: static void set_listener(int s) { _listener = s; } aoqi@0: aoqi@0: // reads a request from the given connected socket aoqi@0: static LinuxAttachOperation* read_request(int s); aoqi@0: aoqi@0: public: aoqi@0: enum { aoqi@0: ATTACH_PROTOCOL_VER = 1 // protocol version aoqi@0: }; aoqi@0: enum { aoqi@0: ATTACH_ERROR_BADVERSION = 101 // error codes aoqi@0: }; aoqi@0: aoqi@0: // initialize the listener, returns 0 if okay aoqi@0: static int init(); aoqi@0: aoqi@0: static char* path() { return _path; } aoqi@0: static bool has_path() { return _has_path; } aoqi@0: static int listener() { return _listener; } aoqi@0: aoqi@0: // write the given buffer to a socket aoqi@0: static int write_fully(int s, char* buf, int len); aoqi@0: aoqi@0: static LinuxAttachOperation* dequeue(); aoqi@0: }; aoqi@0: aoqi@0: class LinuxAttachOperation: public AttachOperation { aoqi@0: private: aoqi@0: // the connection to the client aoqi@0: int _socket; aoqi@0: aoqi@0: public: aoqi@0: void complete(jint res, bufferedStream* st); aoqi@0: aoqi@0: void set_socket(int s) { _socket = s; } aoqi@0: int socket() const { return _socket; } aoqi@0: aoqi@0: LinuxAttachOperation(char* name) : AttachOperation(name) { aoqi@0: set_socket(-1); aoqi@0: } aoqi@0: }; aoqi@0: aoqi@0: // statics aoqi@0: char LinuxAttachListener::_path[UNIX_PATH_MAX]; aoqi@0: bool LinuxAttachListener::_has_path; aoqi@0: int LinuxAttachListener::_listener = -1; aoqi@0: aoqi@0: // Supporting class to help split a buffer into individual components aoqi@0: class ArgumentIterator : public StackObj { aoqi@0: private: aoqi@0: char* _pos; aoqi@0: char* _end; aoqi@0: public: aoqi@0: ArgumentIterator(char* arg_buffer, size_t arg_size) { aoqi@0: _pos = arg_buffer; aoqi@0: _end = _pos + arg_size - 1; aoqi@0: } aoqi@0: char* next() { aoqi@0: if (*_pos == '\0') { aoqi@0: return NULL; aoqi@0: } aoqi@0: char* res = _pos; aoqi@0: char* next_pos = strchr(_pos, '\0'); aoqi@0: if (next_pos < _end) { aoqi@0: next_pos++; aoqi@0: } aoqi@0: _pos = next_pos; aoqi@0: return res; aoqi@0: } aoqi@0: }; aoqi@0: aoqi@0: aoqi@0: // atexit hook to stop listener and unlink the file that it is aoqi@0: // bound too. aoqi@0: extern "C" { aoqi@0: static void listener_cleanup() { aoqi@0: static int cleanup_done; aoqi@0: if (!cleanup_done) { aoqi@0: cleanup_done = 1; aoqi@0: int s = LinuxAttachListener::listener(); aoqi@0: if (s != -1) { aoqi@0: ::close(s); aoqi@0: } aoqi@0: if (LinuxAttachListener::has_path()) { aoqi@0: ::unlink(LinuxAttachListener::path()); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // Initialization - create a listener socket and bind it to a file aoqi@0: aoqi@0: int LinuxAttachListener::init() { aoqi@0: char path[UNIX_PATH_MAX]; // socket file aoqi@0: char initial_path[UNIX_PATH_MAX]; // socket file during setup aoqi@0: int listener; // listener socket (file descriptor) aoqi@0: aoqi@0: // register function to cleanup aoqi@0: ::atexit(listener_cleanup); aoqi@0: aoqi@0: int n = snprintf(path, UNIX_PATH_MAX, "%s/.java_pid%d", aoqi@0: os::get_temp_directory(), os::current_process_id()); aoqi@0: if (n < (int)UNIX_PATH_MAX) { aoqi@0: n = snprintf(initial_path, UNIX_PATH_MAX, "%s.tmp", path); aoqi@0: } aoqi@0: if (n >= (int)UNIX_PATH_MAX) { aoqi@0: return -1; aoqi@0: } aoqi@0: aoqi@0: // create the listener socket aoqi@0: listener = ::socket(PF_UNIX, SOCK_STREAM, 0); aoqi@0: if (listener == -1) { aoqi@0: return -1; aoqi@0: } aoqi@0: aoqi@0: // bind socket aoqi@0: struct sockaddr_un addr; aoqi@0: addr.sun_family = AF_UNIX; aoqi@0: strcpy(addr.sun_path, initial_path); aoqi@0: ::unlink(initial_path); aoqi@0: int res = ::bind(listener, (struct sockaddr*)&addr, sizeof(addr)); aoqi@0: if (res == -1) { aoqi@0: ::close(listener); aoqi@0: return -1; aoqi@0: } aoqi@0: aoqi@0: // put in listen mode, set permissions, and rename into place aoqi@0: res = ::listen(listener, 5); aoqi@0: if (res == 0) { aoqi@0: RESTARTABLE(::chmod(initial_path, S_IREAD|S_IWRITE), res); aoqi@0: if (res == 0) { aoqi@0: res = ::rename(initial_path, path); aoqi@0: } aoqi@0: } aoqi@0: if (res == -1) { aoqi@0: ::close(listener); aoqi@0: ::unlink(initial_path); aoqi@0: return -1; aoqi@0: } aoqi@0: set_path(path); aoqi@0: set_listener(listener); aoqi@0: aoqi@0: return 0; aoqi@0: } aoqi@0: aoqi@0: // Given a socket that is connected to a peer we read the request and aoqi@0: // create an AttachOperation. As the socket is blocking there is potential aoqi@0: // for a denial-of-service if the peer does not response. However this happens aoqi@0: // after the peer credentials have been checked and in the worst case it just aoqi@0: // means that the attach listener thread is blocked. aoqi@0: // aoqi@0: LinuxAttachOperation* LinuxAttachListener::read_request(int s) { aoqi@0: char ver_str[8]; aoqi@0: sprintf(ver_str, "%d", ATTACH_PROTOCOL_VER); aoqi@0: aoqi@0: // The request is a sequence of strings so we first figure out the aoqi@0: // expected count and the maximum possible length of the request. aoqi@0: // The request is: aoqi@0: // 00000 aoqi@0: // where is the protocol version (1), is the command aoqi@0: // name ("load", "datadump", ...), and is an argument aoqi@0: int expected_str_count = 2 + AttachOperation::arg_count_max; aoqi@0: const int max_len = (sizeof(ver_str) + 1) + (AttachOperation::name_length_max + 1) + aoqi@0: AttachOperation::arg_count_max*(AttachOperation::arg_length_max + 1); aoqi@0: aoqi@0: char buf[max_len]; aoqi@0: int str_count = 0; aoqi@0: aoqi@0: // Read until all (expected) strings have been read, the buffer is aoqi@0: // full, or EOF. aoqi@0: aoqi@0: int off = 0; aoqi@0: int left = max_len; aoqi@0: aoqi@0: do { aoqi@0: int n; aoqi@0: RESTARTABLE(read(s, buf+off, left), n); aoqi@0: if (n == -1) { aoqi@0: return NULL; // reset by peer or other error aoqi@0: } aoqi@0: if (n == 0) { aoqi@0: break; aoqi@0: } aoqi@0: for (int i=0; i so check it now to aoqi@0: // check for protocol mis-match aoqi@0: if (str_count == 1) { aoqi@0: if ((strlen(buf) != strlen(ver_str)) || aoqi@0: (atoi(buf) != ATTACH_PROTOCOL_VER)) { aoqi@0: char msg[32]; aoqi@0: sprintf(msg, "%d\n", ATTACH_ERROR_BADVERSION); aoqi@0: write_fully(s, msg, strlen(msg)); aoqi@0: return NULL; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: off += n; aoqi@0: left -= n; aoqi@0: } while (left > 0 && str_count < expected_str_count); aoqi@0: aoqi@0: if (str_count != expected_str_count) { aoqi@0: return NULL; // incomplete request aoqi@0: } aoqi@0: aoqi@0: // parse request aoqi@0: aoqi@0: ArgumentIterator args(buf, (max_len)-left); aoqi@0: aoqi@0: // version already checked aoqi@0: char* v = args.next(); aoqi@0: aoqi@0: char* name = args.next(); aoqi@0: if (name == NULL || strlen(name) > AttachOperation::name_length_max) { aoqi@0: return NULL; aoqi@0: } aoqi@0: aoqi@0: LinuxAttachOperation* op = new LinuxAttachOperation(name); aoqi@0: aoqi@0: for (int i=0; iset_arg(i, NULL); aoqi@0: } else { aoqi@0: if (strlen(arg) > AttachOperation::arg_length_max) { aoqi@0: delete op; aoqi@0: return NULL; aoqi@0: } aoqi@0: op->set_arg(i, arg); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: op->set_socket(s); aoqi@0: return op; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: // Dequeue an operation aoqi@0: // aoqi@0: // In the Linux implementation there is only a single operation and clients aoqi@0: // cannot queue commands (except at the socket level). aoqi@0: // aoqi@0: LinuxAttachOperation* LinuxAttachListener::dequeue() { aoqi@0: for (;;) { aoqi@0: int s; aoqi@0: aoqi@0: // wait for client to connect aoqi@0: struct sockaddr addr; aoqi@0: socklen_t len = sizeof(addr); aoqi@0: RESTARTABLE(::accept(listener(), &addr, &len), s); aoqi@0: if (s == -1) { aoqi@0: return NULL; // log a warning? aoqi@0: } aoqi@0: aoqi@0: // get the credentials of the peer and check the effective uid/guid aoqi@0: // - check with jeff on this. aoqi@0: struct ucred cred_info; aoqi@0: socklen_t optlen = sizeof(cred_info); aoqi@0: if (::getsockopt(s, SOL_SOCKET, SO_PEERCRED, (void*)&cred_info, &optlen) == -1) { aoqi@0: ::close(s); aoqi@0: continue; aoqi@0: } aoqi@0: uid_t euid = geteuid(); aoqi@0: gid_t egid = getegid(); aoqi@0: aoqi@0: if (cred_info.uid != euid || cred_info.gid != egid) { aoqi@0: ::close(s); aoqi@0: continue; aoqi@0: } aoqi@0: aoqi@0: // peer credential look okay so we read the request aoqi@0: LinuxAttachOperation* op = read_request(s); aoqi@0: if (op == NULL) { aoqi@0: ::close(s); aoqi@0: continue; aoqi@0: } else { aoqi@0: return op; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // write the given buffer to the socket aoqi@0: int LinuxAttachListener::write_fully(int s, char* buf, int len) { aoqi@0: do { aoqi@0: int n = ::write(s, buf, len); aoqi@0: if (n == -1) { aoqi@0: if (errno != EINTR) return -1; aoqi@0: } else { aoqi@0: buf += n; aoqi@0: len -= n; aoqi@0: } aoqi@0: } aoqi@0: while (len > 0); aoqi@0: return 0; aoqi@0: } aoqi@0: aoqi@0: // Complete an operation by sending the operation result and any result aoqi@0: // output to the client. At this time the socket is in blocking mode so aoqi@0: // potentially we can block if there is a lot of data and the client is aoqi@0: // non-responsive. For most operations this is a non-issue because the aoqi@0: // default send buffer is sufficient to buffer everything. In the future aoqi@0: // if there are operations that involves a very big reply then it the aoqi@0: // socket could be made non-blocking and a timeout could be used. aoqi@0: aoqi@0: void LinuxAttachOperation::complete(jint result, bufferedStream* st) { aoqi@0: JavaThread* thread = JavaThread::current(); aoqi@0: ThreadBlockInVM tbivm(thread); aoqi@0: aoqi@0: thread->set_suspend_equivalent(); aoqi@0: // cleared by handle_special_suspend_equivalent_condition() or aoqi@0: // java_suspend_self() via check_and_wait_while_suspended() aoqi@0: aoqi@0: // write operation result aoqi@0: char msg[32]; aoqi@0: sprintf(msg, "%d\n", result); aoqi@0: int rc = LinuxAttachListener::write_fully(this->socket(), msg, strlen(msg)); aoqi@0: aoqi@0: // write any result data aoqi@0: if (rc == 0) { aoqi@0: LinuxAttachListener::write_fully(this->socket(), (char*) st->base(), st->size()); aoqi@0: ::shutdown(this->socket(), 2); aoqi@0: } aoqi@0: aoqi@0: // done aoqi@0: ::close(this->socket()); aoqi@0: aoqi@0: // were we externally suspended while we were waiting? aoqi@0: thread->check_and_wait_while_suspended(); aoqi@0: aoqi@0: delete this; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: // AttachListener functions aoqi@0: aoqi@0: AttachOperation* AttachListener::dequeue() { aoqi@0: JavaThread* thread = JavaThread::current(); aoqi@0: ThreadBlockInVM tbivm(thread); aoqi@0: aoqi@0: thread->set_suspend_equivalent(); aoqi@0: // cleared by handle_special_suspend_equivalent_condition() or aoqi@0: // java_suspend_self() via check_and_wait_while_suspended() aoqi@0: aoqi@0: AttachOperation* op = LinuxAttachListener::dequeue(); aoqi@0: aoqi@0: // were we externally suspended while we were waiting? aoqi@0: thread->check_and_wait_while_suspended(); aoqi@0: aoqi@0: return op; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: // Performs initialization at vm startup aoqi@0: // For Linux we remove any stale .java_pid file which could cause aoqi@0: // an attaching process to think we are ready to receive on the aoqi@0: // domain socket before we are properly initialized aoqi@0: aoqi@0: void AttachListener::vm_start() { aoqi@0: char fn[UNIX_PATH_MAX]; aoqi@0: struct stat64 st; aoqi@0: int ret; aoqi@0: aoqi@0: int n = snprintf(fn, UNIX_PATH_MAX, "%s/.java_pid%d", aoqi@0: os::get_temp_directory(), os::current_process_id()); aoqi@0: assert(n < (int)UNIX_PATH_MAX, "java_pid file name buffer overflow"); aoqi@0: aoqi@0: RESTARTABLE(::stat64(fn, &st), ret); aoqi@0: if (ret == 0) { aoqi@0: ret = ::unlink(fn); aoqi@0: if (ret == -1) { aoqi@0: debug_only(warning("failed to remove stale attach pid file at %s", fn)); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: int AttachListener::pd_init() { aoqi@0: JavaThread* thread = JavaThread::current(); aoqi@0: ThreadBlockInVM tbivm(thread); aoqi@0: aoqi@0: thread->set_suspend_equivalent(); aoqi@0: // cleared by handle_special_suspend_equivalent_condition() or aoqi@0: // java_suspend_self() via check_and_wait_while_suspended() aoqi@0: aoqi@0: int ret_code = LinuxAttachListener::init(); aoqi@0: aoqi@0: // were we externally suspended while we were waiting? aoqi@0: thread->check_and_wait_while_suspended(); aoqi@0: aoqi@0: return ret_code; aoqi@0: } aoqi@0: aoqi@0: // Attach Listener is started lazily except in the case when aoqi@0: // +ReduseSignalUsage is used aoqi@0: bool AttachListener::init_at_startup() { aoqi@0: if (ReduceSignalUsage) { aoqi@0: return true; aoqi@0: } else { aoqi@0: return false; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // If the file .attach_pid exists in the working directory aoqi@0: // or /tmp then this is the trigger to start the attach mechanism aoqi@0: bool AttachListener::is_init_trigger() { aoqi@0: if (init_at_startup() || is_initialized()) { aoqi@0: return false; // initialized at startup or already initialized aoqi@0: } aoqi@0: char fn[PATH_MAX+1]; aoqi@0: sprintf(fn, ".attach_pid%d", os::current_process_id()); aoqi@0: int ret; aoqi@0: struct stat64 st; aoqi@0: RESTARTABLE(::stat64(fn, &st), ret); aoqi@0: if (ret == -1) { aoqi@0: snprintf(fn, sizeof(fn), "%s/.attach_pid%d", aoqi@0: os::get_temp_directory(), os::current_process_id()); aoqi@0: RESTARTABLE(::stat64(fn, &st), ret); aoqi@0: } aoqi@0: if (ret == 0) { aoqi@0: // simple check to avoid starting the attach mechanism when aoqi@0: // a bogus user creates the file aoqi@0: if (st.st_uid == geteuid()) { aoqi@0: init(); aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: // if VM aborts then remove listener aoqi@0: void AttachListener::abort() { aoqi@0: listener_cleanup(); aoqi@0: } aoqi@0: aoqi@0: void AttachListener::pd_data_dump() { aoqi@0: os::signal_notify(SIGQUIT); aoqi@0: } aoqi@0: aoqi@0: AttachOperationFunctionInfo* AttachListener::pd_find_operation(const char* n) { aoqi@0: return NULL; aoqi@0: } aoqi@0: aoqi@0: jint AttachListener::pd_set_flag(AttachOperation* op, outputStream* out) { aoqi@0: out->print_cr("flag '%s' cannot be changed", op->arg(0)); aoqi@0: return JNI_ERR; aoqi@0: } aoqi@0: aoqi@0: void AttachListener::pd_detachall() { aoqi@0: // do nothing for now aoqi@0: }