]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libexec/nuageinit/nuage.lua
nuageinit: add basic support for cloudinit.
[FreeBSD/FreeBSD.git] / libexec / nuageinit / nuage.lua
1 -- SPDX-License-Identifier: BSD-2-Clause
2 --
3 -- Copyright(c) 2022 Baptiste Daroussin <bapt@FreeBSD.org>
4
5 local pu = require("posix.unistd")
6
7 local function warnmsg(str)
8         io.stderr:write(str.."\n")
9 end
10
11 local function errmsg(str)
12         io.stderr:write(str.."\n")
13         os.exit(1)
14 end
15
16 local function dirname(oldpath)
17         if not oldpath then
18                 return nil
19         end
20         local path = oldpath:gsub("[^/]+/*$", "")
21         if path == "" then
22                 return nil
23         end
24         return path
25 end
26
27 local function mkdir_p(path)
28         if lfs.attributes(path, "mode") ~= nil then
29                 return true
30         end
31         local r,err = mkdir_p(dirname(path))
32         if not r then
33                 return nil,err.." (creating "..path..")"
34         end
35         return lfs.mkdir(path)
36 end
37
38 local function sethostname(hostname)
39         if hostname == nil then return end
40         local root = os.getenv("NUAGE_FAKE_ROOTDIR")
41         if not root then
42                 root = ""
43         end
44         local hostnamepath = root .. "/etc/rc.conf.d/hostname"
45
46         mkdir_p(dirname(hostnamepath))
47         local f,err = io.open(hostnamepath, "w")
48         if not f then
49                 warnmsg("Impossible to open "..hostnamepath .. ":" ..err)
50                 return
51         end
52         f:write("hostname=\""..hostname.."\"\n")
53         f:close()
54 end
55
56 local function splitlist(list)
57         local ret = {}
58         if type(list) == "string" then
59                 for str in list:gmatch("([^, ]+)") do
60                         ret[#ret + 1] = str
61                 end
62         elseif type(list) == "table" then
63                 ret = list
64         else
65                 warnmsg("Invalid type ".. type(list) ..", expecting table or string")
66         end
67         return ret
68 end
69
70 local function adduser(pwd)
71         if (type(pwd) ~= "table") then
72                 warnmsg("Argument should be a table")
73                 return nil
74         end
75         local f = io.popen("getent passwd "..pwd.name)
76         local pwdstr = f:read("*a")
77         f:close()
78         if pwdstr:len() ~= 0 then
79                 return pwdstr:match("%a+:.+:%d+:%d+:.*:(.*):.*")
80         end
81         if not pwd.gecos then
82                 pwd.gecos = pwd.name .. " User"
83         end
84         if not pwd.home then
85                 pwd.home = "/home/" .. pwd.name
86         end
87         local extraargs=""
88         if pwd.groups then
89                 local list = splitlist(pwd.groups)
90                 extraargs = " -G ".. table.concat(list, ',')
91         end
92         -- pw will automatically create a group named after the username
93         -- do not add a -g option in this case
94         if pwd.primary_group and pwd.primary_group ~= pwd.name then
95                 extraargs = extraargs .. " -g " .. pwd.primary_group
96         end
97         if not pwd.no_create_home then
98                 extraargs = extraargs .. " -m "
99         end
100         if not pwd.shell then
101                 pwd.shell = "/bin/sh"
102         end
103         local precmd = ""
104         local postcmd = ""
105         if pwd.passwd then
106                 precmd = "echo "..pwd.passwd .. "| "
107                 postcmd = " -H 0 "
108         elseif pwd.plain_text_passwd then
109                 precmd = "echo "..pwd.plain_text_passwd .. "| "
110                 postcmd = " -H 0 "
111         end
112         local root = os.getenv("NUAGE_FAKE_ROOTDIR")
113         local cmd = precmd .. "pw "
114         if root then
115                 cmd = cmd .. "-R " .. root .. " "
116         end
117         cmd = cmd .. "useradd -n ".. pwd.name .. " -M 0755 -w none "
118         cmd = cmd .. extraargs .. " -c '".. pwd.gecos
119         cmd = cmd .. "' -d '" .. pwd.home .. "' -s "..pwd.shell .. postcmd
120
121         local r = os.execute(cmd)
122         if not r then
123                 warnmsg("nuageinit: fail to add user "..pwd.name);
124                 warnmsg(cmd)
125                 return nil
126         end
127         if pwd.locked then
128                 cmd = "pw "
129                 if root then
130                         cmd = cmd .. "-R " .. root .. " "
131                 end
132                 cmd = cmd .. "lock " .. pwd.name
133                 os.execute(cmd)
134         end
135         return pwd.home
136 end
137
138 local function addgroup(grp)
139         if (type(grp) ~= "table") then
140                 warnmsg("Argument should be a table")
141                 return false
142         end
143         local f = io.popen("getent group "..grp.name)
144         local grpstr = f:read("*a")
145         f:close()
146         if grpstr:len() ~= 0 then
147                 return true
148         end
149         local extraargs = ""
150         if grp.members then
151                 local list = splitlist(grp.members)
152                 extraargs = " -M " .. table.concat(list, ',')
153         end
154         local root = os.getenv("NUAGE_FAKE_ROOTDIR")
155         local cmd = "pw "
156         if root then
157                 cmd = cmd .. "-R " .. root .. " "
158         end
159         cmd = cmd .. "groupadd -n ".. grp.name .. extraargs
160         local r = os.execute(cmd)
161         if not r then
162                 warnmsg("nuageinit: fail to add group ".. grp.name);
163                 warnmsg(cmd)
164                 return false
165         end
166         return true
167 end
168
169 local function addsshkey(homedir, key)
170         local chownak = false
171         local chowndotssh = false
172         local ak_path = homedir .. "/.ssh/authorized_keys"
173         local dotssh_path = homedir .. "/.ssh"
174         local dirattrs = lfs.attributes(ak_path)
175         if dirattrs == nil then
176                 chownak = true
177                 dirattrs = lfs.attributes(dotssh_path)
178                 if dirattrs == nil then
179                         if not lfs.mkdir(dotssh_path) then
180                                 warnmsg("nuageinit: impossible to create ".. dotssh_path)
181                                 return
182                         end
183                         chowndotssh = true
184                         dirattrs = lfs.attributes(homedir)
185                 end
186         end
187
188         local f = io.open(ak_path, "a")
189         if not f then
190                 warnmsg("nuageinit: impossible to open "..ak_path)
191                 return
192         end
193         f:write(key .. "\n")
194         f:close()
195         if chownak then
196                 pu.chown(ak_path, dirattrs.uid, dirattrs.gid)
197         end
198         if chowndotssh then
199                 pu.chown(dotssh_path, dirattrs.uid, dirattrs.gid)
200         end
201 end
202
203 local n = {
204         warn = warnmsg,
205         err = errmsg,
206         sethostname = sethostname,
207         adduser = adduser,
208         addgroup = addgroup,
209         addsshkey = addsshkey,
210         dirname = dirname,
211         mkdir_p = mkdir_p,
212 }
213
214 return n