1 /*
   2  * Copyright (c) 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 
  26 import java.util.ArrayList;
  27 import java.util.List;
  28 import java.util.Objects;
  29 import java.util.concurrent.Semaphore;
  30 import java.util.concurrent.TimeUnit;
  31 
  32 import org.testng.Assert;
  33 import org.testng.TestNG;
  34 import org.testng.annotations.Test;
  35 import org.testng.annotations.BeforeSuite;
  36 import org.testng.annotations.DataProvider;
  37 
  38 import jdk.test.lib.Platform;
  39 import jdk.test.lib.Utils;
  40 
  41 import sun.misc.Signal;
  42 import sun.misc.SignalHandler;
  43 
  44 /*
  45  * @test
  46  * @library /test/lib
  47  * @modules jdk.unsupported
  48  *          java.base/jdk.internal.misc
  49  * @run testng/othervm -Xrs -DXrs=true SunMiscSignalTest
  50  * @run testng/othervm SunMiscSignalTest
  51  * @summary sun.misc.Signal test
  52  */
  53 
  54 @Test
  55 public class SunMiscSignalTest {
  56 
  57     // Set to true to enable additional debug output
  58     static boolean debug = true;
  59 
  60     // True to test while running with -Xrs
  61     static boolean RUNNING_WITH_Xrs = Boolean.getBoolean("Xrs");
  62 
  63     /**
  64      * Print a debug message if enabled.
  65      *
  66      * @param format the format
  67      * @param args   the arguments
  68      */
  69     static void printf(String format, Object... args) {
  70         if (debug) {
  71             System.out.printf("    " + format, args);
  72         }
  73     }
  74 
  75     enum IsSupported {NO, YES}
  76 
  77     enum CanRegister {NO, YES}
  78 
  79     enum CanRaise {NO, YES}
  80 
  81     enum Invoked {NO, YES}
  82 
  83     enum RestrictedSignals {NORMAL, XRS}
  84 
  85     @BeforeSuite
  86     static void setup() {
  87         System.out.printf("-Xrs: %s%n", RUNNING_WITH_Xrs);
  88     }
  89 
  90     // Provider of signals to be tested with variations for -Xrs and
  91     // platform dependencies
  92     // -Xrs restricted signals signals the VM will not handle SIGINT, SIGTERM, SIGHUP and others
  93     @DataProvider(name = "supportedSignals")
  94     static Object[][] supportedSignals() {
  95         RestrictedSignals rs = RUNNING_WITH_Xrs ? RestrictedSignals.XRS : RestrictedSignals.NORMAL;
  96         CanRegister registerXrs = RUNNING_WITH_Xrs ? CanRegister.NO : CanRegister.YES;
  97         CanRaise raiseXrs = RUNNING_WITH_Xrs ? CanRaise.NO : CanRaise.YES;
  98         Invoked invokedXrs = RUNNING_WITH_Xrs ? Invoked.NO : Invoked.YES;
  99 
 100         Object[][] commonSignals = new Object[][]{
 101                 {"INT",  IsSupported.YES, registerXrs, raiseXrs, invokedXrs},
 102                 {"TERM", IsSupported.YES, registerXrs, raiseXrs, invokedXrs},
 103                 {"ABRT", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 104         };
 105 
 106         Object[][] posixSignals = {
 107                 {"HUP",  IsSupported.YES, registerXrs, raiseXrs, invokedXrs},
 108                 {"QUIT", IsSupported.YES, CanRegister.NO, CanRaise.NO, Invoked.NO},
 109                 {"BUS",  IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 110                 {"USR1", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 111                 {"USR2", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 112                 {"PIPE", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 113                 {"ALRM", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 114                 {"CHLD", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 115                 {"CONT", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 116                 {"TSTP", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 117                 {"TTIN", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 118                 {"TTOU", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 119                 {"URG",  IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 120                 {"XCPU", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 121                 {"XFSZ", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 122                 {"VTALRM", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 123                 {"PROF", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 124                 {"WINCH", IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 125                 {"IO",   IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 126                 {"SYS",   IsSupported.YES, CanRegister.YES, CanRaise.YES, invokedXrs},
 127         };
 128 
 129         Object[][] windowsSignals = {
 130                 {"HUP",  IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 131                 {"QUIT", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 132                 {"BUS",  IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 133                 {"USR1", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 134                 {"USR2", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 135                 {"PIPE", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 136                 {"ALRM", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 137                 {"CHLD", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 138                 {"CONT", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 139                 {"TSTP", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 140                 {"TTIN", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 141                 {"TTOU", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 142                 {"URG",  IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 143                 {"XCPU", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 144                 {"XFSZ", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 145                 {"VTALRM", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 146                 {"PROF", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 147                 {"WINCH", IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 148                 {"IO",   IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 149                 {"SYS",  IsSupported.NO, CanRegister.NO, CanRaise.NO, Invoked.NO},
 150         };
 151 
 152         return concatArrays(commonSignals, (Platform.isWindows() ? windowsSignals : posixSignals));
 153     }
 154 
 155     // Provider of invalid signal names
 156     @DataProvider(name = "invalidSunMiscSignalNames")
 157     Object[][] invalidSunMiscSignalNames() {
 158         return new Object[][]{
 159                 {""},
 160                 {"I"},
 161                 {"SIG"},
 162                 {"SIGabc"},
 163                 {"SIGINT"},     // prefix not allowed
 164                 {"abc"},
 165         };
 166     }
 167 
 168     static Object[][] concatArrays(Object[][]... arrays) {
 169         int l = 0;
 170         for (Object[][] a : arrays) {
 171             l += a.length;
 172         }
 173 
 174         Object[][] newArray = new Object[l][];
 175         l = 0;
 176         for (int i = 0; i < arrays.length; i++) {
 177             System.arraycopy(arrays[i], 0, newArray, l, arrays[i].length);
 178             l += arrays[i].length;
 179         }
 180 
 181         return newArray;
 182     }
 183 
 184     // Return true if the signal is one of the shutdown signals known to the VM
 185     private static boolean isShutdownSignal(Signal signal) {
 186         String name = signal.getName();
 187         return name.equals("INT") || name.equals("HUP") || name.equals("TERM");
 188     }
 189 
 190     /**
 191      * Quick verification of supported signals using sun.misc.Signal.
 192      *
 193      * @param name the signal name
 194      * @throws InterruptedException would be an error if thrown
 195      */
 196     @Test(dataProvider = "supportedSignals")
 197     static void testSunMisc(String name, IsSupported supported, CanRegister register,
 198                             CanRaise raise, Invoked invoked) throws InterruptedException {
 199         Handler h = new Handler();
 200         SignalHandler orig = null;
 201         Signal signal = null;
 202         try {
 203             signal = new Signal(name);
 204             Assert.assertEquals(supported, IsSupported.YES, "Unexpected support for " + name);
 205 
 206             Assert.assertEquals(signal.getName(), name, "getName() mismatch, ");
 207 
 208             Assert.assertEquals(signal.toString(), "SIG" + name, "toString() mismatch, ");
 209 
 210             try {
 211                 orig = Signal.handle(signal, h);
 212                 printf("oldHandler: %s%n", orig);
 213                 Assert.assertEquals(CanRegister.YES, register, "Unexpected handle succeeded " + name);
 214                 try {
 215                     Signal.raise(signal);
 216                     Assert.assertEquals(CanRaise.YES, raise, "Unexpected raise success for " + name);
 217                     Invoked inv = h.semaphore().tryAcquire(Utils.adjustTimeout(100L),
 218                             TimeUnit.MILLISECONDS) ? Invoked.YES : Invoked.NO;
 219                     if (!isShutdownSignal(signal)) {
 220                         // Normal case
 221                         Assert.assertEquals(inv, invoked, "handler not invoked;");
 222                     } else {
 223                         if (orig == SignalHandler.SIG_IGN) {
 224                             Assert.assertEquals(inv, Invoked.NO, "handler should not be invoked");
 225                         } else {
 226                             Assert.assertEquals(inv, invoked, "handler not invoked;");
 227                         }
 228                     }
 229                 } catch (IllegalArgumentException uoe3) {
 230                     Assert.assertNotEquals(CanRaise.YES, raise, "raise failed for " + name +
 231                             ": " + uoe3.getMessage());
 232                 }
 233             } catch (IllegalArgumentException uoe2) {
 234                 Assert.assertNotEquals(CanRegister.YES, register, "handle failed for: " + name +
 235                         ": " + uoe2.getMessage());
 236             }
 237         } catch (IllegalArgumentException uoe) {
 238             Assert.assertNotEquals(IsSupported.YES, supported, "Support missing for " + name +
 239                     ": " + uoe.getMessage());
 240             return;
 241         } finally {
 242             // Restore original signal handler
 243             if (orig != null && signal != null) {
 244                 Signal.handle(signal, orig);
 245             }
 246         }
 247     }
 248 
 249     // Test Signal is equal to itself and not equals to others
 250     @Test(dataProvider = "supportedSignals")
 251     static void testEquals(String name, IsSupported supported, CanRegister register,
 252                            CanRaise raise, Invoked invoked) {
 253         Object[][] data = supportedSignals();
 254         for (int i = 0; i < data.length; i++) {
 255             IsSupported otherSupported = (IsSupported) data[i][1];
 256             if (supported == IsSupported.NO || otherSupported == IsSupported.NO) {
 257                 continue;
 258             }
 259             String otherName = (String) data[i][0];
 260 
 261             Signal sig1 = new Signal(name);
 262             Signal sig2 = new Signal(otherName);
 263             if (name.equals(otherName)) {
 264                 Assert.assertEquals(sig1, sig2, "Equals failed; ");
 265                 Assert.assertEquals(sig1.hashCode(), sig2.hashCode(), "HashCode wrong; ");
 266             } else {
 267                 Assert.assertNotEquals(sig1, sig2, "NotEquals failed; ");
 268                 Assert.assertNotEquals(sig1.hashCode(), sig2.hashCode(), "HashCode wrong; ");
 269             }
 270         }
 271     }
 272 
 273     @Test(dataProvider = "invalidSunMiscSignalNames")
 274     static void testSunMiscIAE(String name) {
 275         try {
 276             new Signal(name);
 277             Assert.fail("Should have thrown IAE for signal: " + name);
 278         } catch (IllegalArgumentException iae) {
 279             Assert.assertEquals(iae.getMessage(), "Unknown signal: " + name, "getMessage() incorrect; ");
 280         }
 281     }
 282 
 283     // Note: JDK 8 did not check/throw NPE, passing null resulted in a segv
 284     @Test(expectedExceptions = NullPointerException.class)
 285     static void nullSignal() {
 286         new Signal(null);
 287     }
 288 
 289     // Test expected exception when raising a signal when no handler defined
 290     @Test
 291     static void testRaiseNoConsumer() {
 292         Signal signal = new Signal("INT");
 293         SignalHandler orig = null;
 294         try {
 295             orig = Signal.handle(signal, SignalHandler.SIG_DFL);
 296             printf("oldHandler: %s%n", orig);
 297             if (orig == SignalHandler.SIG_IGN) {
 298                 // SIG_IGN for TERM means it cannot be handled
 299                 return;
 300             }
 301             Signal.raise(signal);
 302             Assert.fail("Should have thrown IllegalArgumentException");
 303         } catch (IllegalArgumentException iae) {
 304             printf("IAE message: %s%n", iae.getMessage());
 305         } finally {
 306             // Restore original signal handler
 307             if (orig != null && signal != null) {
 308                 Signal.handle(signal, orig);
 309             }
 310         }
 311     }
 312 
 313     /**
 314      * The thread that runs the handler for sun.misc.Signal should be a
 315      * Daemon thread.
 316      */
 317     @Test
 318     static void isDaemonThread() throws InterruptedException {
 319         if (RUNNING_WITH_Xrs) {
 320             return;
 321         }
 322         Handler handler = new Handler();
 323         Signal signal = new Signal("INT");
 324         SignalHandler orig = Signal.handle(signal, handler);
 325         printf("oldHandler: %s%n", orig);
 326         if (orig == SignalHandler.SIG_IGN) {
 327             // SIG_IGN for INT means it cannot be handled
 328             return;
 329         }
 330 
 331         Signal.raise(signal);
 332         boolean handled = handler.semaphore()
 333                 .tryAcquire(Utils.adjustTimeout(100L), TimeUnit.MILLISECONDS);
 334         if (!handled) {
 335             // For debug try again
 336             printf("Second try to see signal");
 337             handled = handler.semaphore()
 338                     .tryAcquire(Utils.adjustTimeout(2L), TimeUnit.SECONDS);
 339         }
 340         Assert.assertEquals(handled, !RUNNING_WITH_Xrs,
 341                 "raising s.m.Signal did not get a callback;");
 342 
 343         Assert.assertTrue(handler.wasDaemon(), "Thread.isDaemon running the handler; ");
 344     }
 345 
 346     // Check that trying to invoke SIG_DFL.handle throws UnsupportedOperationException.
 347     @Test(expectedExceptions = UnsupportedOperationException.class)
 348     static void cannotHandleSIGDFL() {
 349         Signal signal = new Signal("INT");
 350         Assert.assertNotNull(SignalHandler.SIG_DFL, "SIG_DFL null; ");
 351         SignalHandler.SIG_DFL.handle(signal);
 352     }
 353 
 354     // Check that trying to invoke SIG_IGN.handle throws UnsupportedOperationException.
 355     @Test(expectedExceptions = UnsupportedOperationException.class)
 356     static void cannotHandleSIGIGN() {
 357         Signal signal = new Signal("INT");
 358         Assert.assertNotNull(SignalHandler.SIG_IGN, "SIG_IGN null; ");
 359         SignalHandler.SIG_IGN.handle(signal);
 360     }
 361 
 362     // Check that setting a Signal handler returns the previous handler.
 363     @Test()
 364     static void checkLastHandler() {
 365         if (RUNNING_WITH_Xrs) {
 366             return;
 367         }
 368         Signal signal = new Signal("TERM");
 369         Handler h1 = new Handler();
 370         Handler h2 = new Handler();
 371         SignalHandler orig = Signal.handle(signal, h1);
 372         if (orig == SignalHandler.SIG_IGN) {
 373             // SIG_IGN for TERM means it cannot be handled
 374             return;
 375         }
 376 
 377         try {
 378             SignalHandler prev = Signal.handle(signal, h2);
 379             Assert.assertSame(prev, h1, "prev handler mismatch");
 380 
 381             prev = Signal.handle(signal, h1);
 382             Assert.assertSame(prev, h2, "prev handler mismatch");
 383         } finally {
 384             if (orig != null && signal != null) {
 385                 Signal.handle(signal, orig);
 386             }
 387         }
 388     }
 389 
 390     /**
 391      * Test Handler, a SignalHandler for Signal notifications.
 392      * Signals a semaphore when invoked and records whether
 393      * the thread calling the Handler was a daemon.
 394      */
 395     static class Handler implements SignalHandler {
 396         // A semaphore to check for accept being called
 397         Semaphore sema = new Semaphore(0);
 398 
 399         Boolean wasDaemon = null;
 400 
 401         Semaphore semaphore() {
 402             return sema;
 403         }
 404 
 405         synchronized Boolean wasDaemon() {
 406             return wasDaemon;
 407         }
 408 
 409         /**
 410          * Releases the semaphore when called as SignalHandler.handle.
 411          *
 412          * @param signal the Signal that occurred
 413          */
 414         @Override
 415         public void handle(Signal signal) {
 416             synchronized (this) {
 417                 wasDaemon = Thread.currentThread().isDaemon();
 418             }
 419             sema.release();
 420             printf("sun.misc.handle sig: %s, num: %d%n", signal.getName(), signal.getNumber());
 421         }
 422 
 423         public String toString() {
 424             return "Handler: sem: " + sema.getQueueLength() +
 425                     ", wasDaemon: " + Objects.toString(wasDaemon());
 426         }
 427     }
 428 
 429     // Main can be used to run the tests from the command line with only testng.jar.
 430     @SuppressWarnings("raw_types")
 431     @Test(enabled = false)
 432     public static void main(String[] args) {
 433         Class<?>[] testclass = {SunMiscSignalTest.class};
 434         TestNG testng = new TestNG();
 435         testng.setTestClasses(testclass);
 436         testng.run();
 437     }
 438 
 439 }