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