func (u *User) GroupList() []string {
	var result []string
	for _, group := range u.Groups {
		result = append(result, group.Name)
	}
	return result
}
GroupList returns slice with user groups names
package system
import (
	"errors"
	"os"
	"os/exec"
	"sort"
	"strconv"
	"strings"
	"syscall"
	"time"
	"pkg.re/essentialkaos/ek.v12/env"
	"pkg.re/essentialkaos/ek.v12/strutil"
)
type User struct {
	UID      int      `json:"uid"`
	GID      int      `json:"gid"`
	Name     string   `json:"name"`
	Groups   []*Group `json:"groups"`
	Comment  string   `json:"comment"`
	Shell    string   `json:"shell"`
	HomeDir  string   `json:"home_dir"`
	RealUID  int      `json:"real_uid"`
	RealGID  int      `json:"real_gid"`
	RealName string   `json:"real_name"`
}
type Group struct {
	Name string `json:"name"`
	GID  int    `json:"gid"`
}
type SessionInfo struct {
	User             *User     `json:"user"`
	LoginTime        time.Time `json:"login_time"`
	LastActivityTime time.Time `json:"last_activity_time"`
}
type sessionsInfo []*SessionInfo
func (s sessionsInfo) Len() int {
	return len(s)
}
func (s sessionsInfo) Less(i, j int) bool {
	return s[i].LoginTime.Unix() < s[j].LoginTime.Unix()
}
func (s sessionsInfo) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
var (
	ErrEmptyPath = errors.New("Path is empty")
	ErrEmptyUserName = errors.New("User name/ID can't be blank")
	ErrEmptyGroupName = errors.New("Group name/ID can't be blank")
	ErrCantParseIdOutput = errors.New("Can't parse id command output")
	ErrCantParseGetentOutput = errors.New("Can't parse getent command output")
)
var CurrentUserCachePeriod = 5 * time.Minute
var curUser *User
var curUserUpdateDate time.Time
var ptsDir = "/dev/pts"
func Who() ([]*SessionInfo, error) {
	var result []*SessionInfo
	ptsList := readDir(ptsDir)
	if len(ptsList) == 0 {
		return result, nil
	}
	for _, file := range ptsList {
		if file == "ptmx" {
			continue
		}
		info, err := getSessionInfo(file)
		if err != nil {
			continue
		}
		result = append(result, info)
	}
	if len(result) != 0 {
		sort.Sort(sessionsInfo(result))
	}
	return result, nil
}
func CurrentUser(avoidCache ...bool) (*User, error) {
	if len(avoidCache) == 0 || avoidCache[0] == false {
		if curUser != nil && time.Since(curUserUpdateDate) < CurrentUserCachePeriod {
			return curUser, nil
		}
	}
	username, err := getCurrentUserName()
	if err != nil {
		return nil, err
	}
	user, err := LookupUser(username)
	if err != nil {
		return user, err
	}
	if user.Name == "root" {
		appendRealUserInfo(user)
	}
	curUser = user
	curUserUpdateDate = time.Now()
	return user, nil
}
func LookupUser(nameOrID string) (*User, error) {
	if nameOrID == "" {
		return nil, ErrEmptyUserName
	}
	user, err := getUserInfo(nameOrID)
	if err != nil {
		return nil, err
	}
	err = appendGroupInfo(user)
	if err != nil {
		return nil, err
	}
	return user, nil
}
func LookupGroup(nameOrID string) (*Group, error) {
	if nameOrID == "" {
		return nil, ErrEmptyGroupName
	}
	return getGroupInfo(nameOrID)
}
func CurrentTTY() string {
	pid := strconv.Itoa(os.Getpid())
	fdLink, err := os.Readlink("/proc/" + pid + "/fd/0")
	if err != nil {
		return ""
	}
	return fdLink
}
func (u *User) IsRoot() bool {
	return u.UID == 0 && u.GID == 0
}
func (u *User) IsSudo() bool {
	return u.IsRoot() && u.RealUID != 0 && u.RealGID != 0
}