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