Skip to content

Commit

Permalink
Merge pull request #65 from essentialkaos/develop
Browse files Browse the repository at this point in the history
Version 3.3.0
  • Loading branch information
andyone authored Aug 20, 2016
2 parents e6b69c5 + 0a1b854 commit cb19ce9
Show file tree
Hide file tree
Showing 8 changed files with 449 additions and 5 deletions.
5 changes: 5 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## Changelog

#### v3.3.0

* `[system/process]` Added new package for getting information about active system processes
* `[terminal]` Fixed bug with title formating in `ReadAnswer` function

#### v3.2.3

* `[terminal]` Fixed bug with title formating in `ReadUI` function
Expand Down
60 changes: 60 additions & 0 deletions color/examples_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package color

// ////////////////////////////////////////////////////////////////////////////////// //
// //
// Copyright (c) 2009-2016 Essential Kaos //
// Essential Kaos Open Source License <http://essentialkaos.com/ekol?en> //
// //
// ////////////////////////////////////////////////////////////////////////////////// //

import (
"fmt"
)

// ////////////////////////////////////////////////////////////////////////////////// //

func ExampleRGB2Hex() {
fmt.Printf("%x\n", RGB2Hex(127, 25, 75))
// Output: 7f194b
}

func ExampleHex2RGB() {
r, g, b := Hex2RGB(0x7f194b)

fmt.Printf("r:%d g:%d b:%d\n", r, g, b)

// Output: r:127 g:25 b:75
}

func ExampleHex2RGBA() {
r, g, b, a := Hex2RGBA(0x7f194bcc)

fmt.Printf("r:%d g:%d b:%d a:%d\n", r, g, b, a)

// Output: r:127 g:25 b:75 a:204
}

func ExampleRGB2HSB() {
h, s, v := RGB2HSB(127, 25, 75)

fmt.Printf("h:%d s:%d v:%d\n", h, s, v)

// Output: h:331 s:81 v:50
}

func ExampleHSB2RGB() {
r, g, b := HSB2RGB(331, 81, 50)

fmt.Printf("r:%d g:%d b:%d\n", r, g, b)

// Output: r:128 g:24 b:74
}

func ExampleIsRGBA() {
fmt.Println(IsRGBA(0xAABBCC))
fmt.Println(IsRGBA(0xAABBCCDD))

// Output:
// false
// true
}
5 changes: 3 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Auxiliary packages for Go.

### Platform support

Currently we support Linux and Mac OS X. Some packages have stubs for Windows (for autocomplete).
Currently we support Linux and Mac OS X. Some packages have stubs for Windows (_for autocomplete_).

### Installation

