package utils import ( "crypto/sha256" "encoding/base64" "fmt" "os" "os/exec" "regexp" "slices" "strconv" "strings" "sync" "time" ) const ( NIS_YPCAT = "ypcat" SYS_PASSWD = "/etc/passwd" ) // LinuxSysUser linux系统用户信息 type LinuxSysUser struct { Name string Home string Shell string Uid int Gid int SSHkeyInfo map[string]SSHPubKeyInfo } func GetOneSysUser(name string) (*LinuxSysUser, error) { content, err := os.ReadFile(SYS_PASSWD) if err != nil { return nil, err } lines := strings.Split(strings.Trim(string(content), "\n"), "\n") for _, line := range lines { if strings.HasPrefix(line, name) { fields := strings.Split(line, ":") if len(fields) != 7 || fields[0] != name { continue } uid, err := strconv.Atoi(fields[2]) if err != nil { continue } if uid < 999 && uid != 0 { continue } u := LinuxSysUser{} u.Uid = uid gid, err := strconv.Atoi(fields[3]) if err != nil { continue } u.Gid = gid u.Name = fields[0] u.Shell = fields[6] u.Home = fields[5] s, err := ParseSSHAuthKey(u.Home + "/.ssh/authorized_keys") if err == nil { u.SSHkeyInfo = s } return &u, nil } } return nil, nil } func GetOneNisUser(name string) (*LinuxSysUser, error) { ypcat := FindCmd(NIS_YPCAT) if ypcat == nil { return nil, nil } content, err := exec.Command(NIS_YPCAT, "passwd").Output() if err != nil { return nil, err } lines := strings.Split(strings.Trim(string(content), "\n"), "\n") for _, line := range lines { if strings.HasPrefix(line, name) { fields := strings.Split(line, ":") if len(fields) != 7 || fields[0] != name { continue } uid, err := strconv.Atoi(fields[2]) if err != nil { continue } if uid < 999 && uid != 0 { continue } u := LinuxSysUser{} u.Uid = uid gid, err := strconv.Atoi(fields[3]) if err != nil { continue } u.Gid = gid u.Name = fields[0] u.Shell = fields[6] u.Home = fields[5] s, err := ParseSSHAuthKey(u.Home + "/.ssh/authorized_keys") if err == nil { u.SSHkeyInfo = s } return &u, nil } } return nil, nil } // GetSysUser 获取系统用户信息 func GetSysUser() (map[int]LinuxSysUser, error) { content, err := os.ReadFile(SYS_PASSWD) if err != nil { return nil, err } r := parsePasswd(string(content)) return r, nil } // GetNisUser 获取Nis用户信息 func GetNisUser() (map[int]LinuxSysUser, error) { ypcat := FindCmd(NIS_YPCAT) if ypcat == nil { return make(map[int]LinuxSysUser), nil } output, err := exec.Command(NIS_YPCAT, "passwd").Output() if err != nil { return nil, err } r := parsePasswd(string(output)) return r, nil } // GetClusUser 获取Clusconf用户信息 func GetClusUser() { // todo } type SSHPubKeyInfo struct { Type string Content string // base64格式的公钥 UserInfo string Sha256 string // 公钥经过sha256后,再base64,如果结尾是=,删除= } // ParseSSHAuthKey 解析ssh认证文件的内容 func ParseSSHAuthKey(path string) (map[string]SSHPubKeyInfo, error) { content, err := os.ReadFile(path) if err != nil { return nil, err } pub := string(content) lines := strings.Split(strings.Trim(pub, "\n"), "\n") result := make(map[string]SSHPubKeyInfo) for _, line := range lines { if len(line) == 0 { continue } fields := strings.Fields(line) if len(fields) != 3 { continue } if !strings.HasPrefix(fields[0], "ssh-") { continue } item := SSHPubKeyInfo{} item.Type, _ = strings.CutPrefix(fields[0], "ssh-") item.Content = fields[1] item.UserInfo = fields[2] key, err := base64.StdEncoding.DecodeString(item.Content) if err != nil { continue } b := sha256.Sum256(key) item.Sha256 = strings.Trim(base64.StdEncoding.EncodeToString(b[:]), "=") result[item.Sha256] = item } return result, nil } func parsePasswd(str string) map[int]LinuxSysUser { result := make(map[int]LinuxSysUser) pwd := strings.Trim(string(str), "\n") lines := strings.Split(pwd, "\n") for _, line := range lines { fields := strings.Split(line, ":") if len(fields) != 7 { continue } uid, err := strconv.Atoi(fields[2]) if err != nil { continue } if uid < 999 && uid != 0 { continue } u := LinuxSysUser{} u.Uid = uid gid, err := strconv.Atoi(fields[3]) if err != nil { continue } u.Gid = gid u.Name = fields[0] u.Shell = fields[6] u.Home = fields[5] s, err := ParseSSHAuthKey(u.Home + "/.ssh/authorized_keys") if err == nil { u.SSHkeyInfo = s } result[u.Uid] = u } return result } type OnlineUser struct { Name string `json:"name"` Type string `json:"type"` When time.Time `json:"loginTime"` Pid int `json:"pid"` LoginFrom string `json:"loginForm"` } func (ou OnlineUser) String() string { return fmt.Sprintf("name:%s type:%s when:%s pids:%v login from:%s", ou.Name, ou.Type, ou.When.Format("2006-01-02 15:04"), ou.Pid, ou.LoginFrom) } func (ou OnlineUser) Sha256sum() [32]byte { return sha256.Sum256([]byte(ou.String())) } var ( ReOnLineUser = regexp.MustCompile(`^(?i)([a-zA-Z_0-9]*)\s+([a-zA-Z0-9/]*)\s+(\d{4}-\d{1,2}-\d{1,2} \d{2}:\d{2})\s+(?:old|\.|\d{2}:\d{2})\s+(\d+)\s+\((.*)\)$`) // sshd远程登录的 ReOnLineUserTTY = regexp.MustCompile(`^(?i)([a-zA-Z_0-9]*)\s+(tty[0-9]*)\s+(\d{4}-\d{1,2}-\d{1,2} \d{2}:\d{2})\s+(?:old|\.|\d{2}:\d{2})\s+(\d*(?:,\d*)*)$`) // 通过控制台登录的 ReOnLineUserX = regexp.MustCompile(`^(?i)([a-zA-Z_0-9]*)\s+(:[0-9]*)\s+(\d{4}-\d{1,2}-\d{1,2} \d{2}:\d{2})\s+\?\s+(\d*(?:,\d*)*).*$`) // 通过图像界面 ) // GetOnlineUser func GetOnlineUser() ([]OnlineUser, error) { output, err := exec.Command("who", "-u").Output() if err != nil { return nil, err } lines := strings.Split(strings.Trim(string(output), "\n"), "\n") result := make([]OnlineUser, 0, 16) for _, line := range lines { if ReOnLineUser.MatchString(line) { m := ReOnLineUser.FindStringSubmatch(line) if len(m) != 6 { continue } u := OnlineUser{} u.Name = m[1] u.Type = m[2] t, err := time.Parse("2006-01-02 15:04", m[3]) if err != nil { return nil, err } u.When = t u.LoginFrom = m[5] p, err := strconv.Atoi(m[4]) if err != nil { return nil, err } u.Pid = p result = append(result, u) } } return result, nil } var ( ReNum = regexp.MustCompile(`^\d+$`) ) func GetPidChild(pid int) ([]int, error) { items, err := os.ReadDir("/proc") if err != nil { return nil, err } result := make([]int, 0, 8) for _, item := range items { if !item.IsDir() { continue } if !ReNum.MatchString(item.Name()) { continue } ppid, err := getPPID(item.Name()) if err != nil { continue } if ppid == pid { i, err := strconv.Atoi(item.Name()) if err == nil { result = append(result, i) } } } slices.Sort(result) return result, nil } // GetPidChild2 并发获取进程的子进程,并发数为8 func GetPidChild2(pid int) ([]int, error) { items, err := os.ReadDir("/proc") if err != nil { return nil, err } result := make([]int, 0, 16) resultLock := sync.Mutex{} // 指定并发数为8 goroutineNum := 8 wg := sync.WaitGroup{} wg.Add(goroutineNum) // 创建管道,并为每个管道添加一个处理goroutine cs := make([]chan string, 0, goroutineNum) for range goroutineNum { c := make(chan string, 128) cs = append(cs, c) go func(sc <-chan string, ppid int, wg *sync.WaitGroup) { for pid := range sc { if pid == "0" { break } p, err := getPPID(pid) if err == nil && p == ppid { pp, err := strconv.Atoi(pid) if err == nil { resultLock.Lock() result = append(result, pp) resultLock.Unlock() } } } wg.Done() }(c, pid, &wg) } // 向goroutine分发任务 for i, item := range items { cs[i%goroutineNum] <- item.Name() } // 向goroutine发送关闭信号 for _, c := range cs { c <- "0" } // 等待goroutine关闭 wg.Wait() // 关闭管道 for _, c := range cs { close(c) } slices.Sort(result) return result, nil } func getPPID(pid string) (int, error) { path := fmt.Sprintf("/proc/%s/status", pid) content, err := os.ReadFile(path) if err != nil { return 0, err } lines := strings.Split(string(content), "\n") for _, line := range lines { if !strings.HasPrefix(line, "PPid:") { continue } fields := strings.Fields(line) if len(fields) != 2 { continue } return strconv.Atoi(fields[1]) } return 0, fmt.Errorf("error not find PPid in %s", path) }