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