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  }