diff --git a/cmd/cmd.go b/cmd/cmd.go index 7f22a48..71b64cf 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -21,10 +21,3 @@ var ( func Execute() error { return rootCmd.Execute() } - -func init() { - rootCmd.AddCommand(runCmd) - rootCmd.AddCommand(validateCmd) - rootCmd.AddCommand(exportCmd) - rootCmd.AddCommand(reloadCmd) -} diff --git a/cmd/export.go b/cmd/export.go index 4c73d93..d09f105 100644 --- a/cmd/export.go +++ b/cmd/export.go @@ -27,5 +27,6 @@ var ( ) func init() { + rootCmd.AddCommand(exportCmd) exportCmd.AddCommand(exportOutlineCmd) } diff --git a/cmd/reload.go b/cmd/reload.go index 4b46097..8a71b44 100644 --- a/cmd/reload.go +++ b/cmd/reload.go @@ -6,26 +6,42 @@ package cmd import ( + "fmt" "github.com/spf13/cobra" + "github.com/v2rayA/dae/cmd/internal" "os" "strconv" + "strings" "syscall" ) var ( reloadCmd = &cobra.Command{ - Use: "reload pid", + Use: "reload [pid]", Run: func(cmd *cobra.Command, args []string) { + internal.AutoSu() if len(args) == 0 { - cmd.Help() - os.Exit(1) + _pid, err := os.ReadFile(PidFilePath) + if err != nil { + fmt.Println("Failed to read pid file:", err) + os.Exit(1) + } + args = []string{strings.TrimSpace(string(_pid))} } pid, err := strconv.Atoi(args[0]) if err != nil { cmd.Help() os.Exit(1) } - syscall.Kill(pid, syscall.SIGUSR1) + if err = syscall.Kill(pid, syscall.SIGUSR1); err != nil { + fmt.Println(err) + os.Exit(1) + } + fmt.Println("OK") }, } ) + +func init() { + rootCmd.AddCommand(reloadCmd) +} \ No newline at end of file diff --git a/cmd/run.go b/cmd/run.go index f6bbb71..0e99541 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -10,6 +10,7 @@ import ( "github.com/v2rayA/dae/common/subscription" "github.com/v2rayA/dae/config" "github.com/v2rayA/dae/control" + "github.com/v2rayA/dae/pkg/config_parser" "github.com/v2rayA/dae/pkg/logger" "os" "os/signal" @@ -92,6 +93,7 @@ func Run(log *logrus.Logger, conf *config.Config) (err error) { sigs <- nil }() reloading := false + isSuspend := false loop: for sig := range sigs { switch sig { @@ -114,6 +116,9 @@ loop: // Listening error. break loop } + case syscall.SIGHUP: + isSuspend = true + fallthrough case syscall.SIGUSR1: // Reload signal. log.Warnln("[Reload] Received reload signal; prepare to reload") @@ -121,15 +126,29 @@ loop: // Load new config. log.Warnln("[Reload] Load new config") - newConf, includes, err := readConfig(cfgFile) - if err != nil { - log.WithFields(logrus.Fields{ - "err": err, - }).Errorln("[Reload] Failed to reload") - sdnotify.Ready() - continue + var newConf *config.Config + if isSuspend { + isSuspend = false + newConf, err = emptyConfig() + if err != nil { + log.WithFields(logrus.Fields{ + "err": err, + }).Errorln("[Reload] Failed to reload") + sdnotify.Ready() + continue + } + } else { + var includes []string + newConf, includes, err = readConfig(cfgFile) + if err != nil { + log.WithFields(logrus.Fields{ + "err": err, + }).Errorln("[Reload] Failed to reload") + sdnotify.Ready() + continue + } + log.Infof("Include config files: [%v]", strings.Join(includes, ", ")) } - log.Infof("Include config files: [%v]", strings.Join(includes, ", ")) // New logger. log = logger.NewLogger(newConf.Global.LogLevel, disableTimestamp) logrus.SetLevel(log.Level) @@ -244,3 +263,18 @@ func readConfig(cfgFile string) (conf *config.Config, includes []string, err err } return conf, includes, nil } + +func emptyConfig() (conf *config.Config, err error) { + sections, err := config_parser.Parse(`global{} routing{}`) + if err != nil { + return nil, err + } + if conf, err = config.New(sections); err != nil { + return nil, err + } + return conf, nil +} + +func init() { + rootCmd.AddCommand(runCmd) +} diff --git a/cmd/suspend.go b/cmd/suspend.go new file mode 100644 index 0000000..2c59fc4 --- /dev/null +++ b/cmd/suspend.go @@ -0,0 +1,47 @@ +/* + * SPDX-License-Identifier: AGPL-3.0-only + * Copyright (c) 2023, v2rayA Organization + */ + +package cmd + +import ( + "fmt" + "github.com/spf13/cobra" + "github.com/v2rayA/dae/cmd/internal" + "os" + "strconv" + "strings" + "syscall" +) + +var ( + suspendCmd = &cobra.Command{ + Use: "suspend [pid]", + Run: func(cmd *cobra.Command, args []string) { + internal.AutoSu() + if len(args) == 0 { + _pid, err := os.ReadFile(PidFilePath) + if err != nil { + fmt.Println("Failed to read pid file:", err) + os.Exit(1) + } + args = []string{strings.TrimSpace(string(_pid))} + } + pid, err := strconv.Atoi(args[0]) + if err != nil { + cmd.Help() + os.Exit(1) + } + if err = syscall.Kill(pid, syscall.SIGHUP); err != nil { + fmt.Println(err) + os.Exit(1) + } + fmt.Println("OK") + }, + } +) + +func init() { + rootCmd.AddCommand(suspendCmd) +} diff --git a/cmd/validate.go b/cmd/validate.go index 59bc712..401ff91 100644 --- a/cmd/validate.go +++ b/cmd/validate.go @@ -31,5 +31,7 @@ var ( ) func init() { + rootCmd.AddCommand(validateCmd) + validateCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file") } diff --git a/control/dns_control.go b/control/dns_control.go index 3615fee..d3cc93c 100644 --- a/control/dns_control.go +++ b/control/dns_control.go @@ -399,7 +399,7 @@ func (c *DnsController) dialSend(req *udpRequest, data []byte, upstream *dns.Ups } }() - _ = conn.SetDeadline(time.Now().Add(DnsNatTimeout)) + _ = conn.SetDeadline(time.Now().Add(5 * time.Second)) _, err = conn.Write(data) if err != nil { if c.log.IsLevelEnabled(logrus.DebugLevel) { @@ -446,7 +446,7 @@ func (c *DnsController) dialSend(req *udpRequest, data []byte, upstream *dns.Ups } }() - _ = conn.SetDeadline(time.Now().Add(DnsNatTimeout)) + _ = conn.SetDeadline(time.Now().Add(5 * time.Second)) // We should write two byte length in the front of TCP DNS request. bReq := pool.Get(2 + len(data)) defer pool.Put(bReq) diff --git a/docs/getting-started/external-dns.md b/docs/getting-started/external-dns.md index ff5adef..b062229 100644 --- a/docs/getting-started/external-dns.md +++ b/docs/getting-started/external-dns.md @@ -83,9 +83,3 @@ You should configure dae as follows: 4. If you bind to LAN, make sure your DHCP server will distribute dae as the DNS server (DNS request should be forwarded by dae for domain based traffic split). -## - - - ``` - - ``` \ No newline at end of file diff --git a/docs/getting-started/reload-and-suspend.md b/docs/getting-started/reload-and-suspend.md new file mode 100644 index 0000000..f9eecd5 --- /dev/null +++ b/docs/getting-started/reload-and-suspend.md @@ -0,0 +1,30 @@ +# Reload and Suspend + +dae supports configuration reloading and program suspending, which can help you save a lot of time when modifying the configuration or temporarily suspend dae. + +**Reload** + +Generally, dae won't interrupt connections when reloading configuration. And it is very fast compared to restart. + +Usage: + +```shell +dae reload +``` + +**Suspend** + +It will be useful if you want to suspend dae temporarily and recover it later. + +Usage: + +```shell +dae suspend +``` + +If you want to recover, use reload: + +```shell +dae reload +``` +