// UnitFileStatuses returns a list of all unit files with their current status,
// as shown by `systemctl list-unit-files`.
func UnitFileStatuses() (units, statuses []string, err error) {
	cmd := exec.Command("systemctl", "--no-pager", "list-unit-files")
	out, err := cmd.CombinedOutput()
	if err != nil {
		err2 := errors.New(err.Error() + ": output: " + string(out))
		return units, statuses, err2
	}
	table := tabular.ProbabalisticSplit(string(out))
	units = tabular.GetColumnNoHeader(0, table)
	// last two are empty line and junk statistics we don't care about
	if len(units) <= 3 {
		msg := fmt.Sprint(cmd.Args) + " didn't output enough lines"
		return units, statuses, errors.New(msg)
	}
	cmd = exec.Command("systemctl", "--no-pager", "list-unit-files")
	table = tabular.ProbabalisticSplit(string(out))
	statuses = tabular.GetColumnNoHeader(1, table)
	// last two are empty line and junk statistics we don't care about
	if len(statuses) <= 3 {
		msg := fmt.Sprint(cmd.Args) + " didn't output enough lines"
		return units, statuses, errors.New(msg)
	}
	return units[:len(units)-2], statuses[:len(statuses)-2], nil

}
Example #2
0
// how many inodes are in this given state? state can be one of:
// total, used, free, percent
func inodesInState(filesystem, state string) (total uint64, err error) {
	cmd := exec.Command("df", "-i")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return total, err
	}
	table := tabular.ProbabalisticSplit(string(out))
	filesystems := tabular.GetColumnByHeader("Filesystem", table)
	var totals []string
	switch state {
	case "total":
		totals = tabular.GetColumnByHeader("Inodes", table)
	case "free":
		totals = tabular.GetColumnByHeader("IFree", table)
	case "used":
		totals = tabular.GetColumnByHeader("IUsed", table)
	case "percent":
		totals = tabular.GetColumnByHeader("IUse%", table)
	default:
		formatStr := "Internal error: unexpected state in inodesInState: %v"
		return total, fmt.Errorf(formatStr, state)
	}
	if len(filesystems) != len(totals) {
		formatStr := "The number of filesystems (%d) didn't match the number of"
		formatStr += " inode totals (%d)!"
		return total, fmt.Errorf(formatStr, len(filesystems), len(totals))
	}
	for i := range totals {
		if filesystems[i] == filesystem {
			// trim the % in case the state was "percent"
			return strconv.ParseUint(strings.TrimSuffix(totals[i], "%"), 10, 64)
		}
	}
	return total, fmt.Errorf("Couldn't find that filesystem: " + filesystem)
}
// ListeningSockets returns a list of all sockets in the "LISTENING" state
func ListeningSockets() (socks []string, err error) {
	out, err := exec.Command("systemctl", "list-sockets").CombinedOutput()
	if err != nil {
		return socks, errors.New(err.Error() + ": output: " + string(out))
	}
	table := tabular.ProbabalisticSplit(string(out))
	return tabular.GetColumnByHeader("LISTENING", table), nil
}
Example #4
0
// DockerImageRepositories returns a slice of the names of the Docker images
// present on the host (what's under the REPOSITORIES column of `docker images`)
func DockerImageRepositories() (images []string, err error) {
	cmd := exec.Command("docker", "images")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return images, err
	}
	table := tabular.ProbabalisticSplit(string(out))
	return tabular.GetColumnByHeader("REPOSITORIES", table), nil
}
Example #5
0
// returns a column of the routing table as a slice of strings
func routingTableColumn(name string) []string {
	cmd := exec.Command("route", "-n")
	out := wrkutils.CommandOutput(cmd)
	table := tabular.ProbabalisticSplit(out)
	if len(table) < 1 {
		log.WithFields(log.Fields{
			"column": name,
			"table":  "\n" + tabular.ToString(table),
		}).Fatal("Routing table was not available or not properly parsed")
	}
	finalTable := table[1:] // has extra line before headers
	return tabular.GetColumnByHeader(name, finalTable)
}
// DockerImageRepositories returns a slice of the names of the Docker images
// present on the host (what's under the REPOSITORIES column of `docker images`)
func DockerImageRepositories() (images []string, err error) {
	cmd := exec.Command("docker", "images")
	out, err := cmd.CombinedOutput()
	if err != nil {
		// try escalating to sudo, the error might have been one of permissions
		cmd = exec.Command("sudo", "docker", "images")
		out, err = cmd.CombinedOutput()
		if err != nil {
			return images, err
		}
	}
	table := tabular.ProbabalisticSplit(string(out))
	return tabular.GetColumnByHeader("REPOSITORY", table), nil
}
Example #7
0
// getYumRepos constructs Repos from the yum.conf file at path. Gives non-zero
// Names, Fullnames, and URLs.
func getYumRepos() (repos []repo) {
	// safeAccess allows access w/o fear of a panic into a slice of strings
	safeAccess := func(slc []string, index int) string {
		// catch runtime panic
		defer func() {
			if err := recover(); err != nil {
				msg := "safeAccess: Please report this error"
				errutil.IndexError(msg, index, slc)
			}
		}() // invoke inside defer
		if len(slc) > index {
			return slc[index]
		}
		return ""
	}

	// get and parse output of `yum repolist`
	cmd := exec.Command("yum", "repolist")
	outstr := chkutil.CommandOutput(cmd)
	// handle empty case
	if strings.Contains(outstr, "repolist: 0") {
		return repos
	}
	slc := tabular.ProbabalisticSplit(outstr)

	ids := tabular.GetColumnNoHeader(0, slc) // TODO use columnbyheader here
	errutil.IndexError("getYumRepos", 2, ids)
	ids = ids[:len(ids)-2]

	names := tabular.GetColumnNoHeader(1, slc)    // TODO and here
	statuses := tabular.GetColumnNoHeader(2, slc) // TODO and here
	if len(ids) != len(names) || len(names) != len(statuses) {
		log.WithFields(log.Fields{
			"names":    len(names),
			"ids":      len(ids),
			"statuses": len(statuses),
		}).Warn("Could not fetch complete metadata for every repo.")
	}
	// Construct repos
	for i := range ids {
		name := safeAccess(names, i)
		id := safeAccess(ids, i)
		status := safeAccess(statuses, i)
		repo := repo{Name: name, ID: id, Status: status}
		repos = append(repos, repo)
	}
	return repos
}
Example #8
0
// RunningContainers returns a list of names of running docker containers
// (what's under the IMAGE column of `docker ps -a` if it has status "Up".
func RunningContainers() (containers []string, err error) {
	cmd := exec.Command("docker", "ps", "-a")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return containers, err
	}
	// the output of `docker ps -a` has spaces in columns, but each column
	// is separated by 2 or more spaces. Just what Probabalistic was made for!
	lines := tabular.ProbabalisticSplit(string(out))
	names := tabular.GetColumnByHeader("IMAGE", lines)
	statuses := tabular.GetColumnByHeader("STATUS", lines)
	for i, status := range statuses {
		// index error caught by second condition in if clause
		if strings.Contains(status, "Up") && len(names) > i {
			containers = append(containers, names[i])
		}
	}
	return containers, nil
}
Example #9
0
// getAptrepos constructs repos from the sources.list file at path. Gives
// non-zero URLs
func getAptRepos() (repos []repo) {
	// getAptSources returns all the urls of all apt sources (including source
	// code repositories
	getAptSources := func() (urls []string) {
		otherLists := chkutil.GetFilesWithExtension("/etc/apt/sources.list.d", ".list")
		sourceLists := append([]string{"/etc/apt/sources.list"}, otherLists...)
		for _, f := range sourceLists {
			split := tabular.ProbabalisticSplit(chkutil.FileToString(f))
			// filter out comments
			commentRegex := regexp.MustCompile(`^\s*#`)
			for _, line := range split {
				if len(line) > 1 && !(commentRegex.MatchString(line[0])) {
					urls = append(urls, line[1])
				}
			}
		}
		return urls
	}
	for _, src := range getAptSources() {
		repos = append(repos, repo{URL: src})
	}
	return repos
}
Example #10
0
// getSwapOrMemory returns output from `free`, it is an abstraction of
// getSwap and getMemory. inputs: status: free | used | total
// swapOrMem: memory | swap, units: b | kb | mb | gb | tb
// TODO: support kib/gib style units, with proper transformations.
func getSwapOrMemory(status string, swapOrMem string, units string) int {
	statusToColumn := map[string]int{
		"total": 1,
		"used":  2,
		"free":  3,
	}
	unitsToFlag := map[string]string{
		"b":  "--bytes",
		"kb": "--kilo",
		"mb": "--mega",
		"gb": "--giga",
		"tb": "--tera",
	}
	typeToRow := map[string]int{
		"memory": 0,
		"swap":   1,
	}
	// check to see that our keys are really in our dict
	if _, ok := statusToColumn[status]; !ok {
		log.WithFields(log.Fields{
			"status":   status,
			"expected": []string{"total", "used", "free"},
		}).Fatal("Internal error: invalid status in getSwapOrMemory")
	} else if _, ok := unitsToFlag[units]; !ok {
		log.WithFields(log.Fields{
			"units":    units,
			"expected": []string{"b", "kb", "mb", "gb", "tb"},
		}).Fatal("Internal error: invalid units in getSwapOrMemory")
	} else if _, ok := typeToRow[swapOrMem]; !ok {
		log.WithFields(log.Fields{
			"option":   swapOrMem,
			"expected": []string{"memory", "swap"},
		}).Fatal("Internal error: invalid option in getSwapOrMemory")
	}
	// execute free and return the appropriate output
	cmd := exec.Command("free", unitsToFlag[units])
	outStr := wrkutils.CommandOutput(cmd)
	table := tabular.ProbabalisticSplit(outStr)
	column := tabular.GetColumnByHeader(status, table)
	row := typeToRow[swapOrMem]
	// check for errors in output of `free`
	if column == nil {
		log.WithFields(log.Fields{
			"header": status,
			"table":  "\n" + tabular.ToString(table),
		}).Fatal("Free column was empty")
	}
	if row >= len(column) {
		log.WithFields(log.Fields{
			"output": outStr,
			"column": column,
			"row":    row,
		}).Fatal("`free` didn't output enough rows")
	}
	toReturn, err := strconv.ParseInt(column[row], 10, 64)
	if err != nil {
		log.WithFields(log.Fields{
			"cell":   column[row],
			"error":  err.Error(),
			"output": outStr,
		}).Fatal("Couldn't parse output of `free` as an int")
	}
	return int(toReturn)
}
Example #11
0
// port parses /proc/net/tcp to determine if a given port is in an open state
// and returns an error if it is not.
func port(parameters []string) (exitCode int, exitMessage string) {
	// getHexPorts gets all open ports as hex strings from /proc/net/tcp
	getHexPorts := func() (ports []string) {
		paths := [2]string{"/proc/net/tcp", "/proc/net/udp"}
		for _, path := range paths {
			data := wrkutils.FileToString(path)
			table := tabular.ProbabalisticSplit(data)
			// TODO by header isn't working
			//localAddresses := tabular.GetColumnByHeader("local_address", table)
			localAddresses := tabular.GetAllNoHeader(table)
			portRe := regexp.MustCompile(`([0-9A-F]{8}):([0-9A-F]{4})`)
			for _, address := range localAddresses {
				port := portRe.FindString(address)
				if port != "" {
					if len(port) < 10 {
						log.WithFields(log.Fields{
							"port":   port,
							"length": len(port),
						}).Fatal("Couldn't parse port number in " + path)
					}
					portString := string(port[9:])
					ports = append(ports, portString)
				}
			}
		}
		return ports
	}

	// strHexToDecimal converts from string containing hex number to int
	strHexToDecimal := func(hex string) int {
		portInt, err := strconv.ParseInt(hex, 16, 64)
		if err != nil {
			log.WithFields(log.Fields{
				"number": hex,
				"error":  err.Error(),
			}).Fatal("Couldn't parse hex number")
		}
		return int(portInt)
	}

	// getOpenPorts gets a list of open/listening ports as integers
	getOpenPorts := func() (ports []int) {
		for _, port := range getHexPorts() {
			ports = append(ports, strHexToDecimal(port))
		}
		return ports
	}

	// TODO check if it is in a valid range
	port := wrkutils.ParseMyInt(parameters[0])
	open := getOpenPorts()
	for _, p := range open {
		if p == port {
			return 0, ""
		}
	}
	// convert ports to string to send to wrkutils.GenericError
	var strPorts []string
	for _, port := range open {
		strPorts = append(strPorts, fmt.Sprint(port))
	}
	return wrkutils.GenericError("Port not open", fmt.Sprint(port), strPorts)
}