sysuser.go 2.99 KB
Newer Older
liming6's avatar
liming6 committed
1
2
3
package utils

import (
liming6's avatar
liming6 committed
4
	"fmt"
liming6's avatar
liming6 committed
5
6
7
8
	"os"
	"os/exec"
	"strconv"
	"strings"
liming6's avatar
liming6 committed
9
	"sync"
liming6's avatar
liming6 committed
10
11
12
13
14
15
16
17
18
19
20
)

/*
获取Linux系统上所有用户的信息,如果系统安装了nis,还要解析nis的用户
*/

const (
	NISClient = "ypbind"
	NISCat    = "ypcat"
)

liming6's avatar
liming6 committed
21
22
var SysUserInfo = sync.Map{}

liming6's avatar
liming6 committed
23
24
25
// detectNis 探测系统是否为Nis的客户端,即解析是否存在ypbind命令,且ypbind命令在运行
func detectNis() (bool, error) {
	haveCmd, _ := DetectCmd(NISClient)
26
	if !haveCmd {
liming6's avatar
liming6 committed
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
		return false, nil
	}
	ps, err := GetProcessByName(NISClient)
	if err != nil {
		return false, err
	}
	if len(ps) > 0 {
		return true, nil
	}
	return false, nil
}

// GetNisUsers 获取Nis中的所有用户信息
func GetNisUsers() ([]SysUser, error) {
	haveCmd, cmdPath := DetectCmd(NISCat)
	if !haveCmd {
		return make([]SysUser, 0), nil
	}
	output, err := exec.Command(cmdPath, "passwd").Output()
	if err != nil {
		return nil, err
	}
	return parseSysUser(string(output))
}

type SysUser struct {
	Name         string
	Uid          int
	Gid          int
	IsSystemUser bool
	HomeDir      string
	Shell        string
	GECOS        string // 用户信息
}

// parseSysUser 从字符串中解析系统用户信息
func parseSysUser(str string) ([]SysUser, error) {
	sysUsers := make([]SysUser, 0)
	lines := strings.Split(strings.Trim(str, "\n"), "\n")
	if len(lines) == 0 {
		return sysUsers, nil
	}
	for _, line := range lines {
		fields := strings.Split(line, ":")
		if len(fields) != 7 {
			continue
		}
		user := SysUser{}
		user.Name = strings.TrimSpace(fields[0])
		uid, err := strconv.Atoi(strings.TrimSpace(fields[2]))
		if err != nil {
			return nil, err
		}
		user.Uid = uid
		if user.Uid >= 1000 {
			user.IsSystemUser = false
		} else {
			user.IsSystemUser = true
		}
		gid, err := strconv.Atoi(strings.TrimSpace(fields[3]))
		if err != nil {
			return nil, err
		}
		user.Gid = gid
		user.GECOS = fields[4]
		user.HomeDir = strings.TrimSpace(fields[5])
		user.Shell = strings.TrimSpace(fields[6])
		sysUsers = append(sysUsers, user)
	}
	return sysUsers, nil
}

// GetSysUsers 获取系统上所有的用户
liming6's avatar
liming6 committed
100
func GetSysUsers() error {
liming6's avatar
liming6 committed
101
102
	detect, err := detectNis()
	if err != nil {
liming6's avatar
liming6 committed
103
		return err
liming6's avatar
liming6 committed
104
105
106
107
108
109
110
111
	}
	result := make([]SysUser, 0)
	if detect {
		u, _ := GetNisUsers()
		result = append(result, u...)
	}
	u, err := os.ReadFile("/etc/passwd")
	if err != nil {
liming6's avatar
liming6 committed
112
		return nil
liming6's avatar
liming6 committed
113
114
115
	}
	su, err := parseSysUser(string(u))
	if err != nil {
liming6's avatar
liming6 committed
116
		return nil
liming6's avatar
liming6 committed
117
118
	}
	result = append(result, su...)
liming6's avatar
liming6 committed
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
	for _, user := range result {
		SysUserInfo.Store(user.Uid, &user)
	}
	return nil
}

func GetSysUserById(uid int) (*SysUser, error) {
	u, ok := SysUserInfo.Load(uid)
	if !ok {
		err := GetSysUsers()
		if err != nil {
			return nil, err
		}
		uu, o := SysUserInfo.Load(uid)
		if o && uu != nil {
			sysu, so := uu.(*SysUser)
			if so {
				return sysu, nil
			} else {
				return nil, fmt.Errorf("error loading sysuser, type error")
			}
		} else {
			return nil, nil
		}
	} else {
		sysu, o := u.(*SysUser)
		if !o {
			return nil, fmt.Errorf("error loading sysuser, type error")
		}
		return sysu, nil
	}
liming6's avatar
liming6 committed
150
}