1  // Copyright 2015 The Go Authors. All rights reserved.
       2  // Use of this source code is governed by a BSD-style
       3  // license that can be found in the LICENSE file.
       4  
       5  // Test that a signal handler works in non-Go code when using
       6  // os/signal.Notify.
       7  // This is a lot like misc/cgo/testcarchive/main3.c.
       8  
       9  #include <signal.h>
      10  #include <stdio.h>
      11  #include <stdlib.h>
      12  #include <string.h>
      13  #include <time.h>
      14  #include <sched.h>
      15  #include <dlfcn.h>
      16  
      17  static void die(const char* msg) {
      18  	perror(msg);
      19  	exit(EXIT_FAILURE);
      20  }
      21  
      22  static volatile sig_atomic_t sigioSeen;
      23  
      24  static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
      25  	sigioSeen = 1;
      26  }
      27  
      28  int main(int argc, char** argv) {
      29  	int verbose;
      30  	struct sigaction sa;
      31  	void* handle;
      32  	void (*fn1)(void);
      33  	int (*sawSIGIO)(void);
      34  	int i;
      35  	struct timespec ts;
      36  
      37  	verbose = argc > 2;
      38  	setvbuf(stdout, NULL, _IONBF, 0);
      39  
      40  	if (verbose) {
      41  		printf("calling sigaction\n");
      42  	}
      43  
      44  	memset(&sa, 0, sizeof sa);
      45  	sa.sa_sigaction = ioHandler;
      46  	if (sigemptyset(&sa.sa_mask) < 0) {
      47  		die("sigemptyset");
      48  	}
      49  	sa.sa_flags = SA_SIGINFO;
      50  	if (sigaction(SIGIO, &sa, NULL) < 0) {
      51  		die("sigaction");
      52  	}
      53  
      54  	if (verbose) {
      55  		printf("calling dlopen\n");
      56  	}
      57  
      58  	handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL);
      59  	if (handle == NULL) {
      60  		fprintf(stderr, "%s\n", dlerror());
      61  		exit(EXIT_FAILURE);
      62  	}
      63  
      64  	// At this point there should not be a Go signal handler
      65  	// installed for SIGIO.
      66  
      67  	if (verbose) {
      68  		printf("raising SIGIO\n");
      69  	}
      70  
      71  	if (raise(SIGIO) < 0) {
      72  		die("raise");
      73  	}
      74  
      75  	if (verbose) {
      76  		printf("waiting for sigioSeen\n");
      77  	}
      78  
      79  	// Wait until the signal has been delivered.
      80  	i = 0;
      81  	while (!sigioSeen) {
      82  		ts.tv_sec = 0;
      83  		ts.tv_nsec = 1000000;
      84  		nanosleep(&ts, NULL);
      85  		i++;
      86  		if (i > 5000) {
      87  			fprintf(stderr, "looping too long waiting for signal\n");
      88  			exit(EXIT_FAILURE);
      89  		}
      90  	}
      91  
      92  	sigioSeen = 0;
      93  
      94  	// Tell the Go code to catch SIGIO.
      95  
      96  	if (verbose) {
      97  		printf("calling dlsym\n");
      98  	}
      99  
     100  	fn1 = (void(*)(void))dlsym(handle, "CatchSIGIO");
     101  	if (fn1 == NULL) {
     102  		fprintf(stderr, "%s\n", dlerror());
     103  		exit(EXIT_FAILURE);
     104  	}
     105  
     106  	if (verbose) {
     107  		printf("calling CatchSIGIO\n");
     108  	}
     109  
     110  	fn1();
     111  
     112  	if (verbose) {
     113  		printf("raising SIGIO\n");
     114  	}
     115  
     116  	if (raise(SIGIO) < 0) {
     117  		die("raise");
     118  	}
     119  
     120  	if (verbose) {
     121  		printf("calling dlsym\n");
     122  	}
     123  
     124  	// Check that the Go code saw SIGIO.
     125  	sawSIGIO = (int (*)(void))dlsym(handle, "SawSIGIO");
     126  	if (sawSIGIO == NULL) {
     127  		fprintf(stderr, "%s\n", dlerror());
     128  		exit(EXIT_FAILURE);
     129  	}
     130  
     131  	if (verbose) {
     132  		printf("calling SawSIGIO\n");
     133  	}
     134  
     135  	if (!sawSIGIO()) {
     136  		fprintf(stderr, "Go handler did not see SIGIO\n");
     137  		exit(EXIT_FAILURE);
     138  	}
     139  
     140  	if (sigioSeen != 0) {
     141  		fprintf(stderr, "C handler saw SIGIO when only Go handler should have\n");
     142  		exit(EXIT_FAILURE);
     143  	}
     144  
     145  	// Tell the Go code to stop catching SIGIO.
     146  
     147  	if (verbose) {
     148  		printf("calling dlsym\n");
     149  	}
     150  
     151  	fn1 = (void(*)(void))dlsym(handle, "ResetSIGIO");
     152  	if (fn1 == NULL) {
     153  		fprintf(stderr, "%s\n", dlerror());
     154  		exit(EXIT_FAILURE);
     155  	}
     156  
     157  	if (verbose) {
     158  		printf("calling ResetSIGIO\n");
     159  	}
     160  
     161  	fn1();
     162  
     163  	if (verbose) {
     164  		printf("raising SIGIO\n");
     165  	}
     166  
     167  	if (raise(SIGIO) < 0) {
     168  		die("raise");
     169  	}
     170  
     171  	if (verbose) {
     172  		printf("calling SawSIGIO\n");
     173  	}
     174  
     175  	if (sawSIGIO()) {
     176  		fprintf(stderr, "Go handler saw SIGIO after Reset\n");
     177  		exit(EXIT_FAILURE);
     178  	}
     179  
     180  	if (verbose) {
     181  		printf("waiting for sigioSeen\n");
     182  	}
     183  
     184  	// Wait until the signal has been delivered.
     185  	i = 0;
     186  	while (!sigioSeen) {
     187  		ts.tv_sec = 0;
     188  		ts.tv_nsec = 1000000;
     189  		nanosleep(&ts, NULL);
     190  		i++;
     191  		if (i > 5000) {
     192  			fprintf(stderr, "looping too long waiting for signal\n");
     193  			exit(EXIT_FAILURE);
     194  		}
     195  	}
     196  
     197  	printf("PASS\n");
     198  	return 0;
     199  }