github.com/whatap/golib@v0.0.22/util/cmdutil/CMDutil.go (about) 1 package cmdutil 2 3 import ( 4 "bufio" 5 "bytes" 6 "container/list" 7 "fmt" 8 9 //"log" 10 "os" 11 "os/exec" 12 13 //"syscall" 14 //"runtime/debug" 15 "runtime" 16 "strconv" 17 "strings" 18 19 //"gitlab.whatap.io/go/agent/util/logutil" 20 "github.com/whatap/golib/util/stringutil" 21 ) 22 23 // Pipeline strings together the given exec.Cmd commands in a similar fashion 24 // to the Unix pipeline. Each command's standard output is connected to the 25 // standard input of the next command, and the output of the final command in 26 // the pipeline is returned, along with the collected standard error of all 27 // commands and the first error found (if any). 28 // 29 // To provide input to the pipeline, assign an io.Reader to the first's Stdin. 30 func Pipeline(cmds ...*exec.Cmd) (pipeLineOutput, collectedStandardError []byte, pipeLineError error) { 31 defer func() { 32 for _, cmd := range cmds { 33 //syscall.Kill(cmd.Process.Pid, syscall.SIGKILL) 34 cmd.Process.Kill() 35 } 36 }() 37 // Require at least one command 38 if len(cmds) < 1 { 39 return nil, nil, nil 40 } 41 42 // Collect the output from the command(s) 43 var output bytes.Buffer 44 var stderr bytes.Buffer 45 46 last := len(cmds) - 1 47 for i, cmd := range cmds[:last] { 48 var err error 49 // Connect each command's stdin to the previous command's stdout 50 if cmds[i+1].Stdin, err = cmd.StdoutPipe(); err != nil { 51 //logutil.Infoln("cmd.StdoutPipe() cmd=", cmd.Path, " error", err) 52 return nil, nil, err 53 } 54 // Connect each command's stderr to a buffer 55 cmd.Stderr = &stderr 56 } 57 58 // Connect the output and error for the last command 59 cmds[last].Stdout, cmds[last].Stderr = &output, &stderr 60 61 // Start and Wait each command 62 // 2018.8.21 먼저 Start를 다 시키고 나서, Wait 실행 중 중간 cmd 에서 에러가 나면 나머지 cmd 는 좀비 프로세스로 변함 63 for _, cmd := range cmds { 64 if err := cmd.Start(); err != nil { 65 //logutil.Println("WA30200", "PipeLine Start Error, Path=", cmd.Path, ", err=", err) 66 return output.Bytes(), stderr.Bytes(), err 67 } 68 } 69 70 for _, cmd := range cmds { 71 if err := cmd.Wait(); err != nil { 72 //logutil.Println("WA30201", "PipeLine Wait Error, Path=", cmd.Path, ", err=", err) 73 return output.Bytes(), stderr.Bytes(), err 74 } 75 } 76 77 // Return the pipeline output and the collected standard error 78 return output.Bytes(), stderr.Bytes(), nil 79 } 80 81 func GetPHPInfo() map[string]string { 82 defer func() { 83 // recover 84 if r := recover(); r != nil { 85 // 86 //log.Println("recover:", r, string(debug.Stack())) 87 } 88 }() 89 m := make(map[string]string) 90 phpinfo := cmdPHPInfo() 91 phpinfo = strings.Replace(phpinfo, "\r", "", -1) 92 93 // PHP Version 94 phpVersion := stringutil.Substring(phpinfo, "PHP Version", "\n\nConfiguration\n\n") 95 s1 := strings.Split(phpVersion, "\n") 96 97 for _, tmp := range s1 { 98 k, v := stringutil.ToPair(tmp, "=>") 99 if k != "" { 100 m[k] = v 101 } 102 } 103 104 return m 105 } 106 107 func GetPHPModuleInfo() map[string]string { 108 defer func() { 109 // recover 110 if r := recover(); r != nil { 111 // 112 //log.Println("recover:", r, string(debug.Stack())) 113 } 114 }() 115 m := make(map[string]string) 116 keysList := list.New() 117 118 // pos := -1 119 // pos1 := -1 120 // mpos := -1 121 // mpos1 := -1 122 123 //php -m 124 moduleinfo := cmdPHPModuleInfo() 125 moduleinfo = strings.Replace(moduleinfo, "\r", "", -1) 126 127 phpmodules := stringutil.Substring(moduleinfo, "[PHP Modules]", "[Zend Modules]") 128 s1 := strings.Split(phpmodules, "\n") 129 // key 등록 130 for _, tmp := range s1 { 131 if strings.TrimSpace(tmp) != "" { 132 m[tmp] = "" 133 keysList.PushBack(tmp) 134 //log.Println("PHP Module key= ", tmp) 135 } 136 } 137 138 zendmodules := stringutil.Substring(moduleinfo, "[Zend Modules]", "") 139 140 s2 := strings.Split(zendmodules, "\n") 141 // key 등록 142 for _, tmp := range s2 { 143 if strings.TrimSpace(tmp) != "" { 144 m[tmp] = "" 145 keysList.PushBack(tmp) 146 //log.Println("Zend Module key= ", tmp) 147 } 148 } 149 150 mLen := keysList.Len() 151 keys := make([]string, mLen) 152 idx := 0 153 for e := keysList.Front(); e != nil; e = e.Next() { 154 //log.Println("keys=", e) 155 keys[idx] = string(e.Value.(string)) 156 idx++ 157 } 158 159 //phpI := exec.Command(php, "-i") 160 phpinfo := cmdPHPInfo() 161 phpinfo = strings.Replace(phpinfo, "\r", "", -1) 162 163 // Configuration 164 str := stringutil.Substring(phpinfo, "\n\nConfiguration\n\n", "\n\nAdditional Modules\n\n") 165 //log.Println("Configuration=", str) 166 for i := 0; i < mLen; i++ { 167 detail := "" 168 if i+1 < mLen { 169 detail = stringutil.Substring(str, "\n\n"+keys[i], "\n\n"+keys[i+1]) 170 } else { 171 detail = stringutil.Substring(str, "\n\n"+keys[i], "") 172 } 173 174 s3 := stringutil.Tokenizer(detail, "\n") 175 m[keys[i]] = strings.Join(s3, ", ") 176 } 177 178 return m 179 } 180 181 func cmdPHPInfo() string { 182 defer func() { 183 if r := recover(); r != nil { 184 } 185 }() 186 php := os.Getenv("WHATAP_PHP_BIN") 187 if strings.TrimSpace(php) != "" { 188 cmd := exec.Command(php, "-i") 189 out, err := cmd.Output() 190 191 if err != nil { 192 //error 193 //log.Println("command err", err) 194 return "" 195 } 196 197 return string(out) 198 } 199 return "" 200 } 201 202 func cmdPHPModuleInfo() string { 203 defer func() { 204 if r := recover(); r != nil { 205 } 206 }() 207 php := os.Getenv("WHATAP_PHP_BIN") 208 if strings.TrimSpace(php) != "" { 209 cmd := exec.Command(php, "-m") 210 out, err := cmd.Output() 211 212 if err != nil { 213 //error 214 //log.Println("command err", err) 215 return "" 216 } 217 return string(out) 218 } 219 return "" 220 } 221 222 func GetPstackInfo(pid int32) map[int64]string { 223 rt := make(map[int64]string, 0) 224 225 // parse , linux, freebsd 226 // nts 쓰레드 아이디 없이 스택이 1개 -1 쓰레드 아이디로 설정 227 // zts 쓰레드 아이디 228 // freebasd nts 인 경우 쓰레드 아이디가 정상(freebsd10), 쓰레드 아이디는 -1 (freebsd11), 스택은 1개. 229 //linux 230 //# pstack 1720 231 //Thread 27 (Thread 0x7f3842d3c700 (LWP 1722)): 232 //#0 0x00007f38580b568c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0 233 //#1 0x00007f38598215ed in ap_queue_pop () 234 //#2 0x00007f385981fc54 in ?? () 235 //#3 0x00007f38580b1aa1 in start_thread () from /lib64/libpthread.so.0 236 //#4 0x00007f3857dfec4d in clone () from /lib64/libc.so.6 237 //Thread 26 (Thread 0x7f384233b700 (LWP 1723)): 238 //#0 0x00007f38580b568c in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0 239 //#1 0x00007f38598215ed in ap_queue_pop () 240 //#2 0x00007f385981fc54 in ?? () 241 //#3 0x00007f38580b1aa1 in start_thread () from /lib64/libpthread.so.0 242 //#4 0x00007f3857dfec4d in clone () from /lib64/libc.so.6 243 244 //freebsd 245 // pstack -O 95874 246 //95874: /usr/local/sbin/httpd 247 //----------------- thread 100967 (running) ----------------- 248 // 0x801c5a228 __sys_flock (21539d6, 8, ffffffff, 8, 1e, 0) + 8 249 // 0x8021539d6 _init (215364c, 8, 0, 0, 0, 0) + 238e 250 // 0x80215364c _init (2152720, 8, 2881200, 8, 14, 0) + 2004 251 // 0x802152720 _init (43bdbd, 0, 28ed098, 8, 2820118, 8) + 10d8 252 // 0x43bdbd ap_run_mpm (43467f, 0, ffffed10, 7fff, ffffed30, 7fff) + 3d 253 // 0x43467f main (433ccf, 0, 433b60, 0, 0, 0) + 8bf 254 // 0x433ccf _start (6b1000, 8, 0, 0, 0, 0) + 16f 255 256 out, err := cmdPstackInfo(pid) 257 258 if err != nil { 259 //logutil.Println("WA30202", "Error GetPstackInfo ", err) 260 return nil 261 } 262 263 // DEBUG 264 //rt[int64(pid)] = string(out) 265 //logutil.Infoln("GetPstackInfo", "pid = ", pid, ", out=", string(out)) 266 r := bufio.NewScanner(strings.NewReader(string(out))) 267 tidCount := 0 268 tid := int64(0) 269 sb := stringutil.NewStringBuffer() 270 for r.Scan() { 271 line := r.Text() 272 if runtime.GOOS == "linux" { 273 if strings.HasPrefix(line, "Thread ") { 274 tidCount++ 275 // 저장. 276 if sb.ToString() != "" { 277 if tid == 0 { 278 rt[-1] = sb.ToString() 279 } else { 280 rt[tid] = sb.ToString() 281 } 282 } 283 v := stringutil.Substring(line, "(LWP", "))") 284 tid, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 64) 285 sb.Clear() 286 } else { 287 if strings.Index(line, "whatap_") < 0 { 288 //funcName := stringutil.Substring(strings.TrimSpace(line), "in ", "(") 289 funcName := line 290 if funcName != "" { 291 //if funcName != "init" { 292 sb.AppendLine(funcName) 293 //} 294 } 295 } 296 } 297 } else if runtime.GOOS == "freebsd" { 298 if strings.HasPrefix(line, "----------------- thread") { 299 tidCount++ 300 // 저장. 301 if sb.ToString() != "" { 302 if tid == 0 { 303 rt[-1] = sb.ToString() 304 } else { 305 rt[tid] = sb.ToString() 306 } 307 } 308 v := stringutil.Substring(line, "thread", "(running)") 309 //logutil.Infoln("GetPstackInfo", "thread=", v, ",line=", line) 310 tid, _ = strconv.ParseInt(strings.TrimSpace(v), 10, 64) 311 sb.Clear() 312 } else { 313 if strings.Index(line, "whatap_") < 0 { 314 //funcName := stringutil.Substring(strings.TrimSpace(line), " ", "(") 315 funcName := line 316 if funcName != "" { 317 //if funcName != "init" { 318 sb.AppendLine(funcName) 319 //} 320 } 321 } 322 } 323 } 324 } 325 326 if sb.ToString() != "" { 327 if tid == 0 { 328 // 쓰레드 없는 경우. -1 값으로 통일 329 rt[-1] = sb.ToString() 330 //logutil.Infoln("GetPstakInfo", "tail none tidr stack=", rt[-1]) 331 } else { 332 rt[tid] = sb.ToString() 333 //logutil.Infoln("GetPstakInfo", "tail tid=", tid, ",stack=", rt[tid]) 334 335 } 336 } 337 338 return rt 339 } 340 341 func cmdPstackInfo(pid int32) (out []byte, err error) { 342 if runtime.GOOS == "linux" { 343 cmd := exec.Command("pstack", fmt.Sprintf("%d", int(pid))) 344 out, err = cmd.Output() 345 } else if runtime.GOOS == "freebsd" { 346 cmd := exec.Command("pstack", "-O", fmt.Sprintf("%d", int(pid))) 347 out, err = cmd.Output() 348 } 349 return out, err 350 } 351 352 // Get docker full id from /proc/self/cgroup 353 func GetDockerFullId() string { 354 355 defer func() { 356 if r := recover(); r != nil { 357 } 358 }() 359 360 // check exists /proc/self/cgroup 361 if _, err := os.Stat("/proc/self/cgroup"); os.IsNotExist(err) { 362 // path/to/whatever does not exist 363 return "" 364 } 365 //cat /proc/self/cgroup | head -n 1 | cut -d '/' -f3 366 c1 := exec.Command("cat", "/proc/self/cgroup") 367 c2 := exec.Command("head", "-n", "1") 368 c3 := exec.Command("cut", "-d", "/", "-f3") 369 370 // Run the pipeline 371 out, _, err := Pipeline(c1, c2, c3) 372 if err != nil { 373 //logutil.Println("WA30203", "GetDockerFullId Error : errors : ", err) 374 return "" 375 } 376 return strings.TrimSuffix(string(out), "\n") 377 } 378 379 func GetLinuxProductUUID() string { 380 if runtime.GOOS == "linux" { 381 cmd := exec.Command("cat", "/sys/class/dmi/id/product_uuid") 382 out, err := cmd.Output() 383 if err == nil { 384 return string(out) 385 } 386 } 387 return "" 388 } 389 390 func CMDMain() { 391 c1 := exec.Command("ps", "aux") 392 c2 := exec.Command("grep", "httpd") 393 c3 := exec.Command("awk", "{print $3}") 394 c4 := exec.Command("awk", "{total = total + $1} END {print total}") 395 396 // Run the pipeline 397 //output, stderr, err := Pipeline(c1, c2, c3, c4) 398 output, _, err := Pipeline(c1, c2, c3, c4) 399 if err != nil { 400 //logutil.Printf("Error : %s", err) 401 } 402 403 // Print the stdout, if any 404 if len(output) > 0 { 405 //logutil.Printf("output %s", output) 406 407 } 408 }