|
@@ -0,0 +1,2394 @@
|
|
|
+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 000000000..7a7bfe089
|
|
|
+--- /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 000000000..86d3ade19
|
|
|
+--- /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 000000000..2daa2cca4
|
|
|
+--- /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 000000000..0894d1814
|
|
|
+--- /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 000000000..b24f3de01
|
|
|
+--- /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 000000000..3f408ec9b
|
|
|
+--- /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 000000000..bee588a7e
|
|
|
+--- /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 000000000..8582190af
|
|
|
+--- /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 000000000..7c50b719c
|
|
|
+--- /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 000000000..6a177bbb9
|
|
|
+--- /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 000000000..2f6fc4d4d
|
|
|
+--- /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 000000000..0c432edee
|
|
|
+--- /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 000000000..d20a6f012
|
|
|
+--- /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__ */
|
|
|
++}
|