1-- Prosody IM 2-- Copyright (C) 2008-2010 Matthew Wild 3-- Copyright (C) 2008-2010 Waqas Hussain 4-- 5-- This project is MIT/X11 licensed. Please see the 6-- COPYING file in the source package for more information. 7-- 8 9 10local config = require "core.configmanager"; 11local encodings = require "util.encodings"; 12local stringprep = encodings.stringprep; 13local storagemanager = require "core.storagemanager"; 14local usermanager = require "core.usermanager"; 15local signal = require "util.signal"; 16local set = require "util.set"; 17local lfs = require "lfs"; 18local pcall = pcall; 19local type = type; 20 21local nodeprep, nameprep = stringprep.nodeprep, stringprep.nameprep; 22 23local io, os = io, os; 24local print = print; 25local tonumber = tonumber; 26 27local _G = _G; 28local prosody = prosody; 29 30-- UI helpers 31local function show_message(msg, ...) 32 print(msg:format(...)); 33end 34 35local function show_usage(usage, desc) 36 print("Usage: ".._G.arg[0].." "..usage); 37 if desc then 38 print(" "..desc); 39 end 40end 41 42local function getchar(n) 43 local stty_ret = os.execute("stty raw -echo 2>/dev/null"); 44 local ok, char; 45 if stty_ret == true or stty_ret == 0 then 46 ok, char = pcall(io.read, n or 1); 47 os.execute("stty sane"); 48 else 49 ok, char = pcall(io.read, "*l"); 50 if ok then 51 char = char:sub(1, n or 1); 52 end 53 end 54 if ok then 55 return char; 56 end 57end 58 59local function getline() 60 local ok, line = pcall(io.read, "*l"); 61 if ok then 62 return line; 63 end 64end 65 66local function getpass() 67 local stty_ret, _, status_code = os.execute("stty -echo 2>/dev/null"); 68 if status_code then -- COMPAT w/ Lua 5.1 69 stty_ret = status_code; 70 end 71 if stty_ret ~= 0 then 72 io.write("\027[08m"); -- ANSI 'hidden' text attribute 73 end 74 local ok, pass = pcall(io.read, "*l"); 75 if stty_ret == 0 then 76 os.execute("stty sane"); 77 else 78 io.write("\027[00m"); 79 end 80 io.write("\n"); 81 if ok then 82 return pass; 83 end 84end 85 86local function show_yesno(prompt) 87 io.write(prompt, " "); 88 local choice = getchar():lower(); 89 io.write("\n"); 90 if not choice:match("%a") then 91 choice = prompt:match("%[.-(%U).-%]$"); 92 if not choice then return nil; end 93 end 94 return (choice == "y"); 95end 96 97local function read_password() 98 local password; 99 while true do 100 io.write("Enter new password: "); 101 password = getpass(); 102 if not password then 103 show_message("No password - cancelled"); 104 return; 105 end 106 io.write("Retype new password: "); 107 if getpass() ~= password then 108 if not show_yesno [=[Passwords did not match, try again? [Y/n]]=] then 109 return; 110 end 111 else 112 break; 113 end 114 end 115 return password; 116end 117 118local function show_prompt(prompt) 119 io.write(prompt, " "); 120 local line = getline(); 121 line = line and line:gsub("\n$",""); 122 return (line and #line > 0) and line or nil; 123end 124 125-- Server control 126local function adduser(params) 127 local user, host, password = nodeprep(params.user), nameprep(params.host), params.password; 128 if not user then 129 return false, "invalid-username"; 130 elseif not host then 131 return false, "invalid-hostname"; 132 end 133 134 local host_session = prosody.hosts[host]; 135 if not host_session then 136 return false, "no-such-host"; 137 end 138 139 storagemanager.initialize_host(host); 140 local provider = host_session.users; 141 if not(provider) or provider.name == "null" then 142 usermanager.initialize_host(host); 143 end 144 145 local ok, errmsg = usermanager.create_user(user, password, host); 146 if not ok then 147 return false, errmsg or "creating-user-failed"; 148 end 149 return true; 150end 151 152local function user_exists(params) 153 local user, host = nodeprep(params.user), nameprep(params.host); 154 155 storagemanager.initialize_host(host); 156 local provider = prosody.hosts[host].users; 157 if not(provider) or provider.name == "null" then 158 usermanager.initialize_host(host); 159 end 160 161 return usermanager.user_exists(user, host); 162end 163 164local function passwd(params) 165 if not user_exists(params) then 166 return false, "no-such-user"; 167 end 168 169 return adduser(params); 170end 171 172local function deluser(params) 173 if not user_exists(params) then 174 return false, "no-such-user"; 175 end 176 local user, host = nodeprep(params.user), nameprep(params.host); 177 178 return usermanager.delete_user(user, host); 179end 180 181local function getpid() 182 local pidfile = config.get("*", "pidfile"); 183 if not pidfile then 184 return false, "no-pidfile"; 185 end 186 187 if type(pidfile) ~= "string" then 188 return false, "invalid-pidfile"; 189 end 190 191 pidfile = config.resolve_relative_path(prosody.paths.data, pidfile); 192 193 local modules_disabled = set.new(config.get("*", "modules_disabled")); 194 if prosody.platform ~= "posix" or modules_disabled:contains("posix") then 195 return false, "no-posix"; 196 end 197 198 local file, err = io.open(pidfile, "r+"); 199 if not file then 200 return false, "pidfile-read-failed", err; 201 end 202 203 local locked, err = lfs.lock(file, "w"); 204 if locked then 205 file:close(); 206 return false, "pidfile-not-locked"; 207 end 208 209 local pid = tonumber(file:read("*a")); 210 file:close(); 211 212 if not pid then 213 return false, "invalid-pid"; 214 end 215 216 return true, pid; 217end 218 219local function isrunning() 220 local ok, pid, err = getpid(); 221 if not ok then 222 if pid == "pidfile-read-failed" or pid == "pidfile-not-locked" then 223 -- Report as not running, since we can't open the pidfile 224 -- (it probably doesn't exist) 225 return true, false; 226 end 227 return ok, pid; 228 end 229 return true, signal.kill(pid, 0) == 0; 230end 231 232local function start(source_dir) 233 local ok, ret = isrunning(); 234 if not ok then 235 return ok, ret; 236 end 237 if ret then 238 return false, "already-running"; 239 end 240 if not source_dir then 241 os.execute("./prosody -D"); 242 else 243 os.execute(source_dir.."/../../bin/prosody -D"); 244 end 245 return true; 246end 247 248local function stop() 249 local ok, ret = isrunning(); 250 if not ok then 251 return ok, ret; 252 end 253 if not ret then 254 return false, "not-running"; 255 end 256 257 local ok, pid = getpid() 258 if not ok then return false, pid; end 259 260 signal.kill(pid, signal.SIGTERM); 261 return true; 262end 263 264local function reload() 265 local ok, ret = isrunning(); 266 if not ok then 267 return ok, ret; 268 end 269 if not ret then 270 return false, "not-running"; 271 end 272 273 local ok, pid = getpid() 274 if not ok then return false, pid; end 275 276 signal.kill(pid, signal.SIGHUP); 277 return true; 278end 279 280return { 281 show_message = show_message; 282 show_warning = show_message; 283 show_usage = show_usage; 284 getchar = getchar; 285 getline = getline; 286 getpass = getpass; 287 show_yesno = show_yesno; 288 read_password = read_password; 289 show_prompt = show_prompt; 290 adduser = adduser; 291 user_exists = user_exists; 292 passwd = passwd; 293 deluser = deluser; 294 getpid = getpid; 295 isrunning = isrunning; 296 start = start; 297 stop = stop; 298 reload = reload; 299}; 300