1/*
2 * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.internal.misc;
27
28import java.util.Hashtable;
29import java.util.Objects;
30
31/**
32 * This class provides ANSI/ISO C signal support. A Java program can register
33 * signal handlers for the current process. There are two restrictions:
34 * <ul>
35 * <li>
36 * Java code cannot register a handler for signals that are already used
37 * by the Java VM implementation. The <code>Signal.handle</code>
38 * function raises an <code>IllegalArgumentException</code> if such an attempt
39 * is made.
40 * <li>
41 * When <code>Signal.handle</code> is called, the VM internally registers a
42 * special C signal handler. There is no way to force the Java signal handler
43 * to run synchronously before the C signal handler returns. Instead, when the
44 * VM receives a signal, the special C signal handler creates a new thread
45 * (at priority <code>Thread.MAX_PRIORITY</code>) to
46 * run the registered Java signal handler. The C signal handler immediately
47 * returns. Note that because the Java signal handler runs in a newly created
48 * thread, it may not actually be executed until some time after the C signal
49 * handler returns.
50 * </ul>
51 * <p>
52 * Signal objects are created based on their names. For example:
53 * <blockquote><pre>
54 * new Signal("INT");
55 * </pre></blockquote>
56 * constructs a signal object corresponding to <code>SIGINT</code>, which is
57 * typically produced when the user presses <code>Ctrl-C</code> at the command line.
58 * The <code>Signal</code> constructor throws <code>IllegalArgumentException</code>
59 * when it is passed an unknown signal.
60 * <p>
61 * This is an example of how Java code handles <code>SIGINT</code>:
62 * <blockquote><pre>
63 * Signal.Handler handler = new Signal.Handler () {
64 *     public void handle(Signal sig) {
65 *       ... // handle SIGINT
66 *     }
67 * };
68 * Signal.handle(new Signal("INT"), handler);
69 * </pre></blockquote>
70 *
71 * @since    9
72 */
73public final class Signal {
74    private static Hashtable<Signal, Signal.Handler> handlers = new Hashtable<>(4);
75    private static Hashtable<Integer, Signal> signals = new Hashtable<>(4);
76
77    private int number;
78    private String name;
79
80    /* Returns the signal number */
81    public int getNumber() {
82        return number;
83    }
84
85    /**
86     * Returns the signal name.
87     *
88     * @return the name of the signal.
89     * @see jdk.internal.misc.Signal#Signal(String name)
90     */
91    public String getName() {
92        return name;
93    }
94
95    /**
96     * Compares the equality of two <code>Signal</code> objects.
97     *
98     * @param other the object to compare with.
99     * @return whether two <code>Signal</code> objects are equal.
100     */
101    public boolean equals(Object other) {
102        if (this == other) {
103            return true;
104        }
105        if (other == null || !(other instanceof Signal)) {
106            return false;
107        }
108        Signal other1 = (Signal)other;
109        return name.equals(other1.name) && (number == other1.number);
110    }
111
112    /**
113     * Returns a hashcode for this Signal.
114     *
115     * @return  a hash code value for this object.
116     */
117    public int hashCode() {
118        return number;
119    }
120
121    /**
122     * Returns a string representation of this signal. For example, "SIGINT"
123     * for an object constructed using <code>new Signal ("INT")</code>.
124     *
125     * @return a string representation of the signal
126     */
127    public String toString() {
128        return "SIG" + name;
129    }
130
131    /**
132     * Constructs a signal from its name.
133     *
134     * @param name the name of the signal.
135     * @exception IllegalArgumentException unknown signal
136     * @see jdk.internal.misc.Signal#getName()
137     */
138    public Signal(String name) {
139        Objects.requireNonNull(name, "name");
140        // Signal names are the short names;
141        // the "SIG" prefix is not used for compatibility with previous JDKs
142        if (name.startsWith("SIG")) {
143            throw new IllegalArgumentException("Unknown signal: " + name);
144        }
145        this.name = name;
146        number = findSignal0(name);
147        if (number < 0) {
148            throw new IllegalArgumentException("Unknown signal: " + name);
149        }
150    }
151
152    /**
153     * Registers a signal handler.
154     *
155     * @param sig a signal
156     * @param handler the handler to be registered with the given signal.
157     * @return the old handler
158     * @exception IllegalArgumentException the signal is in use by the VM
159     * @see jdk.internal.misc.Signal#raise(Signal sig)
160     * @see jdk.internal.misc.Signal.Handler
161     * @see jdk.internal.misc.Signal.Handler#SIG_DFL
162     * @see jdk.internal.misc.Signal.Handler#SIG_IGN
163     */
164    public static synchronized Signal.Handler handle(Signal sig,
165                                                    Signal.Handler handler)
166        throws IllegalArgumentException {
167        Objects.requireNonNull(sig, "sig");
168        Objects.requireNonNull(handler, "handler");
169        long newH = (handler instanceof NativeHandler) ?
170                      ((NativeHandler)handler).getHandler() : 2;
171        long oldH = handle0(sig.number, newH);
172        if (oldH == -1) {
173            throw new IllegalArgumentException
174                ("Signal already used by VM or OS: " + sig);
175        }
176        signals.put(sig.number, sig);
177        synchronized (handlers) {
178            Signal.Handler oldHandler = handlers.get(sig);
179            handlers.remove(sig);
180            if (newH == 2) {
181                handlers.put(sig, handler);
182            }
183            if (oldH == 0) {
184                return Signal.Handler.SIG_DFL;
185            } else if (oldH == 1) {
186                return Signal.Handler.SIG_IGN;
187            } else if (oldH == 2) {
188                return oldHandler;
189            } else {
190                return new NativeHandler(oldH);
191            }
192        }
193    }
194
195    /**
196     * Raises a signal in the current process.
197     *
198     * @param sig a signal
199     * @see jdk.internal.misc.Signal#handle(Signal sig, Signal.Handler handler)
200     */
201    public static void raise(Signal sig) throws IllegalArgumentException {
202        Objects.requireNonNull(sig, "sig");
203        if (handlers.get(sig) == null) {
204            throw new IllegalArgumentException("Unhandled signal: " + sig);
205        }
206        raise0(sig.number);
207    }
208
209    /* Called by the VM to execute Java signal handlers. */
210    private static void dispatch(final int number) {
211        final Signal sig = signals.get(number);
212        final Signal.Handler handler = handlers.get(sig);
213
214        Runnable runnable = new Runnable () {
215            public void run() {
216              // Don't bother to reset the priority. Signal handler will
217              // run at maximum priority inherited from the VM signal
218              // dispatch thread.
219              // Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
220                handler.handle(sig);
221            }
222        };
223        if (handler != null) {
224            new Thread(null, runnable, sig + " handler", 0, false).start();
225        }
226    }
227
228    /* Find the signal number, given a name. Returns -1 for unknown signals. */
229    private static native int findSignal0(String sigName);
230    /* Registers a native signal handler, and returns the old handler.
231     * Handler values:
232     *   0     default handler
233     *   1     ignore the signal
234     *   2     call back to Signal.dispatch
235     *   other arbitrary native signal handlers
236     */
237    private static native long handle0(int sig, long nativeH);
238    /* Raise a given signal number */
239    private static native void raise0(int sig);
240
241    /**
242     * This is the signal handler interface expected in <code>Signal.handle</code>.
243     */
244    public interface Handler {
245
246        /**
247         * The default signal handler
248         */
249        public static final Signal.Handler SIG_DFL = new NativeHandler(0);
250        /**
251         * Ignore the signal
252         */
253        public static final Signal.Handler SIG_IGN = new NativeHandler(1);
254
255        /**
256         * Handle the given signal
257         *
258         * @param sig a signal object
259         */
260        public void handle(Signal sig);
261    }
262
263
264    /*
265     * A package-private class implementing a signal handler in native code.
266     */
267    static final class NativeHandler implements Signal.Handler {
268
269        private final long handler;
270
271        long getHandler() {
272            return handler;
273        }
274
275        NativeHandler(long handler) {
276            this.handler = handler;
277        }
278
279        public void handle(Signal sig) {
280            throw new UnsupportedOperationException("invoking native signal handle not supported");
281        }
282
283        public String toString() {
284            return this == SIG_DFL ? "SIG_DFL" :
285                    (this == SIG_IGN ? "SIG_IGN" : super.toString());
286        }
287    }
288
289}
290