Source file src/runtime/testdata/testprognet/signalexec.go

     1  // Copyright 2017 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  //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
     6  // +build darwin dragonfly freebsd linux netbsd openbsd
     7  
     8  // This is in testprognet instead of testprog because testprog
     9  // must not import anything (like net, but also like os/signal)
    10  // that kicks off background goroutines during init.
    11  
    12  package main
    13  
    14  import (
    15  	"fmt"
    16  	"io"
    17  	"os"
    18  	"os/exec"
    19  	"os/signal"
    20  	"runtime"
    21  	"sync"
    22  	"syscall"
    23  	"time"
    24  )
    25  
    26  func init() {
    27  	register("SignalDuringExec", SignalDuringExec)
    28  	register("SignalDuringExecPgrp", SignalDuringExecPgrp)
    29  	register("Nop", Nop)
    30  }
    31  
    32  func SignalDuringExec() {
    33  	// Re-launch ourselves in a new process group.
    34  	cmd := exec.Command(os.Args[0], "SignalDuringExecPgrp")
    35  	cmd.Stdout = os.Stdout
    36  	cmd.Stderr = os.Stderr
    37  	cmd.SysProcAttr = &syscall.SysProcAttr{
    38  		Setpgid: true,
    39  	}
    40  
    41  	// Start the new process with an extra pipe. It will
    42  	// exit if the pipe is closed.
    43  	rp, wp, err := os.Pipe()
    44  	if err != nil {
    45  		fmt.Printf("Failed to create pipe: %v", err)
    46  		return
    47  	}
    48  	cmd.ExtraFiles = []*os.File{rp}
    49  
    50  	// Run the command.
    51  	if err := cmd.Run(); err != nil {
    52  		fmt.Printf("Run failed: %v", err)
    53  	}
    54  
    55  	// We don't actually need to write to the pipe, it just
    56  	// needs to get closed, which will happen on process
    57  	// exit.
    58  	runtime.KeepAlive(wp)
    59  }
    60  
    61  func SignalDuringExecPgrp() {
    62  	// Grab fd 3 which is a pipe we need to read on.
    63  	f := os.NewFile(3, "pipe")
    64  	go func() {
    65  		// Nothing will ever get written to the pipe, so we'll
    66  		// just block on it. If it closes, ReadAll will return
    67  		// one way or another, at which point we'll exit.
    68  		io.ReadAll(f)
    69  		os.Exit(1)
    70  	}()
    71  
    72  	// This is just for SignalDuringExec.
    73  	pgrp := syscall.Getpgrp()
    74  
    75  	const tries = 10
    76  
    77  	var wg sync.WaitGroup
    78  	c := make(chan os.Signal, tries)
    79  	signal.Notify(c, syscall.SIGWINCH)
    80  	wg.Add(1)
    81  	go func() {
    82  		defer wg.Done()
    83  		for range c {
    84  		}
    85  	}()
    86  
    87  	for i := 0; i < tries; i++ {
    88  		time.Sleep(time.Microsecond)
    89  		wg.Add(2)
    90  		go func() {
    91  			defer wg.Done()
    92  			cmd := exec.Command(os.Args[0], "Nop")
    93  			cmd.Stdout = os.Stdout
    94  			cmd.Stderr = os.Stderr
    95  			if err := cmd.Run(); err != nil {
    96  				fmt.Printf("Start failed: %v", err)
    97  			}
    98  		}()
    99  		go func() {
   100  			defer wg.Done()
   101  			syscall.Kill(-pgrp, syscall.SIGWINCH)
   102  		}()
   103  	}
   104  
   105  	signal.Stop(c)
   106  	close(c)
   107  	wg.Wait()
   108  
   109  	fmt.Println("OK")
   110  }
   111  
   112  func Nop() {
   113  	// This is just for SignalDuringExec.
   114  }
   115  

View as plain text