1-- SPDX-License-Identifier: BSD-2-Clause 2-- 3-- Copyright(c) 2022 Baptiste Daroussin <bapt@FreeBSD.org> 4 5local pu = require("posix.unistd") 6 7local function warnmsg(str) 8 io.stderr:write(str.."\n") 9end 10 11local function errmsg(str) 12 io.stderr:write(str.."\n") 13 os.exit(1) 14end 15 16local 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 25end 26 27local 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) 36end 37 38local 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() 54end 55 56local 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 68end 69 70local 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 136end 137 138local 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 167end 168 169local 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 201end 202 203local 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 214return n 215