1
1
package main
2
2
3
3
import (
4
+ "context"
4
5
"fmt"
5
6
"os"
7
+ "os/signal"
8
+ "os/user"
6
9
"strings"
7
10
"sync"
11
+ "syscall"
8
12
"time"
9
13
10
14
ps "github.com/mitchellh/go-ps"
24
28
"preexec-ts" , 0 ,
25
29
"pre-execution timestamp to gauge how log execution took" ,
26
30
)
31
+ flgQTimeout = cmdQuery .PersistentFlags ().Duration (
32
+ "timeout" , 0 ,
33
+ "timeout after which to give up" ,
34
+ )
27
35
)
28
36
37
+ func init () {
38
+ cmdQuery .RunE = cmdQueryRun
39
+ }
40
+
29
41
const (
30
42
_partStatus = "st"
31
43
_partTimestamp = "ts"
@@ -38,6 +50,11 @@ const (
38
50
_partPidShellExec = "pid_shell_exec"
39
51
_partPidParent = "pid_parent"
40
52
_partPidParentExec = "pid_parent_exec"
53
+ _partPidRemote = "pid_remote"
54
+ _partPidRemoteExec = "pid_remote_exec"
55
+
56
+ _partSessionUsername = "session_username"
57
+ _partSessionHostname = "session_hostname"
41
58
42
59
_partVcs = "vcs"
43
60
_partVcsBranch = "vcs_br"
@@ -57,44 +74,77 @@ const (
57
74
_partVcsGitIdxExcluded = "git_idx_excl"
58
75
)
59
76
60
- func init () {
61
- cmdQuery .RunE = cmdQueryRun
62
- }
63
-
64
77
func timeFMT (ts time.Time ) string {
65
78
return ts .Format ("15:04:05 01/02/06" )
66
79
}
67
80
68
- func cmdQueryRun (_ * cobra.Command , _ []string ) error {
69
- tasks := new (AsyncTaskDispatcher )
81
+ func handleQUIT () context.CancelFunc {
82
+ sig := make (chan os.Signal , 1 )
83
+ signal .Notify (sig , os .Interrupt , os .Kill , syscall .SIGTERM )
70
84
71
- printCH := make ( chan shellKV )
72
- printerWG := new (sync. WaitGroup )
73
- printerWG . Add ( 1 )
85
+ // defer flog("terminating" )
86
+
87
+ // Stdout watchdog
74
88
go func () {
75
- defer printerWG .Done ()
76
- shellKVStaggeredPrinter (printCH , 20 * time .Millisecond , 600 * time .Millisecond )
89
+ // flog("start watchdog " + fmt.Sprintf("%d", os.Getppid()))
90
+ defer bgctxCancel ()
91
+
92
+ for {
93
+ if _ , err := os .Stdout .Stat (); err != nil {
94
+ // flog("terminating early")
95
+ return
96
+ }
97
+
98
+ tick := time .After (100 * time .Millisecond )
99
+ select {
100
+ case <- tick :
101
+ continue
102
+ case <- sig :
103
+ // flog("terminating early")
104
+ return
105
+ case <- bgctx .Done ():
106
+ return
107
+ }
108
+ }
77
109
}()
78
- printerStop := func () {
79
- close (printCH )
80
- printerWG .Wait ()
81
- }
82
- printPart := func (name string , value interface {}) {
83
- printCH <- shellKV {name , value }
110
+
111
+ return bgctxCancel
112
+ }
113
+
114
+ func cmdQueryRun (_ * cobra.Command , _ []string ) error {
115
+ defer bgctxCancel ()
116
+
117
+ printerStop , printPart := startPrinter ()
118
+ defer printerStop ()
119
+
120
+ if * flgQTimeout != 0 {
121
+ go func () {
122
+ // Timeout handler
123
+ select {
124
+ case <- bgctx .Done ():
125
+ return
126
+ case <- time .After (* flgQTimeout ):
127
+ printPart ("done" , "timeout" )
128
+ printerStop ()
129
+ bgctxCancel ()
130
+ os .Exit (1 )
131
+ }
132
+ }()
84
133
}
85
134
135
+ tasks := new (AsyncTaskDispatcher )
136
+ defer func () {
137
+ tasks .Wait ()
138
+ printPart ("done" , "ok" )
139
+ }()
140
+
86
141
nowTS := time .Now ()
87
142
printPart (_partTimestamp , timeFMT (nowTS ))
88
143
89
144
if * flgQCmdStatus != 0 {
90
145
printPart (_partStatus , fmt .Sprintf ("%#v" , * flgQCmdStatus ))
91
146
}
92
147
93
- defer func () {
94
- tasks .Wait ()
95
- printerStop ()
96
- }()
97
-
98
148
tasks .Dispatch (func () {
99
149
homeDir := os .Getenv ("HOME" )
100
150
@@ -105,6 +155,16 @@ func cmdQueryRun(_ *cobra.Command, _ []string) error {
105
155
printPart (_partWorkDirShort , trimPath (wdh ))
106
156
}
107
157
158
+ sessionUser , err := user .Current ()
159
+ if err == nil {
160
+ printPart (_partSessionUsername , sessionUser .Username )
161
+ }
162
+
163
+ sessionHostname , err := os .Hostname ()
164
+ if err == nil {
165
+ printPart (_partSessionHostname , sessionHostname )
166
+ }
167
+
108
168
if * flgQPreexecTS != 0 {
109
169
cmdTS := time .Unix (int64 (* flgQPreexecTS ), 0 )
110
170
@@ -116,32 +176,36 @@ func cmdQueryRun(_ *cobra.Command, _ []string) error {
116
176
})
117
177
118
178
tasks .Dispatch (func () {
119
- pidCurr := os .Getpid ()
120
- var pidShell ps.Process
121
-
122
- for i := 0 ; i < 3 ; i ++ {
123
- var err error
124
- pidShell , err = ps .FindProcess (pidCurr )
125
- if err != nil {
126
- return
127
- }
128
- pidCurr = pidShell .PPid ()
129
- }
130
-
131
- if pidShell == nil {
179
+ psChain , err := moduleFindProcessChain ()
180
+ if err != nil {
132
181
return
133
182
}
134
183
135
- printPart (_partPidShell , pidShell .Pid ())
136
- printPart (_partPidShellExec , pidShell .Executable ())
184
+ if len (psChain ) > 3 {
185
+ pidShell := psChain [1 ]
186
+ pidShellParent := psChain [2 ]
137
187
138
- pidShellParent , err := ps .FindProcess (pidShell .PPid ())
139
- if err != nil {
140
- return
188
+ pidShellExecName , _ , _ := strings .Cut (pidShell .Executable (), " " )
189
+ printPart (_partPidShell , pidShell .Pid ())
190
+ printPart (_partPidShellExec , pidShellExecName )
191
+
192
+ pidShellParentExecName , _ , _ := strings .Cut (pidShellParent .Executable (), " " )
193
+ printPart (_partPidParent , pidShellParent .Pid ())
194
+ printPart (_partPidParentExec , pidShellParentExecName )
141
195
}
142
196
143
- printPart (_partPidParent , pidShellParent .Pid ())
144
- printPart (_partPidParentExec , pidShellParent .Executable ())
197
+ var pidRemote ps.Process
198
+ for _ , psInfo := range psChain {
199
+ if strings .Contains (psInfo .Executable (), "ssh" ) {
200
+ pidRemote = psInfo
201
+ break
202
+ }
203
+ }
204
+ if pidRemote != nil {
205
+ pidShellRemoteExecName , _ , _ := strings .Cut (pidRemote .Executable (), " " )
206
+ printPart (_partPidRemote , pidRemote .Pid ())
207
+ printPart (_partPidRemoteExec , pidShellRemoteExecName )
208
+ }
145
209
})
146
210
147
211
tasks .Dispatch (func () {
@@ -267,3 +331,40 @@ func cmdQueryRun(_ *cobra.Command, _ []string) error {
267
331
268
332
return nil
269
333
}
334
+
335
+ func startPrinter () (func (), func (name string , value interface {})) {
336
+ printCH := make (chan shellKV )
337
+ printerWG := new (sync.WaitGroup )
338
+ printerWG .Add (1 )
339
+ go func () {
340
+ defer printerWG .Done ()
341
+ shellKVStaggeredPrinter (printCH , 20 * time .Millisecond , 600 * time .Millisecond )
342
+ }()
343
+ printerStop := func () {
344
+ close (printCH )
345
+ printerWG .Wait ()
346
+ }
347
+ printPart := func (name string , value interface {}) {
348
+ printCH <- shellKV {name , value }
349
+ }
350
+ return printerStop , printPart
351
+ }
352
+
353
+ func moduleFindProcessChain () ([]ps.Process , error ) {
354
+ psPTR := os .Getpid ()
355
+ var pidChain []ps.Process
356
+
357
+ for {
358
+ if psPTR == 0 {
359
+ break
360
+ }
361
+ psInfo , err := ps .FindProcess (psPTR )
362
+ if err != nil {
363
+ return nil , err
364
+ }
365
+ pidChain = append (pidChain , psInfo )
366
+ psPTR = psInfo .PPid ()
367
+ }
368
+
369
+ return pidChain , nil
370
+ }
0 commit comments