]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/idtools/usergroupadd_linux.go
provider: Ensured Go 1.11 in TravisCI and README
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / fsouza / go-dockerclient / external / github.com / docker / docker / pkg / idtools / usergroupadd_linux.go
1 package idtools
2
3 import (
4 "fmt"
5 "os/exec"
6 "path/filepath"
7 "strings"
8 "syscall"
9 )
10
11 // add a user and/or group to Linux /etc/passwd, /etc/group using standard
12 // Linux distribution commands:
13 // adduser --uid <id> --shell /bin/login --no-create-home --disabled-login --ingroup <groupname> <username>
14 // useradd -M -u <id> -s /bin/nologin -N -g <groupname> <username>
15 // addgroup --gid <id> <groupname>
16 // groupadd -g <id> <groupname>
17
18 const baseUID int = 10000
19 const baseGID int = 10000
20 const idMAX int = 65534
21
22 var (
23 userCommand string
24 groupCommand string
25
26 cmdTemplates = map[string]string{
27 "adduser": "--uid %d --shell /bin/false --no-create-home --disabled-login --ingroup %s %s",
28 "useradd": "-M -u %d -s /bin/false -N -g %s %s",
29 "addgroup": "--gid %d %s",
30 "groupadd": "-g %d %s",
31 }
32 )
33
34 func init() {
35 // set up which commands are used for adding users/groups dependent on distro
36 if _, err := resolveBinary("adduser"); err == nil {
37 userCommand = "adduser"
38 } else if _, err := resolveBinary("useradd"); err == nil {
39 userCommand = "useradd"
40 }
41 if _, err := resolveBinary("addgroup"); err == nil {
42 groupCommand = "addgroup"
43 } else if _, err := resolveBinary("groupadd"); err == nil {
44 groupCommand = "groupadd"
45 }
46 }
47
48 func resolveBinary(binname string) (string, error) {
49 binaryPath, err := exec.LookPath(binname)
50 if err != nil {
51 return "", err
52 }
53 resolvedPath, err := filepath.EvalSymlinks(binaryPath)
54 if err != nil {
55 return "", err
56 }
57 //only return no error if the final resolved binary basename
58 //matches what was searched for
59 if filepath.Base(resolvedPath) == binname {
60 return resolvedPath, nil
61 }
62 return "", fmt.Errorf("Binary %q does not resolve to a binary of that name in $PATH (%q)", binname, resolvedPath)
63 }
64
65 // AddNamespaceRangesUser takes a name and finds an unused uid, gid pair
66 // and calls the appropriate helper function to add the group and then
67 // the user to the group in /etc/group and /etc/passwd respectively.
68 // This new user's /etc/sub{uid,gid} ranges will be used for user namespace
69 // mapping ranges in containers.
70 func AddNamespaceRangesUser(name string) (int, int, error) {
71 // Find unused uid, gid pair
72 uid, err := findUnusedUID(baseUID)
73 if err != nil {
74 return -1, -1, fmt.Errorf("Unable to find unused UID: %v", err)
75 }
76 gid, err := findUnusedGID(baseGID)
77 if err != nil {
78 return -1, -1, fmt.Errorf("Unable to find unused GID: %v", err)
79 }
80
81 // First add the group that we will use
82 if err := addGroup(name, gid); err != nil {
83 return -1, -1, fmt.Errorf("Error adding group %q: %v", name, err)
84 }
85 // Add the user as a member of the group
86 if err := addUser(name, uid, name); err != nil {
87 return -1, -1, fmt.Errorf("Error adding user %q: %v", name, err)
88 }
89 return uid, gid, nil
90 }
91
92 func addUser(userName string, uid int, groupName string) error {
93
94 if userCommand == "" {
95 return fmt.Errorf("Cannot add user; no useradd/adduser binary found")
96 }
97 args := fmt.Sprintf(cmdTemplates[userCommand], uid, groupName, userName)
98 return execAddCmd(userCommand, args)
99 }
100
101 func addGroup(groupName string, gid int) error {
102
103 if groupCommand == "" {
104 return fmt.Errorf("Cannot add group; no groupadd/addgroup binary found")
105 }
106 args := fmt.Sprintf(cmdTemplates[groupCommand], gid, groupName)
107 // only error out if the error isn't that the group already exists
108 // if the group exists then our needs are already met
109 if err := execAddCmd(groupCommand, args); err != nil && !strings.Contains(err.Error(), "already exists") {
110 return err
111 }
112 return nil
113 }
114
115 func execAddCmd(cmd, args string) error {
116 execCmd := exec.Command(cmd, strings.Split(args, " ")...)
117 out, err := execCmd.CombinedOutput()
118 if err != nil {
119 return fmt.Errorf("Failed to add user/group with error: %v; output: %q", err, string(out))
120 }
121 return nil
122 }
123
124 func findUnusedUID(startUID int) (int, error) {
125 return findUnused("passwd", startUID)
126 }
127
128 func findUnusedGID(startGID int) (int, error) {
129 return findUnused("group", startGID)
130 }
131
132 func findUnused(file string, id int) (int, error) {
133 for {
134 cmdStr := fmt.Sprintf("cat /etc/%s | cut -d: -f3 | grep '^%d$'", file, id)
135 cmd := exec.Command("sh", "-c", cmdStr)
136 if err := cmd.Run(); err != nil {
137 // if a non-zero return code occurs, then we know the ID was not found
138 // and is usable
139 if exiterr, ok := err.(*exec.ExitError); ok {
140 // The program has exited with an exit code != 0
141 if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
142 if status.ExitStatus() == 1 {
143 //no match, we can use this ID
144 return id, nil
145 }
146 }
147 }
148 return -1, fmt.Errorf("Error looking in /etc/%s for unused ID: %v", file, err)
149 }
150 id++
151 if id > idMAX {
152 return -1, fmt.Errorf("Maximum id in %q reached with finding unused numeric ID", file)
153 }
154 }
155 }