diff options
-rw-r--r-- | plugins/mod_console.lua | 58 | ||||
-rwxr-xr-x | prosody | 15 | ||||
-rwxr-xr-x | prosodyctl | 389 | ||||
-rw-r--r-- | util/prosodyctl.lua | 113 |
4 files changed, 569 insertions, 6 deletions
diff --git a/plugins/mod_console.lua b/plugins/mod_console.lua index 502e0d06..feff195e 100644 --- a/plugins/mod_console.lua +++ b/plugins/mod_console.lua @@ -194,6 +194,64 @@ end function def_env.hosts:add(name) end +def_env.s2s = {}; +function def_env.s2s:show() + local _print = self.session.print; + local print = self.session.print; + for host, host_session in pairs(hosts) do + print = function (...) _print(host); _print(...); print = _print; end + for remotehost, session in pairs(host_session.s2sout) do + print(" "..host.." -> "..remotehost); + if session.sendq then + print(" There are "..#session.sendq.." queued outgoing stanzas for this connection"); + end + if session.type == "s2sout_unauthed" then + if session.connecting then + print(" Connection not yet established"); + if not session.srv_hosts then + if not session.conn then + print(" We do not yet have a DNS answer for this host's SRV records"); + else + print(" This host has no SRV records, using A record instead"); + end + elseif session.srv_choice then + print(" We are on SRV record "..session.srv_choice.." of "..#session.srv_hosts); + local srv_choice = session.srv_hosts[session.srv_choice]; + print(" Using "..(srv_choice.target or ".")..":"..(srv_choice.port or 5269)); + end + elseif session.notopen then + print(" The <stream> has not yet been opened"); + elseif not session.dialback_key then + print(" Dialback has not been initiated yet"); + elseif session.dialback_key then + print(" Dialback has been requested, but no result received"); + end + end + end + + for session in pairs(incoming_s2s) do + if session.to_host == host then + print(" "..host.." <- "..(session.from_host or "(unknown)")); + if session.type == "s2sin_unauthed" then + print(" Connection not yet authenticated"); + end + for name in pairs(session.hosts) do + if name ~= session.from_host then + print(" also hosts "..tostring(name)); + end + end + end + end + print = _print; + end + for session in pairs(incoming_s2s) do + if not session.to_host then + print("Other incoming s2s connections"); + print(" (unknown) <- "..(session.from_host or "(unknown)")); + end + end +end + ------------- function printbanner(session) @@ -149,12 +149,8 @@ net_activate_ports("c2s", "xmppclient", {5222}, (global_ssl_ctx and "tls") or "t net_activate_ports("s2s", "xmppserver", {5269}, "tcp"); net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl"); -if config.get("*", "core", "console_enabled") then - if cl.get("console") then - cl.start("console", { interface = config.get("*", "core", "console_interface") or "127.0.0.1" }) - else - log("error", "Console is enabled, but the console module appears not to be loaded"); - end +if cl.get("console") then + cl.start("console", { interface = config.get("*", "core", "console_interface") or "127.0.0.1" }) end -- Global function to initiate prosody shutdown @@ -199,6 +195,7 @@ while select(2, xpcall(server.loop, catch_uncaught_error)) ~= "quitting" do socket.sleep(0.2); end +log("info", "Shutdown status: Cleaning up"); eventmanager.fire_event("server-cleanup"); -- Ok, we're quitting I know, but we @@ -206,6 +203,7 @@ eventmanager.fire_event("server-cleanup"); server.setquitting(false); for hostname, host in pairs(hosts) do + log("info", "Shutdown status: Closing client connections for %s", hostname) if host.sessions then for username, user in pairs(host.sessions) do for resource, session in pairs(user.sessions) do @@ -215,6 +213,7 @@ for hostname, host in pairs(hosts) do end end + log("info", "Shutdown status: Closing outgoing s2s connections from %s", hostname); if host.s2sout then for remotehost, session in pairs(host.s2sout) do if session.close then @@ -226,6 +225,10 @@ for hostname, host in pairs(hosts) do end end +log("info", "Shutdown status: Closing all server connections"); server.closeall(); +server.setquitting(true); + eventmanager.fire_event("server-stopped"); +log("info", "Shutdown status: Complete!"); diff --git a/prosodyctl b/prosodyctl new file mode 100755 index 00000000..db607833 --- /dev/null +++ b/prosodyctl @@ -0,0 +1,389 @@ +#!/usr/bin/env lua +-- Prosody IM v0.4 +-- Copyright (C) 2008-2009 Matthew Wild +-- Copyright (C) 2008-2009 Waqas Hussain +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +-- prosodyctl - command-line controller for Prosody XMPP server + +-- Will be modified by configure script if run -- + +CFG_SOURCEDIR=nil; +CFG_CONFIGDIR=os.getenv("PROSODY_CFGDIR"); +CFG_PLUGINDIR=nil; +CFG_DATADIR=os.getenv("PROSODY_DATADIR"); + +-- -- -- -- -- -- -- ---- -- -- -- -- -- -- -- -- + +if CFG_SOURCEDIR then + package.path = CFG_SOURCEDIR.."/?.lua;"..package.path + package.cpath = CFG_SOURCEDIR.."/?.so;"..package.cpath +end + +if CFG_DATADIR then + if os.getenv("HOME") then + CFG_DATADIR = CFG_DATADIR:gsub("^~", os.getenv("HOME")); + end +end + +-- Required to be able to find packages installed with luarocks +pcall(require, "luarocks.require") + + +config = require "core.configmanager" + +do + -- TODO: Check for other formats when we add support for them + -- Use lfs? Make a new conf/ dir? + local ok, level, err = config.load((CFG_CONFIGDIR or ".").."/prosody.cfg.lua"); + if not ok then + print("\n"); + print("**************************"); + if level == "parser" then + print("A problem occured while reading the config file "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua"); + local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)"); + print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err))); + print(""); + elseif level == "file" then + print("Prosody was unable to find the configuration file."); + print("We looked for: "..(CFG_CONFIGDIR or ".").."/prosody.cfg.lua"); + print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist"); + print("Copy or rename it to prosody.cfg.lua and edit as necessary."); + end + print("More help on configuring Prosody can be found at http://prosody.im/doc/configure"); + print("Good luck!"); + print("**************************"); + print(""); + os.exit(1); + end +end + +local error_messages = setmetatable({ + ["invalid-username"] = "The given username is invalid in a Jabber ID"; + ["invalid-hostname"] = "The given hostname is invalid"; + ["no-password"] = "No password was supplied"; + ["no-such-user"] = "The given user does not exist on the server"; + }, { __index = function (t,k) return "Error: "..(tostring(k):gsub("%-", " "):gsub("^.", string.upper)); end }); + +hosts = {}; + +require "core.hostmanager" +require "core.eventmanager".fire_event("server-starting"); + +require "util.prosodyctl" +----------------------- + +function show_message(msg, ...) + print(msg:format(...)); +end + +function show_warning(msg, ...) + print(msg:format(...)); +end + +function show_usage(usage, desc) + print("Usage: "..arg[0].." "..usage); + if desc then + print(" "..desc); + end +end + +local function getchar(n) + os.execute("stty raw -echo"); + local char = io.read(n or 1); + os.execute("stty sane"); + return char; +end + +local function getpass() + os.execute("stty -echo"); + local pass = io.read("*l"); + os.execute("stty sane"); + io.write("\n"); + return pass; +end + +function show_yesno(prompt) + io.write(prompt, " "); + local choice = getchar():lower(); + io.write("\n"); + if not choice:match("%a") then + choice = prompt:match("%[.-(%U).-%]$"); + if not choice then return nil; end + end + return (choice == "y"); +end + +local function read_password() + local password; + while true do + io.write("Enter new password: "); + password = getpass(); + io.write("Retype new password: "); + if getpass() ~= password then + if not show_yesno [=[Passwords did not match, try again? [Y/n]]=] then + return; + end + else + break; + end + end + return password; +end +----------------------- +local commands = {}; +local command = arg[1]; + +function commands.adduser(arg) + if not arg[1] or arg[1] == "--help" then + show_usage([[adduser JID]], [[Create the specified user account in Prosody]]); + return 1; + end + local user, host = arg[1]:match("([^@]+)@(.+)"); + if not user and host then + show_message [[Failed to understand JID, please supply the JID you want to create]] + show_usage [[adduser user@host]] + return 1; + end + + if not host then + show_message [[Please specify a JID, including a host. e.g. alice@example.com]]; + return 1; + end + + if prosodyctl.user_exists{ user = user, host = host } then + show_message [[That user already exists]]; + return 1; + end + + if not hosts[host] then + show_warning("The host '%s' is not listed in the configuration file (or is not enabled).", host) + show_warning("The user will not be able to log in until this is changed."); + end + + local password = read_password(); + if not password then return 1; end + + local ok, msg = prosodyctl.adduser { user = user, host = host, password = password }; + + if ok then return 0; end + + show_message(error_messages[msg]) + return 1; +end + +function commands.passwd(arg) + if not arg[1] or arg[1] == "--help" then + show_usage([[passwd JID]], [[Set the password for the specified user account in Prosody]]); + return 1; + end + local user, host = arg[1]:match("([^@]+)@(.+)"); + if not user and host then + show_message [[Failed to understand JID, please supply the JID you want to set the password for]] + show_usage [[passwd user@host]] + return 1; + end + + if not host then + show_message [[Please specify a JID, including a host. e.g. alice@example.com]]; + return 1; + end + + if not prosodyctl.user_exists { user = user, host = host } then + show_message [[That user does not exist, use prosodyctl adduser to create a new user]] + return 1; + end + + local password = read_password(); + if not password then return 1; end + + local ok, msg = prosodyctl.passwd { user = user, host = host, password = password }; + + if ok then return 0; end + + show_message(error_messages[msg]) + return 1; +end + +function commands.deluser(arg) + if not arg[1] or arg[1] == "--help" then + show_usage([[deluser JID]], [[Permanently remove the specified user account from Prosody]]); + return 1; + end + local user, host = arg[1]:match("([^@]+)@(.+)"); + if not user and host then + show_message [[Failed to understand JID, please supply the JID you want to set the password for]] + show_usage [[passwd user@host]] + return 1; + end + + if not host then + show_message [[Please specify a JID, including a host. e.g. alice@example.com]]; + return 1; + end + + if not prosodyctl.user_exists { user = user, host = host } then + show_message [[That user does not exist on this server]] + return 1; + end + + local ok, msg = prosodyctl.passwd { user = user, host = host }; + + if ok then return 0; end + + show_message(error_messages[msg]) + return 1; +end + +function commands.start() + local ok, ret = prosodyctl.isrunning(); + if not ok then + show_message(error_messages[ret]); + return 1; + end + + if ret then + local ok, ret = prosodyctl.getpid(); + if not ok then + show_message("Couldn't get running Prosody's PID"); + show_message(error_messages[ret]); + return 1; + end + show_message("Prosody is already running with PID %s", ret or "(unknown)"); + return 1; + end + + local ok, ret = prosodyctl.start(); + if ok then return 0; end + + show_message("Failed to start Prosody"); + show_message(error_messages[ret]) + return 1; +end + +function commands.status() + local ok, ret = prosodyctl.isrunning(); + if not ok then + show_message(error_messages[ret]); + return 1; + end + + if ret then + local ok, ret = prosodyctl.getpid(); + if not ok then + show_message("Couldn't get running Prosody's PID"); + show_message(error_messages[ret]); + return 1; + end + show_message("Prosody is running with PID %s", ret or "(unknown)"); + return 0; + end + return 1; +end + +function commands.stop() + if not prosodyctl.isrunning() then + show_message("Prosody is not running"); + return 1; + end + + local ok, ret = prosodyctl.stop(); + if ok then return 0; end + + show_message(error_messages[ret]) + return 1; +end + +-- ejabberdctl compatibility + +function commands.register(arg) + local user, host, password = unpack(arg); + if (not (user and host)) or arg[1] == "--help" then + if not user and user ~= "--help" then + show_message [[No username specified]] + elseif not host then + show_message [[Please specify which host you want to register the user on]]; + end + show_usage("register USER HOST [PASSWORD]", "Register a user on the server, with the given password"); + return 1; + end + if not password then + password = read_password(); + if not password then + show_message [[Unable to register user with no password]]; + return 1; + end + end + + local ok, msg = prosodyctl.adduser { user = user, host = host, password = password }; + + if ok then return 0; end + + show_message(error_messages[msg]) + return 1; +end + +function commands.unregister(arg) + local user, host = unpack(arg); + if (not (user and host)) or arg[1] == "--help" then + if not user then + show_message [[No username specified]] + elseif not host then + show_message [[Please specify which host you want to unregister the user from]]; + end + show_usage("register USER HOST [PASSWORD]", "Permanently remove a user account from the server"); + return 1; + end + + local ok, msg = prosodyctl.deluser { user = user, host = host }; + + if ok then return 0; end + + show_message(error_messages[msg]) + return 1; +end + + +--------------------- + +if not commands[command] then -- Show help for all commands + function show_usage(usage, desc) + print(" "..usage); + print(" "..desc); + end + + print("prosodyctl - Manage a Prosody server"); + print(""); + print("Usage: "..arg[0].." COMMAND [OPTIONS]"); + print(""); + print("Where COMMAND may be one of:\n"); + + local commands_order = { "adduser", "passwd", "deluser" }; + + local done = {}; + + for _, command_name in ipairs(commands_order) do + local command = commands[command_name]; + if command then + command{ "--help" }; + print"" + done[command_name] = true; + end + end + + for command_name, command in pairs(commands) do + if not done[command_name] then + command{ "--help" }; + print"" + done[command_name] = true; + end + end + + + os.exit(0); +end + +os.exit(commands[command]({ select(2, unpack(arg)) })); diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua new file mode 100644 index 00000000..4c6b6ea4 --- /dev/null +++ b/util/prosodyctl.lua @@ -0,0 +1,113 @@ + +local config = require "core.configmanager"; +local encodings = require "util.encodings"; +local stringprep = encodings.stringprep; +local usermanager = require "core.usermanager"; +local signal = require "util.signal"; + +local nodeprep, nameprep = stringprep.nodeprep, stringprep.nameprep; + +local io, os = io, os; +local tostring, tonumber = tostring, tonumber; +module "prosodyctl" + +function adduser(params) + local user, host, password = nodeprep(params.user), nameprep(params.host), params.password; + if not user then + return false, "invalid-username"; + elseif not host then + return false, "invalid-hostname"; + end + + usermanager.create_user(user, password, host); + return true; +end + +function user_exists(params) + return usermanager.user_exists(params.user, params.host); +end + +function passwd(params) + if not _M.user_exists(params) then + return false, "no-such-user"; + end + + return _M.adduser(params); +end + +function deluser(params) + if not _M.user_exists(params) then + return false, "no-such-user"; + end + params.password = nil; + + return _M.adduser(params); +end + +function getpid() + local pidfile = config.get("*", "core", "pidfile"); + if not pidfile then + return false, "no-pidfile"; + end + + local file, err = io.open(pidfile); + if not file then + return false, "pidfile-read-failed", ret; + end + + local pid = tonumber(file:read("*a")); + file:close(); + + if not pid then + return false, "invalid-pid"; + end + + return true, pid; +end + +function isrunning() + local ok, pid, err = _M.getpid(); + if not ok then + if pid == "pidfile-read-failed" then + -- Report as not running, since we can't open the pidfile + -- (it probably doesn't exist) + return true, false; + end + return ok, pid; + end + return true, signal.kill(pid, 0) == 0; +end + +function start() + local ok, ret = _M.isrunning(); + if not ok then + return ok, ret; + end + if ret then + return false, "already-running"; + end + if not CFG_SOURCEDIR then + os.execute("./prosody"); + elseif CFG_SOURCEDIR:match("^/usr/local") then + os.execute("/usr/local/bin/prosody"); + else + os.execute("prosody"); + end + return true; +end + +function stop() + local ok, ret = _M.isrunning(); + if not ok then + return ok, ret; + end + if not ret then + return false, "not-running"; + end + + local ok, pid = _M.getpid() + if not ok then return false, pid; end + + signal.kill(pid, signal.SIGTERM); + return true; +end |