ladybird/Ports/OpenJDK/patches/0006-Add-serenity-specific-modules-to-java.base-and-jdk.a.patch

2395 lines
83 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Timur Sultanov <sultanovts@yandex.ru>
Date: Sun, 12 Jun 2022 15:58:40 -0600
Subject: [PATCH] Add serenity-specific modules to java.base and jdk.attach
It would be nice to re-direct the build to the same files *BSD use, but
for now we've got our own copy
Co-Authored-By: Andrew Kaster <akaster@serenityos.org>
---
.../DefaultAsynchronousChannelProvider.java | 47 ++
.../sun/nio/ch/DefaultSelectorProvider.java | 54 ++
.../SerenityAsynchronousChannelProvider.java | 91 +++
.../classes/sun/nio/ch/SerenityPollPort.java | 538 ++++++++++++++++++
.../sun/nio/fs/DefaultFileSystemProvider.java | 53 ++
.../classes/sun/nio/fs/SerenityFileStore.java | 105 ++++
.../sun/nio/fs/SerenityFileSystem.java | 94 +++
.../nio/fs/SerenityFileSystemProvider.java | 52 ++
.../sun/nio/fs/SerenityNativeDispatcher.java | 49 ++
.../serenity/native/libnet/serenity_close.c | 458 +++++++++++++++
.../sun/tools/attach/AttachProviderImpl.java | 82 +++
.../sun/tools/attach/VirtualMachineImpl.java | 326 +++++++++++
.../native/libattach/VirtualMachineImpl.c | 328 +++++++++++
13 files changed, 2277 insertions(+)
create mode 100644 src/java.base/serenity/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java
create mode 100644 src/java.base/serenity/classes/sun/nio/ch/DefaultSelectorProvider.java
create mode 100644 src/java.base/serenity/classes/sun/nio/ch/SerenityAsynchronousChannelProvider.java
create mode 100644 src/java.base/serenity/classes/sun/nio/ch/SerenityPollPort.java
create mode 100644 src/java.base/serenity/classes/sun/nio/fs/DefaultFileSystemProvider.java
create mode 100644 src/java.base/serenity/classes/sun/nio/fs/SerenityFileStore.java
create mode 100644 src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystem.java
create mode 100644 src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystemProvider.java
create mode 100644 src/java.base/serenity/classes/sun/nio/fs/SerenityNativeDispatcher.java
create mode 100644 src/java.base/serenity/native/libnet/serenity_close.c
create mode 100644 src/jdk.attach/serenity/classes/sun/tools/attach/AttachProviderImpl.java
create mode 100644 src/jdk.attach/serenity/classes/sun/tools/attach/VirtualMachineImpl.java
create mode 100644 src/jdk.attach/serenity/native/libattach/VirtualMachineImpl.c
diff --git a/src/java.base/serenity/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java b/src/java.base/serenity/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a7bfe0897390a5828dfea7a7f9d0e83f25af9af
--- /dev/null
+++ b/src/java.base/serenity/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.spi.AsynchronousChannelProvider;
+
+/**
+ * Creates this platform's default AsynchronousChannelProvider
+ */
+
+public class DefaultAsynchronousChannelProvider {
+
+ /**
+ * Prevent instantiation.
+ */
+ private DefaultAsynchronousChannelProvider() { }
+
+ /**
+ * Returns the default AsynchronousChannelProvider.
+ */
+ public static AsynchronousChannelProvider create() {
+ return new SerenityAsynchronousChannelProvider();
+ }
+}
diff --git a/src/java.base/serenity/classes/sun/nio/ch/DefaultSelectorProvider.java b/src/java.base/serenity/classes/sun/nio/ch/DefaultSelectorProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..86d3ade19de292048b3b028a7f78a1cc61f4784f
--- /dev/null
+++ b/src/java.base/serenity/classes/sun/nio/ch/DefaultSelectorProvider.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.ch;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Creates this platform's default SelectorProvider
+ */
+
+@SuppressWarnings("removal")
+public class DefaultSelectorProvider {
+ private static final SelectorProviderImpl INSTANCE;
+ static {
+ PrivilegedAction<SelectorProviderImpl> pa = PollSelectorProvider::new;
+ INSTANCE = AccessController.doPrivileged(pa);
+ }
+
+ /**
+ * Prevent instantiation.
+ */
+ private DefaultSelectorProvider() { }
+
+ /**
+ * Returns the default SelectorProvider implementation.
+ */
+ public static SelectorProviderImpl get() {
+ return INSTANCE;
+ }
+}
\ No newline at end of file
diff --git a/src/java.base/serenity/classes/sun/nio/ch/SerenityAsynchronousChannelProvider.java b/src/java.base/serenity/classes/sun/nio/ch/SerenityAsynchronousChannelProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..2daa2cca4717a6db0dff166bc4befd19d9fdb528
--- /dev/null
+++ b/src/java.base/serenity/classes/sun/nio/ch/SerenityAsynchronousChannelProvider.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.io.IOException;
+
+public class SerenityAsynchronousChannelProvider
+ extends AsynchronousChannelProvider
+{
+ private static volatile SerenityPollPort defaultPort;
+
+ private SerenityPollPort defaultEventPort() throws IOException {
+ if (defaultPort == null) {
+ synchronized (SerenityAsynchronousChannelProvider.class) {
+ if (defaultPort == null) {
+ defaultPort = new SerenityPollPort(this, ThreadPool.getDefault()).start();
+ }
+ }
+ }
+ return defaultPort;
+ }
+
+ public SerenityAsynchronousChannelProvider() {
+ }
+
+ @Override
+ public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory)
+ throws IOException
+ {
+ return new SerenityPollPort(this, ThreadPool.create(nThreads, factory)).start();
+ }
+
+ @Override
+ public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize)
+ throws IOException
+ {
+ return new SerenityPollPort(this, ThreadPool.wrap(executor, initialSize)).start();
+ }
+
+ private Port toPort(AsynchronousChannelGroup group) throws IOException {
+ if (group == null) {
+ return defaultEventPort();
+ } else {
+ if (!(group instanceof SerenityPollPort))
+ throw new IllegalChannelGroupException();
+ return (Port)group;
+ }
+ }
+
+ @Override
+ public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group)
+ throws IOException
+ {
+ return new UnixAsynchronousServerSocketChannelImpl(toPort(group));
+ }
+
+ @Override
+ public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group)
+ throws IOException
+ {
+ return new UnixAsynchronousSocketChannelImpl(toPort(group));
+ }
+}
diff --git a/src/java.base/serenity/classes/sun/nio/ch/SerenityPollPort.java b/src/java.base/serenity/classes/sun/nio/ch/SerenityPollPort.java
new file mode 100644
index 0000000000000000000000000000000000000000..0894d1814244f39887d119da00290798c960e53c
--- /dev/null
+++ b/src/java.base/serenity/classes/sun/nio/ch/SerenityPollPort.java
@@ -0,0 +1,538 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+import jdk.internal.misc.Unsafe;
+
+/**
+ * AsynchronousChannelGroup implementation based on the AIX pollset framework.
+ */
+final class SerenityPollPort
+ extends Port
+{
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ static {
+ IOUtil.load();
+ init();
+ }
+
+ /**
+ * struct pollfd {
+ * int fd;
+ * short events;
+ * short revents;
+ * }
+ */
+ private static final int SIZEOF_POLLFD = eventSize();
+ private static final int OFFSETOF_EVENTS = eventsOffset();
+ private static final int OFFSETOF_REVENTS = reventsOffset();
+ private static final int OFFSETOF_FD = fdOffset();
+
+ // opcodes
+ private static final int PS_ADD = 0x0;
+ private static final int PS_MOD = 0x1;
+ private static final int PS_DELETE = 0x2;
+
+ // maximum number of events to poll at a time
+ private static final int MAX_POLL_EVENTS = 512;
+
+ // pollset ID
+ private final int pollset;
+
+ // true if port is closed
+ private boolean closed;
+
+ // socket pair used for wakeup
+ private final int sp[];
+
+ // socket pair used to indicate pending pollsetCtl calls
+ // Background info: pollsetCtl blocks when another thread is in a pollsetPoll call.
+ private final int ctlSp[];
+
+ // number of wakeups pending
+ private final AtomicInteger wakeupCount = new AtomicInteger();
+
+ // address of the poll array passed to pollset_poll
+ private final long address;
+
+ // encapsulates an event for a channel
+ static class Event {
+ final PollableChannel channel;
+ final int events;
+
+ Event(PollableChannel channel, int events) {
+ this.channel = channel;
+ this.events = events;
+ }
+
+ PollableChannel channel() { return channel; }
+ int events() { return events; }
+ }
+
+ // queue of events for cases that a polling thread dequeues more than one
+ // event
+ private final ArrayBlockingQueue<Event> queue;
+ private final Event NEED_TO_POLL = new Event(null, 0);
+ private final Event EXECUTE_TASK_OR_SHUTDOWN = new Event(null, 0);
+ private final Event CONTINUE_AFTER_CTL_EVENT = new Event(null, 0);
+
+ // encapsulates a pollset control event for a file descriptor
+ static class ControlEvent {
+ final int fd;
+ final int events;
+ final boolean removeOnly;
+ int error = 0;
+
+ ControlEvent(int fd, int events, boolean removeOnly) {
+ this.fd = fd;
+ this.events = events;
+ this.removeOnly = removeOnly;
+ }
+
+ int fd() { return fd; }
+ int events() { return events; }
+ boolean removeOnly() { return removeOnly; }
+ int error() { return error; }
+ void setError(int error) { this.error = error; }
+ }
+
+ // queue of control events that need to be processed
+ // (this object is also used for synchronization)
+ private final HashSet<ControlEvent> controlQueue = new HashSet<ControlEvent>();
+
+ // lock used to check whether a poll operation is ongoing
+ private final ReentrantLock controlLock = new ReentrantLock();
+
+ SerenityPollPort(AsynchronousChannelProvider provider, ThreadPool pool)
+ throws IOException
+ {
+ super(provider, pool);
+
+ // open pollset
+ this.pollset = pollsetCreate();
+
+ // create socket pair for wakeup mechanism
+ int[] sv = new int[2];
+ try {
+ socketpair(sv);
+ // register one end with pollset
+ pollsetCtl(pollset, PS_ADD, sv[0], Net.POLLIN);
+ } catch (IOException x) {
+ pollsetDestroy(pollset);
+ throw x;
+ }
+ this.sp = sv;
+
+ // create socket pair for pollset control mechanism
+ sv = new int[2];
+ try {
+ socketpair(sv);
+ // register one end with pollset
+ pollsetCtl(pollset, PS_ADD, sv[0], Net.POLLIN);
+ } catch (IOException x) {
+ pollsetDestroy(pollset);
+ throw x;
+ }
+ this.ctlSp = sv;
+
+ // allocate the poll array
+ this.address = allocatePollArray(MAX_POLL_EVENTS);
+
+ // create the queue and offer the special event to ensure that the first
+ // threads polls
+ this.queue = new ArrayBlockingQueue<Event>(MAX_POLL_EVENTS);
+ this.queue.offer(NEED_TO_POLL);
+ }
+
+ SerenityPollPort start() {
+ startThreads(new EventHandlerTask());
+ return this;
+ }
+
+ /**
+ * Release all resources
+ */
+ private void implClose() {
+ synchronized (this) {
+ if (closed)
+ return;
+ closed = true;
+ }
+ freePollArray(address);
+ close0(sp[0]);
+ close0(sp[1]);
+ close0(ctlSp[0]);
+ close0(ctlSp[1]);
+ pollsetDestroy(pollset);
+ }
+
+ private void wakeup() {
+ if (wakeupCount.incrementAndGet() == 1) {
+ // write byte to socketpair to force wakeup
+ try {
+ interrupt(sp[1]);
+ } catch (IOException x) {
+ throw new AssertionError(x);
+ }
+ }
+ }
+
+ @Override
+ void executeOnHandlerTask(Runnable task) {
+ synchronized (this) {
+ if (closed)
+ throw new RejectedExecutionException();
+ offerTask(task);
+ wakeup();
+ }
+ }
+
+ @Override
+ void shutdownHandlerTasks() {
+ /*
+ * If no tasks are running then just release resources; otherwise
+ * write to the one end of the socketpair to wakeup any polling threads.
+ */
+ int nThreads = threadCount();
+ if (nThreads == 0) {
+ implClose();
+ } else {
+ // send interrupt to each thread
+ while (nThreads-- > 0) {
+ wakeup();
+ }
+ }
+ }
+
+ // invoke by clients to register a file descriptor
+ @Override
+ void startPoll(int fd, int events) {
+ queueControlEvent(new ControlEvent(fd, events, false));
+ }
+
+ // Callback method for implementations that need special handling when fd is removed
+ @Override
+ protected void preUnregister(int fd) {
+ queueControlEvent(new ControlEvent(fd, 0, true));
+ }
+
+ // Add control event into queue and wait for completion.
+ // In case the control lock is free, this method also tries to apply the control change directly.
+ private void queueControlEvent(ControlEvent ev) {
+ // pollsetCtl blocks when a poll call is ongoing. This is very probable.
+ // Therefore we let the polling thread do the pollsetCtl call.
+ synchronized (controlQueue) {
+ controlQueue.add(ev);
+ // write byte to socketpair to force wakeup
+ try {
+ interrupt(ctlSp[1]);
+ } catch (IOException x) {
+ throw new AssertionError(x);
+ }
+ do {
+ // Directly empty queue if no poll call is ongoing.
+ if (controlLock.tryLock()) {
+ try {
+ processControlQueue();
+ } finally {
+ controlLock.unlock();
+ }
+ } else {
+ try {
+ // Do not starve in case the polling thread returned before
+ // we could write to ctlSp[1] but the polling thread did not
+ // release the control lock until we checked. Therefore, use
+ // a timed wait for the time being.
+ controlQueue.wait(100);
+ } catch (InterruptedException e) {
+ // ignore exception and try again
+ }
+ }
+ } while (controlQueue.contains(ev));
+ }
+ if (ev.error() != 0) {
+ throw new AssertionError();
+ }
+ }
+
+ // Process all events currently stored in the control queue.
+ private void processControlQueue() {
+ synchronized (controlQueue) {
+ // TODO: this is ripped straight out of AIX implementation, who knows if it will work for Serenity
+ Iterator<ControlEvent> iter = controlQueue.iterator();
+ while (iter.hasNext()) {
+ ControlEvent ev = iter.next();
+ pollsetCtl(pollset, PS_DELETE, ev.fd(), 0);
+ if (!ev.removeOnly()) {
+ ev.setError(pollsetCtl(pollset, PS_MOD, ev.fd(), ev.events()));
+ }
+ iter.remove();
+ }
+ controlQueue.notifyAll();
+ }
+ }
+
+ /*
+ * Task to process events from pollset and dispatch to the channel's
+ * onEvent handler.
+ *
+ * Events are retreived from pollset in batch and offered to a BlockingQueue
+ * where they are consumed by handler threads. A special "NEED_TO_POLL"
+ * event is used to signal one consumer to re-poll when all events have
+ * been consumed.
+ */
+ private class EventHandlerTask implements Runnable {
+ private Event poll() throws IOException {
+ try {
+ for (;;) {
+ int n;
+ controlLock.lock();
+ try {
+ n = pollsetPoll(pollset, address, MAX_POLL_EVENTS);
+ } finally {
+ controlLock.unlock();
+ }
+ /*
+ * 'n' events have been read. Here we map them to their
+ * corresponding channel in batch and queue n-1 so that
+ * they can be handled by other handler threads. The last
+ * event is handled by this thread (and so is not queued).
+ */
+ fdToChannelLock.readLock().lock();
+ try {
+ while (n-- > 0) {
+ long eventAddress = getEvent(address, n);
+ int fd = getDescriptor(eventAddress);
+
+ // To emulate one shot semantic we need to remove
+ // the file descriptor here.
+ if (fd != sp[0] && fd != ctlSp[0]) {
+ synchronized (controlQueue) {
+ pollsetCtl(pollset, PS_DELETE, fd, 0);
+ }
+ }
+
+ // wakeup
+ if (fd == sp[0]) {
+ if (wakeupCount.decrementAndGet() == 0) {
+ // no more wakeups so drain pipe
+ drain1(sp[0]);
+ }
+
+ // queue special event if there are more events
+ // to handle.
+ if (n > 0) {
+ queue.offer(EXECUTE_TASK_OR_SHUTDOWN);
+ continue;
+ }
+ return EXECUTE_TASK_OR_SHUTDOWN;
+ }
+
+ // wakeup to process control event
+ if (fd == ctlSp[0]) {
+ synchronized (controlQueue) {
+ drain1(ctlSp[0]);
+ processControlQueue();
+ }
+ if (n > 0) {
+ continue;
+ }
+ return CONTINUE_AFTER_CTL_EVENT;
+ }
+
+ PollableChannel channel = fdToChannel.get(fd);
+ if (channel != null) {
+ int events = getRevents(eventAddress);
+ Event ev = new Event(channel, events);
+
+ // n-1 events are queued; This thread handles
+ // the last one except for the wakeup
+ if (n > 0) {
+ queue.offer(ev);
+ } else {
+ return ev;
+ }
+ }
+ }
+ } finally {
+ fdToChannelLock.readLock().unlock();
+ }
+ }
+ } finally {
+ // to ensure that some thread will poll when all events have
+ // been consumed
+ queue.offer(NEED_TO_POLL);
+ }
+ }
+
+ public void run() {
+ Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
+ Invoker.getGroupAndInvokeCount();
+ final boolean isPooledThread = (myGroupAndInvokeCount != null);
+ boolean replaceMe = false;
+ Event ev;
+ try {
+ for (;;) {
+ // reset invoke count
+ if (isPooledThread)
+ myGroupAndInvokeCount.resetInvokeCount();
+
+ try {
+ replaceMe = false;
+ ev = queue.take();
+
+ // no events and this thread has been "selected" to
+ // poll for more.
+ if (ev == NEED_TO_POLL) {
+ try {
+ ev = poll();
+ } catch (IOException x) {
+ x.printStackTrace();
+ return;
+ }
+ }
+ } catch (InterruptedException x) {
+ continue;
+ }
+
+ // contine after we processed a control event
+ if (ev == CONTINUE_AFTER_CTL_EVENT) {
+ continue;
+ }
+
+ // handle wakeup to execute task or shutdown
+ if (ev == EXECUTE_TASK_OR_SHUTDOWN) {
+ Runnable task = pollTask();
+ if (task == null) {
+ // shutdown request
+ return;
+ }
+ // run task (may throw error/exception)
+ replaceMe = true;
+ task.run();
+ continue;
+ }
+
+ // process event
+ try {
+ ev.channel().onEvent(ev.events(), isPooledThread);
+ } catch (Error x) {
+ replaceMe = true; throw x;
+ } catch (RuntimeException x) {
+ replaceMe = true; throw x;
+ }
+ }
+ } finally {
+ // last handler to exit when shutdown releases resources
+ int remaining = threadExit(this, replaceMe);
+ if (remaining == 0 && isShutdown()) {
+ implClose();
+ }
+ }
+ }
+ }
+
+ /**
+ * Allocates a poll array to handle up to {@code count} events.
+ */
+ private static long allocatePollArray(int count) {
+ return unsafe.allocateMemory(count * SIZEOF_POLLFD);
+ }
+
+ /**
+ * Free a poll array
+ */
+ private static void freePollArray(long address) {
+ unsafe.freeMemory(address);
+ }
+
+ /**
+ * Returns event[i];
+ */
+ private static long getEvent(long address, int i) {
+ return address + (SIZEOF_POLLFD*i);
+ }
+
+ /**
+ * Returns event->fd
+ */
+ private static int getDescriptor(long eventAddress) {
+ return unsafe.getInt(eventAddress + OFFSETOF_FD);
+ }
+
+ /**
+ * Returns event->events
+ */
+ private static int getEvents(long eventAddress) {
+ return unsafe.getChar(eventAddress + OFFSETOF_EVENTS);
+ }
+
+ /**
+ * Returns event->revents
+ */
+ private static int getRevents(long eventAddress) {
+ return unsafe.getChar(eventAddress + OFFSETOF_REVENTS);
+ }
+
+ // -- Native methods --
+
+ private static native void init();
+
+ private static native int eventSize();
+
+ private static native int eventsOffset();
+
+ private static native int reventsOffset();
+
+ private static native int fdOffset();
+
+ private static native int pollsetCreate() throws IOException;
+
+ private static native int pollsetCtl(int pollset, int opcode, int fd, int events);
+
+ private static native int pollsetPoll(int pollset, long pollAddress, int numfds)
+ throws IOException;
+
+ private static native void pollsetDestroy(int pollset);
+
+ private static native void socketpair(int[] sv) throws IOException;
+
+ private static native void interrupt(int fd) throws IOException;
+
+ private static native void drain1(int fd) throws IOException;
+
+ private static native void close0(int fd);
+}
diff --git a/src/java.base/serenity/classes/sun/nio/fs/DefaultFileSystemProvider.java b/src/java.base/serenity/classes/sun/nio/fs/DefaultFileSystemProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..b24f3de0139e5447455417914c47b3bfcff1301c
--- /dev/null
+++ b/src/java.base/serenity/classes/sun/nio/fs/DefaultFileSystemProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.FileSystem;
+
+/**
+ * Creates this platform's default FileSystemProvider.
+ */
+
+public class DefaultFileSystemProvider {
+ private static final SerenityFileSystemProvider INSTANCE
+ = new SerenityFileSystemProvider();
+
+ private DefaultFileSystemProvider() { }
+
+ /**
+ * Returns the platform's default file system provider.
+ */
+ public static SerenityFileSystemProvider instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Returns the platform's default file system.
+ */
+ public static FileSystem theFileSystem() {
+ return INSTANCE.theFileSystem();
+ }
+}
diff --git a/src/java.base/serenity/classes/sun/nio/fs/SerenityFileStore.java b/src/java.base/serenity/classes/sun/nio/fs/SerenityFileStore.java
new file mode 100644
index 0000000000000000000000000000000000000000..3f408ec9bd370405e4b2287742b3ba09c9233bbc
--- /dev/null
+++ b/src/java.base/serenity/classes/sun/nio/fs/SerenityFileStore.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.attribute.*;
+import java.util.*;
+import java.io.IOException;
+
+/**
+ * AIX implementation of FileStore
+ */
+
+class SerenityFileStore
+ extends UnixFileStore
+{
+
+ SerenityFileStore(UnixPath file) throws IOException {
+ super(file);
+ }
+
+ SerenityFileStore(UnixFileSystem fs, UnixMountEntry entry) throws IOException {
+ super(fs, entry);
+ }
+
+ /**
+ * Finds, and returns, the mount entry for the file system where the file
+ * resides.
+ */
+ @Override
+ UnixMountEntry findMountEntry() throws IOException {
+ SerenityFileSystem fs = (SerenityFileSystem)file().getFileSystem();
+
+ // step 1: get realpath
+ UnixPath path = null;
+ try {
+ byte[] rp = UnixNativeDispatcher.realpath(file());
+ path = new UnixPath(fs, rp);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(file());
+ }
+
+ // step 2: find mount point
+ UnixPath parent = path.getParent();
+ while (parent != null) {
+ UnixFileAttributes attrs = null;
+ try {
+ attrs = UnixFileAttributes.get(parent, true);
+ } catch (UnixException x) {
+ x.rethrowAsIOException(parent);
+ }
+ if (attrs.dev() != dev())
+ break;
+ path = parent;
+ parent = parent.getParent();
+ }
+
+ // step 3: lookup mounted file systems
+ byte[] dir = path.asByteArray();
+ for (UnixMountEntry entry: fs.getMountEntries()) {
+ if (Arrays.equals(dir, entry.dir()))
+ return entry;
+ }
+
+ throw new IOException("Mount point not found");
+ }
+
+ @Override
+ protected boolean isExtendedAttributesEnabled(UnixPath path) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
+ return super.supportsFileAttributeView(type);
+ }
+
+ @Override
+ public boolean supportsFileAttributeView(String name) {
+ return super.supportsFileAttributeView(name);
+ }
+}
diff --git a/src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystem.java b/src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystem.java
new file mode 100644
index 0000000000000000000000000000000000000000..bee588a7ebc0e5d3994a05ac733ba6a8e0d34117
--- /dev/null
+++ b/src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystem.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.io.IOException;
+import java.util.*;
+import static sun.nio.fs.SerenityNativeDispatcher.*;
+
+/**
+ * AIX implementation of FileSystem
+ */
+
+class SerenityFileSystem extends UnixFileSystem {
+
+ SerenityFileSystem(UnixFileSystemProvider provider, String dir) {
+ super(provider, dir);
+ }
+
+ @Override
+ public WatchService newWatchService()
+ throws IOException
+ {
+ return new PollingWatchService();
+ }
+
+ // lazy initialization of the list of supported attribute views
+ private static class SupportedFileFileAttributeViewsHolder {
+ static final Set<String> supportedFileAttributeViews =
+ supportedFileAttributeViews();
+ private static Set<String> supportedFileAttributeViews() {
+ Set<String> result = new HashSet<String>();
+ result.addAll(UnixFileSystem.standardFileAttributeViews());
+ return Collections.unmodifiableSet(result);
+ }
+ }
+
+ @Override
+ public Set<String> supportedFileAttributeViews() {
+ return SupportedFileFileAttributeViewsHolder.supportedFileAttributeViews;
+ }
+
+ @Override
+ void copyNonPosixAttributes(int ofd, int nfd) {
+ // TODO: Implement if needed.
+ }
+
+ /**
+ * Returns object to iterate over the mount entries returned by mntctl
+ */
+ @Override
+ Iterable<UnixMountEntry> getMountEntries() {
+ UnixMountEntry[] entries = null;
+ try {
+ entries = getmntctl();
+ } catch (UnixException x) {
+ // nothing we can do
+ }
+ if (entries == null) {
+ return Collections.emptyList();
+ }
+ return Arrays.asList(entries);
+ }
+
+ @Override
+ FileStore getFileStore(UnixMountEntry entry) throws IOException {
+ return new SerenityFileStore(this, entry);
+ }
+}
diff --git a/src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystemProvider.java b/src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystemProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..8582190afb01b4f1415789aa36b2dd11431443c4
--- /dev/null
+++ b/src/java.base/serenity/classes/sun/nio/fs/SerenityFileSystemProvider.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+import java.io.IOException;
+
+/**
+ * Serenity implementation of FileSystemProvider
+ */
+
+class SerenityFileSystemProvider extends UnixFileSystemProvider {
+ public SerenityFileSystemProvider() {
+ super();
+ }
+
+ @Override
+ SerenityFileSystem newFileSystem(String dir) {
+ return new SerenityFileSystem(this, dir);
+ }
+
+ /**
+ * @see sun.nio.fs.UnixFileSystemProvider#getFileStore(sun.nio.fs.UnixPath)
+ */
+ @Override
+ SerenityFileStore getFileStore(UnixPath path) throws IOException {
+ return new SerenityFileStore(path);
+ }
+}
diff --git a/src/java.base/serenity/classes/sun/nio/fs/SerenityNativeDispatcher.java b/src/java.base/serenity/classes/sun/nio/fs/SerenityNativeDispatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c50b719c792a157f53348d00ecfdc6f89a1f726
--- /dev/null
+++ b/src/java.base/serenity/classes/sun/nio/fs/SerenityNativeDispatcher.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+/**
+ * Serenity specific system calls.
+ */
+
+class SerenityNativeDispatcher extends UnixNativeDispatcher {
+ private SerenityNativeDispatcher() { }
+
+ /**
+ * Special implementation of 'getextmntent' (see SolarisNativeDispatcher)
+ * that returns all entries at once.
+ */
+ static native UnixMountEntry[] getmntctl() throws UnixException;
+
+ // initialize
+ private static native void init();
+
+ static {
+ jdk.internal.loader.BootLoader.loadLibrary("nio");
+ init();
+ }
+}
diff --git a/src/java.base/serenity/native/libnet/serenity_close.c b/src/java.base/serenity/native/libnet/serenity_close.c
new file mode 100644
index 0000000000000000000000000000000000000000..6a177bbb90a59efdcc6734ee68a06fdd12e4fa61
--- /dev/null
+++ b/src/java.base/serenity/native/libnet/serenity_close.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include "jvm.h"
+#include "net_util.h"
+
+/*
+ * Stack allocated by thread when doing blocking operation
+ */
+typedef struct threadEntry {
+ pthread_t thr; /* this thread */
+ struct threadEntry *next; /* next thread */
+ int intr; /* interrupted */
+} threadEntry_t;
+
+/*
+ * Heap allocated during initialized - one entry per fd
+ */
+typedef struct {
+ pthread_mutex_t lock; /* fd lock */
+ threadEntry_t *threads; /* threads blocked on fd */
+} fdEntry_t;
+
+/*
+ * Signal to unblock thread
+ */
+static int sigWakeup = SIGIO;
+
+/*
+ * fdTable holds one entry per file descriptor, up to a certain
+ * maximum.
+ * Theoretically, the number of possible file descriptors can get
+ * large, though usually it does not. Entries for small value file
+ * descriptors are kept in a simple table, which covers most scenarios.
+ * Entries for large value file descriptors are kept in an overflow
+ * table, which is organized as a sparse two dimensional array whose
+ * slabs are allocated on demand. This covers all corner cases while
+ * keeping memory consumption reasonable.
+ */
+
+/* Base table for low value file descriptors */
+static fdEntry_t* fdTable = NULL;
+/* Maximum size of base table (in number of entries). */
+static const int fdTableMaxSize = 0x1000; /* 4K */
+/* Actual size of base table (in number of entries) */
+static int fdTableLen = 0;
+/* Max. theoretical number of file descriptors on system. */
+static int fdLimit = 0;
+
+/* Overflow table, should base table not be large enough. Organized as
+ * an array of n slabs, each holding 64k entries.
+ */
+static fdEntry_t** fdOverflowTable = NULL;
+/* Number of slabs in the overflow table */
+static int fdOverflowTableLen = 0;
+/* Number of entries in one slab */
+static const int fdOverflowTableSlabSize = 0x10000; /* 64k */
+pthread_mutex_t fdOverflowTableLock = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * Null signal handler
+ */
+static void sig_wakeup(int sig) {
+}
+
+/*
+ * Initialization routine (executed when library is loaded)
+ * Allocate fd tables and sets up signal handler.
+ */
+static void __attribute((constructor)) init() {
+ struct rlimit nbr_files;
+ sigset_t sigset;
+ struct sigaction sa;
+ int i = 0;
+
+ /* Determine the maximum number of possible file descriptors. */
+ if (-1 == getrlimit(RLIMIT_NOFILE, &nbr_files)) {
+ fprintf(stderr, "library initialization failed - "
+ "unable to get max # of allocated fds\n");
+ abort();
+ }
+ if (nbr_files.rlim_max != RLIM_INFINITY) {
+ fdLimit = nbr_files.rlim_max;
+ } else {
+ /* We just do not know. */
+ fdLimit = INT_MAX;
+ }
+
+ /* Allocate table for low value file descriptors. */
+ fdTableLen = fdLimit < fdTableMaxSize ? fdLimit : fdTableMaxSize;
+ fdTable = (fdEntry_t*) calloc(fdTableLen, sizeof(fdEntry_t));
+ if (fdTable == NULL) {
+ fprintf(stderr, "library initialization failed - "
+ "unable to allocate file descriptor table - out of memory");
+ abort();
+ } else {
+ for (i = 0; i < fdTableLen; i ++) {
+ pthread_mutex_init(&fdTable[i].lock, NULL);
+ }
+ }
+
+ /* Allocate overflow table, if needed */
+ if (fdLimit > fdTableMaxSize) {
+ fdOverflowTableLen = ((fdLimit - fdTableMaxSize) / fdOverflowTableSlabSize) + 1;
+ fdOverflowTable = (fdEntry_t**) calloc(fdOverflowTableLen, sizeof(fdEntry_t*));
+ if (fdOverflowTable == NULL) {
+ fprintf(stderr, "library initialization failed - "
+ "unable to allocate file descriptor overflow table - out of memory");
+ abort();
+ }
+ }
+
+ /*
+ * Setup the signal handler
+ */
+ sa.sa_handler = sig_wakeup;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(sigWakeup, &sa, NULL);
+
+ sigemptyset(&sigset);
+ sigaddset(&sigset, sigWakeup);
+ sigprocmask(SIG_UNBLOCK, &sigset, NULL);
+}
+
+/*
+ * Return the fd table for this fd.
+ */
+static inline fdEntry_t *getFdEntry(int fd)
+{
+ fdEntry_t* result = NULL;
+
+ if (fd < 0) {
+ return NULL;
+ }
+
+ /* This should not happen. If it does, our assumption about
+ * max. fd value was wrong. */
+ assert(fd < fdLimit);
+
+ if (fd < fdTableMaxSize) {
+ /* fd is in base table. */
+ assert(fd < fdTableLen);
+ result = &fdTable[fd];
+ } else {
+ /* fd is in overflow table. */
+ const int indexInOverflowTable = fd - fdTableMaxSize;
+ const int rootindex = indexInOverflowTable / fdOverflowTableSlabSize;
+ const int slabindex = indexInOverflowTable % fdOverflowTableSlabSize;
+ fdEntry_t* slab = NULL;
+ assert(rootindex < fdOverflowTableLen);
+ assert(slabindex < fdOverflowTableSlabSize);
+ pthread_mutex_lock(&fdOverflowTableLock);
+ /* Allocate new slab in overflow table if needed */
+ if (fdOverflowTable[rootindex] == NULL) {
+ fdEntry_t* const newSlab =
+ (fdEntry_t*)calloc(fdOverflowTableSlabSize, sizeof(fdEntry_t));
+ if (newSlab == NULL) {
+ fprintf(stderr, "Unable to allocate file descriptor overflow"
+ " table slab - out of memory");
+ pthread_mutex_unlock(&fdOverflowTableLock);
+ abort();
+ } else {
+ int i;
+ for (i = 0; i < fdOverflowTableSlabSize; i ++) {
+ pthread_mutex_init(&newSlab[i].lock, NULL);
+ }
+ fdOverflowTable[rootindex] = newSlab;
+ }
+ }
+ pthread_mutex_unlock(&fdOverflowTableLock);
+ slab = fdOverflowTable[rootindex];
+ result = &slab[slabindex];
+ }
+
+ return result;
+
+}
+
+
+/*
+ * Start a blocking operation :-
+ * Insert thread onto thread list for the fd.
+ */
+static inline void startOp(fdEntry_t *fdEntry, threadEntry_t *self)
+{
+ self->thr = pthread_self();
+ self->intr = 0;
+
+ pthread_mutex_lock(&(fdEntry->lock));
+ {
+ self->next = fdEntry->threads;
+ fdEntry->threads = self;
+ }
+ pthread_mutex_unlock(&(fdEntry->lock));
+}
+
+/*
+ * End a blocking operation :-
+ * Remove thread from thread list for the fd
+ * If fd has been interrupted then set errno to EBADF
+ */
+static inline void endOp
+ (fdEntry_t *fdEntry, threadEntry_t *self)
+{
+ int orig_errno = errno;
+ pthread_mutex_lock(&(fdEntry->lock));
+ {
+ threadEntry_t *curr, *prev=NULL;
+ curr = fdEntry->threads;
+ while (curr != NULL) {
+ if (curr == self) {
+ if (curr->intr) {
+ orig_errno = EBADF;
+ }
+ if (prev == NULL) {
+ fdEntry->threads = curr->next;
+ } else {
+ prev->next = curr->next;
+ }
+ break;
+ }
+ prev = curr;
+ curr = curr->next;
+ }
+ }
+ pthread_mutex_unlock(&(fdEntry->lock));
+ errno = orig_errno;
+}
+
+/*
+ * Close or dup2 a file descriptor ensuring that all threads blocked on
+ * the file descriptor are notified via a wakeup signal.
+ *
+ * fd1 < 0 => close(fd2)
+ * fd1 >= 0 => dup2(fd1, fd2)
+ *
+ * Returns -1 with errno set if operation fails.
+ */
+static int closefd(int fd1, int fd2) {
+ int rv, orig_errno;
+ fdEntry_t *fdEntry = getFdEntry(fd2);
+ if (fdEntry == NULL) {
+ errno = EBADF;
+ return -1;
+ }
+
+ /*
+ * Lock the fd to hold-off additional I/O on this fd.
+ */
+ pthread_mutex_lock(&(fdEntry->lock));
+
+ {
+ /*
+ * Send a wakeup signal to all threads blocked on this
+ * file descriptor.
+ */
+ threadEntry_t *curr = fdEntry->threads;
+ while (curr != NULL) {
+ curr->intr = 1;
+ pthread_kill( curr->thr, sigWakeup );
+ curr = curr->next;
+ }
+
+ /*
+ * And close/dup the file descriptor
+ * (restart if interrupted by signal)
+ */
+ do {
+ if (fd1 < 0) {
+ rv = close(fd2);
+ } else {
+ rv = dup2(fd1, fd2);
+ }
+ } while (rv == -1 && errno == EINTR);
+
+ }
+
+ /*
+ * Unlock without destroying errno
+ */
+ orig_errno = errno;
+ pthread_mutex_unlock(&(fdEntry->lock));
+ errno = orig_errno;
+
+ return rv;
+}
+
+/*
+ * Wrapper for dup2 - same semantics as dup2 system call except
+ * that any threads blocked in an I/O system call on fd2 will be
+ * preempted and return -1/EBADF;
+ */
+int NET_Dup2(int fd, int fd2) {
+ if (fd < 0) {
+ errno = EBADF;
+ return -1;
+ }
+ return closefd(fd, fd2);
+}
+
+/*
+ * Wrapper for close - same semantics as close system call
+ * except that any threads blocked in an I/O on fd will be
+ * preempted and the I/O system call will return -1/EBADF.
+ */
+int NET_SocketClose(int fd) {
+ return closefd(-1, fd);
+}
+
+/************** Basic I/O operations here ***************/
+
+/*
+ * Macro to perform a blocking IO operation. Restarts
+ * automatically if interrupted by signal (other than
+ * our wakeup signal)
+ */
+#define BLOCKING_IO_RETURN_INT(FD, FUNC) { \
+ int ret; \
+ threadEntry_t self; \
+ fdEntry_t *fdEntry = getFdEntry(FD); \
+ if (fdEntry == NULL) { \
+ errno = EBADF; \
+ return -1; \
+ } \
+ do { \
+ startOp(fdEntry, &self); \
+ ret = FUNC; \
+ endOp(fdEntry, &self); \
+ } while (ret == -1 && errno == EINTR); \
+ return ret; \
+}
+
+int NET_Read(int s, void* buf, size_t len) {
+ BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) );
+}
+
+int NET_NonBlockingRead(int s, void* buf, size_t len) {
+ BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, MSG_DONTWAIT));
+}
+
+int NET_RecvFrom(int s, void *buf, int len, unsigned int flags,
+ struct sockaddr *from, socklen_t *fromlen) {
+ BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, fromlen) );
+}
+
+int NET_Send(int s, void *msg, int len, unsigned int flags) {
+ BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) );
+}
+
+int NET_SendTo(int s, const void *msg, int len, unsigned int
+ flags, const struct sockaddr *to, int tolen) {
+ BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) );
+}
+
+int NET_Accept(int s, struct sockaddr *addr, socklen_t *addrlen) {
+ BLOCKING_IO_RETURN_INT( s, accept(s, addr, addrlen) );
+}
+
+int NET_Connect(int s, struct sockaddr *addr, int addrlen) {
+ BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) );
+}
+
+int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) {
+ BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) );
+}
+
+/*
+ * Wrapper for poll(s, timeout).
+ * Auto restarts with adjusted timeout if interrupted by
+ * signal other than our wakeup signal.
+ */
+int NET_Timeout(JNIEnv *env, int s, long timeout, jlong nanoTimeStamp) {
+ jlong prevNanoTime = nanoTimeStamp;
+ jlong nanoTimeout = (jlong)timeout * NET_NSEC_PER_MSEC;
+ fdEntry_t *fdEntry = getFdEntry(s);
+
+ /*
+ * Check that fd hasn't been closed.
+ */
+ if (fdEntry == NULL) {
+ errno = EBADF;
+ return -1;
+ }
+
+ for(;;) {
+ struct pollfd pfd;
+ int rv;
+ threadEntry_t self;
+
+ /*
+ * Poll the fd. If interrupted by our wakeup signal
+ * errno will be set to EBADF.
+ */
+ pfd.fd = s;
+ pfd.events = POLLIN | POLLERR;
+
+ startOp(fdEntry, &self);
+ rv = poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC);
+ endOp(fdEntry, &self);
+ /*
+ * If interrupted then adjust timeout. If timeout
+ * has expired return 0 (indicating timeout expired).
+ */
+ if (rv < 0 && errno == EINTR) {
+ if (timeout > 0) {
+ jlong newNanoTime = JVM_NanoTime(env, 0);
+ nanoTimeout -= newNanoTime - prevNanoTime;
+ if (nanoTimeout < NET_NSEC_PER_MSEC) {
+ return 0;
+ }
+ prevNanoTime = newNanoTime;
+ } else {
+ continue; // timeout is -1, so loop again.
+ }
+ } else {
+ return rv;
+ }
+ }
+}
diff --git a/src/jdk.attach/serenity/classes/sun/tools/attach/AttachProviderImpl.java b/src/jdk.attach/serenity/classes/sun/tools/attach/AttachProviderImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f6fc4d4df22ccfda0c36d5ce1cbb6704ec05bbf
--- /dev/null
+++ b/src/jdk.attach/serenity/classes/sun/tools/attach/AttachProviderImpl.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.tools.attach;
+
+import com.sun.tools.attach.VirtualMachine;
+import com.sun.tools.attach.VirtualMachineDescriptor;
+import com.sun.tools.attach.AttachNotSupportedException;
+import java.io.IOException;
+
+// Based on linux/classes/sun/tools/attach/AttachProviderImpl.java.
+
+/*
+ * An AttachProvider implementation for Aix that uses a UNIX domain
+ * socket.
+ */
+public class AttachProviderImpl extends HotSpotAttachProvider {
+
+ public AttachProviderImpl() {
+ }
+
+ public String name() {
+ return "sun";
+ }
+
+ public String type() {
+ return "socket";
+ }
+
+ public VirtualMachine attachVirtualMachine(String vmid)
+ throws AttachNotSupportedException, IOException
+ {
+ checkAttachPermission();
+
+ // AttachNotSupportedException will be thrown if the target VM can be determined
+ // to be not attachable.
+ testAttachable(vmid);
+
+ return new VirtualMachineImpl(this, vmid);
+ }
+
+ public VirtualMachine attachVirtualMachine(VirtualMachineDescriptor vmd)
+ throws AttachNotSupportedException, IOException
+ {
+ if (vmd.provider() != this) {
+ throw new AttachNotSupportedException("provider mismatch");
+ }
+ // To avoid re-checking if the VM if attachable, we check if the descriptor
+ // is for a hotspot VM - these descriptors are created by the listVirtualMachines
+ // implementation which only returns a list of attachable VMs.
+ if (vmd instanceof HotSpotVirtualMachineDescriptor) {
+ assert ((HotSpotVirtualMachineDescriptor)vmd).isAttachable();
+ checkAttachPermission();
+ return new VirtualMachineImpl(this, vmd.id());
+ } else {
+ return attachVirtualMachine(vmd.id());
+ }
+ }
+
+}
diff --git a/src/jdk.attach/serenity/classes/sun/tools/attach/VirtualMachineImpl.java b/src/jdk.attach/serenity/classes/sun/tools/attach/VirtualMachineImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c432edeee3c7d5241006d1aaaf68c1b6d81dd3c
--- /dev/null
+++ b/src/jdk.attach/serenity/classes/sun/tools/attach/VirtualMachineImpl.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.tools.attach;
+
+import com.sun.tools.attach.AttachOperationFailedException;
+import com.sun.tools.attach.AgentLoadException;
+import com.sun.tools.attach.AttachNotSupportedException;
+import com.sun.tools.attach.spi.AttachProvider;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.File;
+
+/*
+ * Aix implementation of HotSpotVirtualMachine
+ */
+public class VirtualMachineImpl extends HotSpotVirtualMachine {
+ // "/tmp" is used as a global well-known location for the files
+ // .java_pid<pid>. and .attach_pid<pid>. It is important that this
+ // location is the same for all processes, otherwise the tools
+ // will not be able to find all Hotspot processes.
+ // Any changes to this needs to be synchronized with HotSpot.
+ private static final String tmpdir = "/tmp";
+ String socket_path;
+
+ /**
+ * Attaches to the target VM
+ */
+ VirtualMachineImpl(AttachProvider provider, String vmid)
+ throws AttachNotSupportedException, IOException
+ {
+ super(provider, vmid);
+
+ // This provider only understands pids
+ int pid;
+ try {
+ pid = Integer.parseInt(vmid);
+ if (pid < 1) {
+ throw new NumberFormatException();
+ }
+ } catch (NumberFormatException x) {
+ throw new AttachNotSupportedException("Invalid process identifier: " + vmid);
+ }
+
+ // Find the socket file. If not found then we attempt to start the
+ // attach mechanism in the target VM by sending it a QUIT signal.
+ // Then we attempt to find the socket file again.
+ File socket_file = new File(tmpdir, ".java_pid" + pid);
+ socket_path = socket_file.getPath();
+ if (!socket_file.exists()) {
+ // Keep canonical version of File, to delete, in case target process ends and /proc link has gone:
+ File f = createAttachFile(pid).getCanonicalFile();
+ try {
+ sendQuitTo(pid);
+
+ // give the target VM time to start the attach mechanism
+ final int delay_step = 100;
+ final long timeout = attachTimeout();
+ long time_spend = 0;
+ long delay = 0;
+ do {
+ // Increase timeout on each attempt to reduce polling
+ delay += delay_step;
+ try {
+ Thread.sleep(delay);
+ } catch (InterruptedException x) { }
+
+ time_spend += delay;
+ if (time_spend > timeout/2 && !socket_file.exists()) {
+ // Send QUIT again to give target VM the last chance to react
+ sendQuitTo(pid);
+ }
+ } while (time_spend <= timeout && !socket_file.exists());
+ if (!socket_file.exists()) {
+ throw new AttachNotSupportedException(
+ String.format("Unable to open socket file %s: " +
+ "target process %d doesn't respond within %dms " +
+ "or HotSpot VM not loaded", socket_path, pid,
+ time_spend));
+ }
+ } finally {
+ f.delete();
+ }
+ }
+
+ // Check that the file owner/permission to avoid attaching to
+ // bogus process
+ checkPermissions(socket_path);
+
+ // Check that we can connect to the process
+ // - this ensures we throw the permission denied error now rather than
+ // later when we attempt to enqueue a command.
+ int s = socket();
+ try {
+ connect(s, socket_path);
+ } finally {
+ close(s);
+ }
+ }
+
+ /**
+ * Detach from the target VM
+ */
+ public void detach() throws IOException {
+ synchronized (this) {
+ if (socket_path != null) {
+ socket_path = null;
+ }
+ }
+ }
+
+ // protocol version
+ private final static String PROTOCOL_VERSION = "1";
+
+ // known errors
+ private final static int ATTACH_ERROR_BADVERSION = 101;
+
+ /**
+ * Execute the given command in the target VM.
+ */
+ InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException {
+ assert args.length <= 3; // includes null
+
+ // did we detach?
+ synchronized (this) {
+ if (socket_path == null) {
+ throw new IOException("Detached from target VM");
+ }
+ }
+
+ // create UNIX socket
+ int s = socket();
+
+ // connect to target VM
+ try {
+ connect(s, socket_path);
+ } catch (IOException x) {
+ close(s);
+ throw x;
+ }
+
+ IOException ioe = null;
+
+ // connected - write request
+ // <ver> <cmd> <args...>
+ try {
+ writeString(s, PROTOCOL_VERSION);
+ writeString(s, cmd);
+
+ for (int i = 0; i < 3; i++) {
+ if (i < args.length && args[i] != null) {
+ writeString(s, (String)args[i]);
+ } else {
+ writeString(s, "");
+ }
+ }
+ } catch (IOException x) {
+ ioe = x;
+ }
+
+
+ // Create an input stream to read reply
+ SocketInputStream sis = new SocketInputStream(s);
+
+ // Read the command completion status
+ int completionStatus;
+ try {
+ completionStatus = readInt(sis);
+ } catch (IOException x) {
+ sis.close();
+ if (ioe != null) {
+ throw ioe;
+ } else {
+ throw x;
+ }
+ }
+
+ if (completionStatus != 0) {
+ // read from the stream and use that as the error message
+ String message = readErrorMessage(sis);
+ sis.close();
+
+ // In the event of a protocol mismatch then the target VM
+ // returns a known error so that we can throw a reasonable
+ // error.
+ if (completionStatus == ATTACH_ERROR_BADVERSION) {
+ throw new IOException("Protocol mismatch with target VM");
+ }
+
+ // Special-case the "load" command so that the right exception is
+ // thrown.
+ if (cmd.equals("load")) {
+ String msg = "Failed to load agent library";
+ if (!message.isEmpty())
+ msg += ": " + message;
+ throw new AgentLoadException(msg);
+ } else {
+ if (message.isEmpty())
+ message = "Command failed in target VM";
+ throw new AttachOperationFailedException(message);
+ }
+ }
+
+ // Return the input stream so that the command output can be read
+ return sis;
+ }
+
+ /*
+ * InputStream for the socket connection to get target VM
+ */
+ private class SocketInputStream extends InputStream {
+ int s;
+
+ public SocketInputStream(int s) {
+ this.s = s;
+ }
+
+ public synchronized int read() throws IOException {
+ byte b[] = new byte[1];
+ int n = this.read(b, 0, 1);
+ if (n == 1) {
+ return b[0] & 0xff;
+ } else {
+ return -1;
+ }
+ }
+
+ public synchronized int read(byte[] bs, int off, int len) throws IOException {
+ if ((off < 0) || (off > bs.length) || (len < 0) ||
+ ((off + len) > bs.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0)
+ return 0;
+
+ return VirtualMachineImpl.read(s, bs, off, len);
+ }
+
+ public synchronized void close() throws IOException {
+ if (s != -1) {
+ int toClose = s;
+ s = -1;
+ VirtualMachineImpl.close(toClose);
+ }
+ }
+ }
+
+ // On Aix a simple handshake is used to start the attach mechanism
+ // if not already started. The client creates a .attach_pid<pid> file in the
+ // target VM's working directory (or temp directory), and the SIGQUIT handler
+ // checks for the file.
+ private File createAttachFile(int pid) throws IOException {
+ String fn = ".attach_pid" + pid;
+ String path = "/proc/" + pid + "/cwd/" + fn;
+ File f = new File(path);
+ try {
+ f.createNewFile();
+ } catch (IOException x) {
+ f = new File(tmpdir, fn);
+ f.createNewFile();
+ }
+ return f;
+ }
+
+ /*
+ * Write/sends the given to the target VM. String is transmitted in
+ * UTF-8 encoding.
+ */
+ private void writeString(int fd, String s) throws IOException {
+ if (s.length() > 0) {
+ byte b[];
+ try {
+ b = s.getBytes("UTF-8");
+ } catch (java.io.UnsupportedEncodingException x) {
+ throw new InternalError(x);
+ }
+ VirtualMachineImpl.write(fd, b, 0, b.length);
+ }
+ byte b[] = new byte[1];
+ b[0] = 0;
+ write(fd, b, 0, 1);
+ }
+
+
+ //-- native methods
+
+ static native void sendQuitTo(int pid) throws IOException;
+
+ static native void checkPermissions(String path) throws IOException;
+
+ static native int socket() throws IOException;
+
+ static native void connect(int fd, String path) throws IOException;
+
+ static native void close(int fd) throws IOException;
+
+ static native int read(int fd, byte buf[], int off, int bufLen) throws IOException;
+
+ static native void write(int fd, byte buf[], int off, int bufLen) throws IOException;
+
+ static {
+ System.loadLibrary("attach");
+ }
+}
diff --git a/src/jdk.attach/serenity/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/serenity/native/libattach/VirtualMachineImpl.c
new file mode 100644
index 0000000000000000000000000000000000000000..d20a6f012f20be4d3ef49b4b05417b92c1f42975
--- /dev/null
+++ b/src/jdk.attach/serenity/native/libattach/VirtualMachineImpl.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni_util.h"
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syslimits.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sun_tools_attach_VirtualMachineImpl.h"
+
+#define RESTARTABLE(_cmd, _result) do { \
+ do { \
+ _result = _cmd; \
+ } while((_result == -1) && (errno == EINTR)); \
+} while(0)
+
+#define ROOT_UID 0
+
+/*
+ * Declare library specific JNI_Onload entry if static build
+ */
+DEF_STATIC_JNI_OnLoad
+
+/*
+ * Class: sun_tools_attach_VirtualMachineImpl
+ * Method: socket
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_socket
+ (JNIEnv *env, jclass cls)
+{
+ int fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ JNU_ThrowIOExceptionWithLastError(env, "socket");
+ }
+ return (jint)fd;
+}
+
+/*
+ * Class: sun_tools_attach_VirtualMachineImpl
+ * Method: connect
+ * Signature: (ILjava/lang/String;)I
+ */
+JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connect
+ (JNIEnv *env, jclass cls, jint fd, jstring path)
+{
+ jboolean isCopy;
+ const char* p = GetStringPlatformChars(env, path, &isCopy);
+ if (p != NULL) {
+ struct sockaddr_un addr;
+ int err = 0;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ /* strncpy is safe because addr.sun_path was zero-initialized before. */
+ strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1);
+
+ if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
+ err = errno;
+ }
+
+ if (isCopy) {
+ JNU_ReleaseStringPlatformChars(env, path, p);
+ }
+
+ /*
+ * If the connect failed then we throw the appropriate exception
+ * here (can't throw it before releasing the string as can't call
+ * JNI with pending exception)
+ */
+ if (err != 0) {
+ if (err == ENOENT) {
+ JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
+ } else {
+ char* msg = strdup(strerror(err));
+ JNU_ThrowIOException(env, msg);
+ if (msg != NULL) {
+ free(msg);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Class: sun_tools_attach_VirtualMachineImpl
+ * Method: sendQuitTo
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_sendQuitTo
+ (JNIEnv *env, jclass cls, jint pid)
+{
+ if (kill((pid_t)pid, SIGQUIT)) {
+ JNU_ThrowIOExceptionWithLastError(env, "kill");
+ }
+}
+
+/*
+ * Class: sun_tools_attach_VirtualMachineImpl
+ * Method: checkPermissions
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions
+ (JNIEnv *env, jclass cls, jstring path)
+{
+ jboolean isCopy;
+ const char* p = GetStringPlatformChars(env, path, &isCopy);
+ if (p != NULL) {
+ struct stat sb;
+ uid_t uid, gid;
+ int res;
+
+ memset(&sb, 0, sizeof(struct stat));
+
+ /*
+ * Check that the path is owned by the effective uid/gid of this
+ * process. Also check that group/other access is not allowed.
+ */
+ uid = geteuid();
+ gid = getegid();
+
+ res = stat(p, &sb);
+ if (res != 0) {
+ /* save errno */
+ res = errno;
+ }
+
+ if (res == 0) {
+ char msg[100];
+ jboolean isError = JNI_FALSE;
+ if (sb.st_uid != uid && uid != ROOT_UID) {
+ snprintf(msg, sizeof(msg),
+ "file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid);
+ isError = JNI_TRUE;
+ } else if (sb.st_gid != gid && uid != ROOT_UID) {
+ snprintf(msg, sizeof(msg),
+ "file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid);
+ isError = JNI_TRUE;
+ } else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) {
+ snprintf(msg, sizeof(msg),
+ "file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777);
+ isError = JNI_TRUE;
+ }
+ if (isError) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "well-known file %s is not secure: %s", p, msg);
+ JNU_ThrowIOException(env, buf);
+ }
+ } else {
+ char* msg = strdup(strerror(res));
+ JNU_ThrowIOException(env, msg);
+ if (msg != NULL) {
+ free(msg);
+ }
+ }
+
+ if (isCopy) {
+ JNU_ReleaseStringPlatformChars(env, path, p);
+ }
+ }
+}
+
+/*
+ * Class: sun_tools_attach_VirtualMachineImpl
+ * Method: close
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_close
+ (JNIEnv *env, jclass cls, jint fd)
+{
+ int res;
+ shutdown(fd, SHUT_RDWR);
+ RESTARTABLE(close(fd), res);
+}
+
+/*
+ * Class: sun_tools_attach_VirtualMachineImpl
+ * Method: read
+ * Signature: (I[BI)I
+ */
+JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_read
+ (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
+{
+ unsigned char buf[128];
+ size_t len = sizeof(buf);
+ ssize_t n;
+
+ size_t remaining = (size_t)(baLen - off);
+ if (len > remaining) {
+ len = remaining;
+ }
+
+ RESTARTABLE(read(fd, buf, len), n);
+ if (n == -1) {
+ JNU_ThrowIOExceptionWithLastError(env, "read");
+ } else {
+ if (n == 0) {
+ n = -1; // EOF
+ } else {
+ (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf));
+ }
+ }
+ return n;
+}
+
+/*
+ * Class: sun_tools_attach_VirtualMachineImpl
+ * Method: write
+ * Signature: (I[B)V
+ */
+JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_write
+ (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen)
+{
+ size_t remaining = bufLen;
+ do {
+ unsigned char buf[128];
+ size_t len = sizeof(buf);
+ int n;
+
+ if (len > remaining) {
+ len = remaining;
+ }
+ (*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf);
+
+ RESTARTABLE(write(fd, buf, len), n);
+ if (n > 0) {
+ off += n;
+ remaining -= n;
+ } else {
+ JNU_ThrowIOExceptionWithLastError(env, "write");
+ return;
+ }
+
+ } while (remaining > 0);
+}
+
+/*
+ * Class: sun_tools_attach_BSDVirtualMachine
+ * Method: createAttachFile
+ * Signature: (Ljava.lang.String;)V
+ */
+JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_createAttachFile0(JNIEnv *env, jclass cls, jstring path)
+{
+ const char* _path;
+ jboolean isCopy;
+ int fd, rc;
+
+ _path = GetStringPlatformChars(env, path, &isCopy);
+ if (_path == NULL) {
+ JNU_ThrowIOException(env, "Must specify a path");
+ return;
+ }
+
+ RESTARTABLE(open(_path, O_CREAT | O_EXCL, S_IWUSR | S_IRUSR), fd);
+ if (fd == -1) {
+ /* release p here before we throw an I/O exception */
+ if (isCopy) {
+ JNU_ReleaseStringPlatformChars(env, path, _path);
+ }
+ JNU_ThrowIOExceptionWithLastError(env, "open");
+ return;
+ }
+
+ RESTARTABLE(chown(_path, geteuid(), getegid()), rc);
+
+ RESTARTABLE(close(fd), rc);
+
+ /* release p here */
+ if (isCopy) {
+ JNU_ReleaseStringPlatformChars(env, path, _path);
+ }
+}
+
+/*
+ * Class: sun_tools_attach_BSDVirtualMachine
+ * Method: getTempDir
+ * Signature: (V)Ljava.lang.String;
+ */
+JNIEXPORT jstring JNICALL Java_sun_tools_attach_VirtualMachineImpl_getTempDir(JNIEnv *env, jclass cls)
+{
+ // This must be hard coded because it's the system's temporary
+ // directory not the java application's temp directory, ala java.io.tmpdir.
+
+#ifdef __APPLE__
+ // macosx has a secure per-user temporary directory.
+ // Don't cache the result as this is only called once.
+ char path[PATH_MAX];
+ int pathSize = confstr(_CS_DARWIN_USER_TEMP_DIR, path, PATH_MAX);
+ if (pathSize == 0 || pathSize > PATH_MAX) {
+ strlcpy(path, "/tmp", sizeof(path));
+ }
+ return JNU_NewStringPlatform(env, path);
+#else /* __APPLE__ */
+ return (*env)->NewStringUTF(env, "/tmp");
+#endif /* __APPLE__ */
+}