mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 20:02:44 +00: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__ */
 | |
| +}
 | 