Expand Down Expand Up @@ -49,6 +49,7 @@ go get -u pkg.re/essentialkaos/ek.v3
* [`spellcheck`](https://godoc.org/pkg.re/essentialkaos/ek.v3/spellcheck) - Package provides spellcheck based on Damerau–Levenshtein distance algorithm
* [`strutil`](https://godoc.org/pkg.re/essentialkaos/ek.v3/strutil) - Package provides utils for working with strings
* [`system`](https://godoc.org/pkg.re/essentialkaos/ek.v3/system) - Package provides methods for working with system data (metrics/users)
* [`system/process`](https://godoc.org/pkg.re/essentialkaos/ek.v3/system/process) - Package provides methods for getting information about active processes
* [`terminal`](https://godoc.org/pkg.re/essentialkaos/ek.v3/terminal) - Package provides methods for working with user input
* [`timeutil`](https://godoc.org/pkg.re/essentialkaos/ek.v3/timeutil) - Package with time utils
* [`tmp`](https://godoc.org/pkg.re/essentialkaos/ek.v3/tmp) - Package provides methods for working with temporary data
Expand All @@ -57,7 +58,7 @@ go get -u pkg.re/essentialkaos/ek.v3

### Projects with EK

* [ssllabs-client](https://github.com/essentialkaos/ssllabs_client) - Pretty awesome command-line client for public SSLLabs API
* [sslcli](https://github.com/essentialkaos/sslcli) - Pretty awesome command-line client for public SSLLabs API
* [redis-cli-monitor](https://github.com/essentialkaos/redis-cli-monitor) - Tiny redis client for renamed MONITOR commands
* [shdoc](https://github.com/essentialkaos/shdoc) - Tool for viewing and exporting docs for shell scripts
* [rbinstall](https://github.com/essentialkaos/rbinstall) - Utility for installing prebuilt ruby to RBEnv
Expand Down
28 changes: 28 additions & 0 deletions system/process/examples_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package process

// ////////////////////////////////////////////////////////////////////////////////// //
// //
// Copyright (c) 2009-2016 Essential Kaos //
// Essential Kaos Open Source License <http://essentialkaos.com/ekol?en> //
// //
// ////////////////////////////////////////////////////////////////////////////////// //

func Example_getTree() {
process, err := GetTree()

if err != nil {
return
}

// process is top process in tree
}

func Example_getList() {
processes, err := GetList()

if err != nil {
return
}

// processes is slice with info about all active processes
}
262 changes: 262 additions & 0 deletions system/process/processes_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
// +build linux

// Package process provides methods for getting information about active processes
package process

// ////////////////////////////////////////////////////////////////////////////////// //
// //
// Copyright (c) 2009-2016 Essential Kaos //
// Essential Kaos Open Source License <http://essentialkaos.com/ekol?en> //
// //
// ////////////////////////////////////////////////////////////////////////////////// //

import (
"errors"
"io/ioutil"
"strconv"
"strings"

"pkg.re/essentialkaos/ek.v3/fsutil"
"pkg.re/essentialkaos/ek.v3/system"
)

// ////////////////////////////////////////////////////////////////////////////////// //

// ProcessInfo contains basic info about process
type ProcessInfo struct {
Command string // Full command
User string // Username
PID int // PID
IsThread bool // True if process is thread
Parent int // Parent process PID
Childs []*ProcessInfo // Slice with child processes
}

// ////////////////////////////////////////////////////////////////////////////////// //

// GetTree return root process with all subprocesses on system
func GetTree() (*ProcessInfo, error) {
list, err := findInfo("/proc", make(map[int]string))

if err != nil {
return nil, err
}

if len(list) == 0 {
return nil, errors.New("Can't find any processes")
}

processMap := processListToMap(list)

for _, process := range processMap {
if process.Parent < 0 {
continue
}

parentProcess := processMap[process.Parent]

if parentProcess == nil {
continue
}

parentProcess.Childs = append(parentProcess.Childs, process)
}

return processMap[1], nil
}

// GetList return slice with all active processes on system
func GetList() ([]*ProcessInfo, error) {
return findInfo("/proc", make(map[int]string))
}

// ////////////////////////////////////////////////////////////////////////////////// //

func findInfo(dir string, userMap map[int]string) ([]*ProcessInfo, error) {
var result []*ProcessInfo

dirs := fsutil.List(dir, true, &fsutil.ListingFilter{Perms: "DRX"})

for _, pidDir := range dirs {
if !isPID(pidDir) {
continue
}

taskDir := dir + "/" + pidDir + "/task"

if fsutil.IsExist(taskDir) {
threads, err := findInfo(taskDir, userMap)

if err != nil {
return nil, err
}

if len(threads) == 0 {
continue
}

processThreads(threads)

result = append(result, threads...)

continue
}

info, err := readProcessInfo(dir+"/"+pidDir, pidDir, userMap)

if err != nil {
return nil, err
}

if info == nil {
continue
}

result = append(result, info)
}

return result, nil
}

func readProcessInfo(dir, pid string, userMap map[int]string) (*ProcessInfo, error) {
cmd, err := ioutil.ReadFile(dir + "/cmdline")

if len(cmd) == 0 {
return nil, nil
}

if err != nil {
return nil, err
}

uid, _, err := fsutil.GetOwner(dir)

if err != nil {
return nil, err
}

username, err := getProcessUser(uid, userMap)

if err != nil {
return nil, err
}

pidInt, err := strconv.Atoi(pid)

if err != nil {
return nil, err
}

return &ProcessInfo{
Command: formatCommand(string(cmd)),
User: username,
PID: pidInt,
Parent: getProcessParent(dir, pidInt),
}, nil
}

func getProcessUser(uid int, userMap map[int]string) (string, error) {
if uid == 0 {
return "root", nil
}

if userMap[uid] != "" {
return userMap[uid], nil
}

user, err := system.LookupUser(strconv.Itoa(uid))

if err != nil {
return "", err
}

userMap[uid] = user.Name

return user.Name, nil
}

func processThreads(threads []*ProcessInfo) {
for _, info := range threads {
info.IsThread = true
}
}

func getProcessParent(pidDir string, pid int) int {
tgid, ppid := getParentPIDs(pidDir)

if tgid != pid {
return tgid
}

return ppid
}

func getParentPIDs(pidDir string) (int, int) {
data, err := ioutil.ReadFile(pidDir + "/status")

if err != nil {
return -1, -1
}

var (
ppid string
tgid string
)

for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "Tgid:") {
tgid = strings.TrimSpace(line[5:])
}

if strings.HasPrefix(line, "PPid:") {
ppid = strings.TrimSpace(line[5:])
}

if ppid != "" && tgid != "" {
break
}
}

if tgid == "" || ppid == "" {
return -1, -1
}

tgidInt, _ := strconv.Atoi(tgid)
ppidInt, _ := strconv.Atoi(ppid)

return tgidInt, ppidInt
}

func formatCommand(cmd string) string {
// Normalize delemiters
command := strings.Replace(cmd, "\000", " ", -1)

// Remove space on the end of command
command = strings.TrimSpace(command)

return command
}

func processListToMap(processes []*ProcessInfo) map[int]*ProcessInfo {
var result = make(map[int]*ProcessInfo)

for _, info := range processes {
result[info.PID] = info
}

return result
}

func isPID(pid string) bool {
if pid == "" {
return false
}

// Pid must start from number
switch pid[0] {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return true
}

return false
}
Loading

0 comments on commit cb19ce9

Please sign in to comment.