@@ -4,30 +4,59 @@ import (
44 "fmt"
55 "os"
66 "os/exec"
7+ "os/signal"
8+ "path/filepath"
79 "strings"
10+ "syscall"
811 "time"
912
1013 "github.com/BitPonyLLC/huekeys/buildinfo"
1114 "github.com/BitPonyLLC/huekeys/internal/menu"
15+ "github.com/BitPonyLLC/huekeys/pkg/patterns"
16+ "github.com/BitPonyLLC/huekeys/pkg/pidpath"
17+ "github.com/BitPonyLLC/huekeys/pkg/util"
1218
1319 "github.com/rs/zerolog/log"
1420 "github.com/spf13/cobra"
1521 "github.com/spf13/viper"
1622)
1723
1824var patternName string
25+ var menuPidPath * pidpath.PidPath
26+ var restarting = false
1927
2028func init () {
2129 menuCmd .Flags ().StringVarP (& patternName , "pattern" , "p" , patternName , "name of pattern to run at start" )
2230 viper .BindPFlag ("menu.pattern" , menuCmd .Flags ().Lookup ("pattern" ))
31+
32+ defaultPidPath := filepath .Join (os .TempDir (), buildinfo .App .Name + "-menu.pid" )
33+ menuCmd .Flags ().String ("pidpath" , defaultPidPath , "pathname of the menu pidfile" )
34+ viper .BindPFlag ("menu.pidpath" , menuCmd .Flags ().Lookup ("pidpath" ))
35+
2336 rootCmd .AddCommand (menuCmd )
2437}
2538
2639var menuCmd = & cobra.Command {
27- Use : "menu" ,
28- Short : "Display a menu in the system tray" ,
29- PreRunE : ensureWaitRunning ,
40+ Use : "menu" ,
41+ Short : "Display a menu in the system tray" ,
42+ PreRunE : func (cmd * cobra.Command , _ []string ) error {
43+ err := menuPidPath .CheckAndSet ()
44+ if err != nil {
45+ return err
46+ }
47+
48+ return ensureWaitRunning (cmd )
49+ },
3050 RunE : func (cmd * cobra.Command , _ []string ) error {
51+ restart := make (chan os.Signal , 1 )
52+ signal .Notify (restart , syscall .SIGHUP )
53+ go func () {
54+ sig := <- restart
55+ restarting = true
56+ log .Info ().Str ("signal" , sig .String ()).Msg ("restarting" )
57+ cancelFunc ()
58+ }()
59+
3160 args := []string {}
3261 for c := runCmd ; c != rootCmd ; c = c .Parent () {
3362 args = append ([]string {c .Name ()}, args ... )
@@ -45,7 +74,24 @@ var menuCmd = &cobra.Command{
4574 }
4675 }
4776
48- return menu .Show (cmd .Context (), & log .Logger , viper .GetString ("sockpath" ))
77+ return menu .Show (cmd .Context (), & log .Logger , waitSockPath ())
78+ },
79+ PostRun : func (_ * cobra.Command , _ []string ) {
80+ if menuPidPath != nil {
81+ menuPidPath .Release ()
82+ }
83+
84+ if ! restarting {
85+ return
86+ }
87+
88+ mCmd := exec .Command (os .Args [0 ], os .Args [1 :]... )
89+ err := mCmd .Start ()
90+ if err != nil {
91+ log .Error ().Err (err ).Msg ("failed to restart menu" )
92+ }
93+
94+ log .Info ().Str ("cmd" , mCmd .String ()).Interface ("menu" , mCmd .Process ).Msg ("new menu process started" )
4995 },
5096 Args : func (cmd * cobra.Command , args []string ) error {
5197 if patternName == "" {
@@ -62,8 +108,8 @@ var menuCmd = &cobra.Command{
62108 },
63109}
64110
65- func ensureWaitRunning (cmd * cobra.Command , args [] string ) error {
66- if ! pidPath .IsOurs () && pidPath .IsRunning () {
111+ func ensureWaitRunning (cmd * cobra.Command ) error {
112+ if ! waitPidPath .IsOurs () && waitPidPath .IsRunning () {
67113 // wait is already executing in the background
68114 return nil
69115 }
@@ -73,19 +119,43 @@ func ensureWaitRunning(cmd *cobra.Command, args []string) error {
73119 return fmt .Errorf ("unable to determine executable pathname: %w" , err )
74120 }
75121
76- // use sh exec to remove sudo parent processes hanging around
77- hkCmd := "exec " + exe + " run wait &"
78- execArgs := []string {"sudo" , "-E" , "sh" , "-c" , hkCmd }
79- execStr := strings .Join (execArgs , " " )
122+ dpEnv , err := patterns .DesktopPatternEnv ()
123+ if err != nil {
124+ if util .IsTTY (os .Stderr ) {
125+ cmd .PrintErrln (err )
126+ } else {
127+ log .Warn ().Err (err ).Msg ("" )
128+ }
129+ }
130+
131+ var execName string
132+ var execArgs []string
133+
134+ // checking only stdin isn't enough: it's attached when launched from gnome!
135+ if util .IsTTY (os .Stdin ) && util .IsTTY (os .Stdout ) {
136+ execName = "sudo"
137+ execArgs = []string {}
138+ } else {
139+ // need to open a dialog for permission...
140+ execName = "pkexec"
141+ execArgs = []string {"--user" , "root" }
142+ }
143+
144+ // use sh exec to let parent processes exit
145+ hkCmd := fmt .Sprint ("export " , dpEnv , "; exec " , exe ,
146+ " --config " , viper .GetViper ().ConfigFileUsed (), " run wait &" )
147+ execArgs = append (execArgs , "sh" , "-c" , hkCmd )
148+
149+ execStr := fmt .Sprint (execName , " " , strings .Join (execArgs , " " ))
80150 log .Debug ().Str ("cmd" , execStr ).Msg ("" )
81151
82- err = exec .Command ("sudo" , "-E" , "sh" , "-c" , hkCmd ).Run ()
152+ err = exec .Command (execName , execArgs ... ).Run ()
83153 if err != nil {
84154 return fmt .Errorf ("unable to run %s: %w" , execStr , err )
85155 }
86156
87157 // wait a second for socket to be ready...
88- sockPath := viper . GetString ( "sockpath" )
158+ sockPath := waitSockPath ( )
89159 for i := 0 ; i < 10 ; i += 1 {
90160 time .Sleep (50 * time .Millisecond )
91161 _ , err := os .Stat (sockPath )
0 commit comments