func (m *termMonitor) termOnStdinClose() { _, err := io.Copy(ioutil.Discard, os.Stdin) // io.Copy() will return a nil on EOF, since reaching EOF is // expected behavior. No matter what, if this unblocks, assume // that stdin is closed, and treat that as having received a // SIGTERM. log.Noticef("Stdin is closed or unreadable: %v", err) m.sigChan <- syscall.SIGTERM }
func (m *termMonitor) termOnPPIDChange(ppid int) { // Under most if not all U*IX systems, the parent PID will change // to that of init once the parent dies. There are several notable // exceptions (Slowlaris/Android), but the parent PID changes // under those platforms as well. // // Naturally we lose if the parent has died by the time when the // Getppid() call was issued in our parent, but, this is better // than nothing. const ppidPollInterval = 1 * time.Second for ppid == os.Getppid() { time.Sleep(ppidPollInterval) } // Treat the parent PID changing as the same as having received // a SIGTERM. log.Noticef("Parent pid changed: %d (was %d)", os.Getppid(), ppid) m.sigChan <- syscall.SIGTERM }
func main() { // Initialize the termination state monitor as soon as possible. termMon = newTermMonitor() // Handle the command line arguments. _, execName := path.Split(os.Args[0]) showVer := flag.Bool("version", false, "Print version and exit") logLevelStr := flag.String("logLevel", "ERROR", "Log level (ERROR/WARN/INFO/DEBUG)") enableLogging := flag.Bool("enableLogging", false, "Log to TOR_PT_STATE_LOCATION/"+obfs4proxyLogFile) unsafeLogging := flag.Bool("unsafeLogging", false, "Disable the address scrubber") flag.Parse() if *showVer { fmt.Printf("%s\n", getVersion()) os.Exit(0) } if err := log.SetLogLevel(*logLevelStr); err != nil { golog.Fatalf("[ERROR]: %s - failed to set log level: %s", execName, err) } // Determine if this is a client or server, initialize the common state. var ptListeners []net.Listener launched := false isClient, err := ptIsClient() if err != nil { golog.Fatalf("[ERROR]: %s - must be run as a managed transport", execName) } if stateDir, err = pt.MakeStateDir(); err != nil { golog.Fatalf("[ERROR]: %s - No state directory: %s", execName, err) } if err = log.Init(*enableLogging, path.Join(stateDir, obfs4proxyLogFile), *unsafeLogging); err != nil { golog.Fatalf("[ERROR]: %s - failed to initialize logging", execName) } if err = transports.Init(); err != nil { log.Errorf("%s - failed to initialize transports: %s", execName, err) os.Exit(-1) } log.Noticef("%s - launched", getVersion()) // Do the managed pluggable transport protocol configuration. if isClient { log.Infof("%s - initializing client transport listeners", execName) launched, ptListeners = clientSetup() } else { log.Infof("%s - initializing server transport listeners", execName) launched, ptListeners = serverSetup() } if !launched { // Initialization failed, the client or server setup routines should // have logged, so just exit here. os.Exit(-1) } log.Infof("%s - accepting connections", execName) defer func() { log.Noticef("%s - terminated", execName) }() // At this point, the pt config protocol is finished, and incoming // connections will be processed. Wait till the parent dies // (immediate exit), a SIGTERM is received (immediate exit), // or a SIGINT is received. if sig := termMon.wait(false); sig == syscall.SIGTERM { return } // Ok, it was the first SIGINT, close all listeners, and wait till, // the parent dies, all the current connections are closed, or either // a SIGINT/SIGTERM is received, and exit. for _, ln := range ptListeners { ln.Close() } termMon.wait(true) }