mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-22 09:21:57 -05:00
2394 lines
83 KiB
Diff
2394 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__ */
|
|
+}
|