github.com/ubuntu/ubuntu-report@v1.7.4-0.20240410144652-96f37d845fac/internal/metrics/cmd.go (about)

     1  package metrics
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"io/ioutil"
     7  	"os/exec"
     8  	"strings"
     9  
    10  	"github.com/pkg/errors"
    11  	log "github.com/sirupsen/logrus"
    12  	"github.com/ubuntu/ubuntu-report/internal/utils"
    13  )
    14  
    15  func (m Metrics) getGPU() []gpuInfo {
    16  	var gpus []gpuInfo
    17  
    18  	r := runCmd(m.gpuInfoCmd)
    19  
    20  	results, err := filterAll(r, `^.* 0300: ([a-zA-Z0-9]+:[a-zA-Z0-9]+)( \(rev .*\))?$`)
    21  	if err != nil {
    22  		log.Infof("couldn't get GPU info: "+utils.ErrFormat, err)
    23  		return nil
    24  	}
    25  
    26  	for _, gpuinfo := range results {
    27  		i := strings.SplitN(gpuinfo, ":", 2)
    28  		if len(i) != 2 {
    29  			log.Infof("GPU info should of form vendor:model, got: %s", gpuinfo)
    30  			continue
    31  		}
    32  		gpus = append(gpus, gpuInfo{Vendor: i[0], Model: i[1]})
    33  	}
    34  
    35  	return gpus
    36  }
    37  
    38  func (m Metrics) getCPU() cpuInfo {
    39  	c := cpuInfo{}
    40  
    41  	r := runCmd(m.cpuInfoCmd)
    42  
    43  	for result := range filter(r, `{"field": *"(.*)", *"data": *"(.*)"},`, true) {
    44  		if result.err != nil {
    45  			log.Infof("Couldn't get CPU info: "+utils.ErrFormat, result.err)
    46  			return cpuInfo{}
    47  		}
    48  
    49  		key, v := result.r[0], result.r[1]
    50  
    51  		switch strings.TrimSpace(key) {
    52  		case "CPU op-mode(s):":
    53  			c.OpMode = v
    54  		case "CPU(s):":
    55  			c.CPUs = v
    56  		case "Thread(s) per core:":
    57  			c.Threads = v
    58  		case "Core(s) per socket:":
    59  			c.Cores = v
    60  		case "Socket(s):":
    61  			c.Sockets = v
    62  		case "Vendor ID:":
    63  			c.Vendor = v
    64  		case "CPU family:":
    65  			c.Family = v
    66  		case "Model:":
    67  			c.Model = v
    68  		case "Stepping:":
    69  			c.Stepping = v
    70  		case "Model name:":
    71  			c.Name = v
    72  		case "Virtualization:":
    73  			c.Virtualization = v
    74  		case "Hypervisor vendor:":
    75  			c.Hypervisor = v
    76  		case "Virtualization type:":
    77  			c.VirtualizationType = v
    78  		}
    79  	}
    80  
    81  	return c
    82  }
    83  
    84  func (m Metrics) getScreens() []screenInfo {
    85  	var screens []screenInfo
    86  
    87  	r := runCmd(m.screenInfoCmd)
    88  
    89  	var results []string
    90  	results, err := filterAll(r, `^(?: +(.*)\*|.* connected .* (\d+mm x \d+mm))`)
    91  	if err != nil {
    92  		log.Infof("couldn't get Screen info: "+utils.ErrFormat, err)
    93  		return nil
    94  	}
    95  
    96  	var lastSize string
    97  	for _, screeninfo := range results {
    98  		if strings.Index(screeninfo, "mm") > -1 {
    99  			lastSize = strings.Replace(screeninfo, " ", "", -1)
   100  			continue
   101  		}
   102  		i := strings.Fields(screeninfo)
   103  		if len(i) < 2 {
   104  			log.Infof("screen info should be either a screen physical size (connected) or a a resolution + freq, got: %s", screeninfo)
   105  			continue
   106  		}
   107  		if lastSize == "" {
   108  			log.Infof("We couldn't get physical info size prior to Resolution and Frequency information.")
   109  			continue
   110  		}
   111  		screens = append(screens, screenInfo{Size: lastSize, Resolution: i[0], Frequency: i[len(i)-1]})
   112  	}
   113  
   114  	return screens
   115  }
   116  
   117  func (m Metrics) getPartitions() []float64 {
   118  	var sizes []float64
   119  
   120  	r := runCmd(m.spaceInfoCmd)
   121  
   122  	results, err := filterAll(r, `^/dev/([^\s]+ +[^\s]*).*$`)
   123  	if err != nil {
   124  		log.Infof("couldn't get Disk info: "+utils.ErrFormat, err)
   125  		return nil
   126  	}
   127  
   128  	for _, size := range results {
   129  		// negative lookahead isn't supported in go, so exclude loop devices manually
   130  		if strings.HasPrefix(size, "loop") {
   131  			continue
   132  		}
   133  		s := strings.Fields(size)
   134  		if len(s) != 2 {
   135  			log.Infof("partition size should be of form 'block device      size', got: %s", size)
   136  			continue
   137  		}
   138  		v, err := convKBToGB(s[1])
   139  		if err != nil {
   140  			log.Infof("partition size should be an integer: "+utils.ErrFormat, err)
   141  			continue
   142  		}
   143  		sizes = append(sizes, v)
   144  	}
   145  
   146  	return sizes
   147  }
   148  
   149  func (m Metrics) getArch() string {
   150  	b, err := m.archCmd.CombinedOutput()
   151  	if err != nil {
   152  		log.Infof("couldn't get Architecture: "+utils.ErrFormat, err)
   153  		return ""
   154  	}
   155  
   156  	return strings.TrimSpace(string(b))
   157  }
   158  
   159  func (m Metrics) getHwCap() string {
   160  	if m.hwCapCmd == nil {
   161  		// if no data return empty string. This is caused by an
   162  		// unsupported architecture or older version of glibc
   163  		return ""
   164  	}
   165  
   166  	rSupported := runCmd(m.hwCapCmd)
   167  
   168  	// check if there is any hwcap output
   169  	bytesSupported, err := ioutil.ReadAll(rSupported)
   170  	if err != nil {
   171  		log.Infof("Couldn't get hwcap: "+utils.ErrFormat, err)
   172  		return ""
   173  	}
   174  
   175  	hwCapBytes := []byte("Subdirectories of glibc-hwcaps")
   176  	hwCapIndex := bytes.Index(bytesSupported, hwCapBytes)
   177  	if hwCapIndex < 0 {
   178  		// no glibc-hwcaps, return empty string
   179  		return ""
   180  	}
   181  
   182  	// remove the legacy hwcap section, as we don't want to report those
   183  	legacyBytes := []byte("Legacy HWCAP subdirectories")
   184  
   185  	legacyIndex := bytes.Index(bytesSupported, legacyBytes)
   186  	if legacyIndex >= 0 {
   187  		bytesSupported = bytesSupported[0:legacyIndex]
   188  	}
   189  
   190  	// convert back to io.Reader for the filter functions
   191  	newSupported := bytes.NewReader(bytesSupported)
   192  
   193  	// now find which version is supported
   194  	resultSupported, err := filterFirst(newSupported, `^(?:(.*) +.*supported, searched.*)`, false)
   195  	if err != nil {
   196  		log.Infof("No supported hwcap: "+utils.ErrFormat, err)
   197  		return "-"
   198  	}
   199  
   200  	return resultSupported
   201  }
   202  
   203  func runCmd(cmd *exec.Cmd) io.Reader {
   204  	pr, pw := io.Pipe()
   205  	cmd.Stdout = pw
   206  
   207  	go func() {
   208  		err := cmd.Run()
   209  		if err != nil {
   210  			pw.CloseWithError(errors.Wrapf(err, "'%s' return an error", cmd.Args))
   211  			return
   212  		}
   213  		pw.Close()
   214  	}()
   215  	return pr
   216  }