prosodyctl
changeset 8638 47e3b8b6f17a
parent 8564 7b9ffddc4276
child 8655 03bb534593cb
equal deleted inserted replaced
8637:f6f62c92b642 8638:47e3b8b6f17a
    41 	if os.getenv("HOME") then
    41 	if os.getenv("HOME") then
    42 		CFG_DATADIR = CFG_DATADIR:gsub("^~", os.getenv("HOME"));
    42 		CFG_DATADIR = CFG_DATADIR:gsub("^~", os.getenv("HOME"));
    43 	end
    43 	end
    44 end
    44 end
    45 
    45 
    46 -- Global 'prosody' object
    46 -----------
    47 local prosody = {
    47 
    48 	hosts = {};
    48 require "util.startup".prosodyctl();
    49 	events = require "util.events".new();
    49 
    50 	platform = "posix";
    50 -----------
    51 	lock_globals = function () end;
       
    52 	unlock_globals = function () end;
       
    53 	installed = CFG_SOURCEDIR ~= nil;
       
    54 	core_post_stanza = function () end; -- TODO: mod_router!
       
    55 };
       
    56 _G.prosody = prosody;
       
    57 
       
    58 local dependencies = require "util.dependencies";
       
    59 if not dependencies.check_dependencies() then
       
    60 	os.exit(1);
       
    61 end
       
    62 
       
    63 config = require "core.configmanager"
       
    64 
       
    65 local ENV_CONFIG;
       
    66 do
       
    67 	local filenames = {};
       
    68 
       
    69 	local filename;
       
    70 	if arg[1] == "--config" and arg[2] then
       
    71 		table.insert(filenames, arg[2]);
       
    72 		if CFG_CONFIGDIR then
       
    73 			table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]);
       
    74 		end
       
    75 		table.remove(arg, 1); table.remove(arg, 1);
       
    76 	else
       
    77 		table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg.lua");
       
    78 	end
       
    79 	for _,_filename in ipairs(filenames) do
       
    80 		filename = _filename;
       
    81 		local file = io.open(filename);
       
    82 		if file then
       
    83 			file:close();
       
    84 			ENV_CONFIG = filename;
       
    85 			CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$");
       
    86 			break;
       
    87 		end
       
    88 	end
       
    89 	local ok, level, err = config.load(filename);
       
    90 	if not ok then
       
    91 		print("\n");
       
    92 		print("**************************");
       
    93 		if level == "parser" then
       
    94 			print("A problem occured while reading the config file "..filename);
       
    95 			local err_line, err_message = tostring(err):match("%[string .-%]:(%d*): (.*)");
       
    96 			print("Error"..(err_line and (" on line "..err_line) or "")..": "..(err_message or tostring(err)));
       
    97 			print("");
       
    98 		elseif level == "file" then
       
    99 			print("Prosody was unable to find the configuration file.");
       
   100 			print("We looked for: "..filename);
       
   101 			print("A sample config file is included in the Prosody download called prosody.cfg.lua.dist");
       
   102 			print("Copy or rename it to prosody.cfg.lua and edit as necessary.");
       
   103 		end
       
   104 		print("More help on configuring Prosody can be found at https://prosody.im/doc/configure");
       
   105 		print("Good luck!");
       
   106 		print("**************************");
       
   107 		print("");
       
   108 		os.exit(1);
       
   109 	end
       
   110 end
       
   111 local original_logging_config = config.get("*", "log");
       
   112 config.set("*", "log", { { levels = { min="info" }, to = "console" } });
       
   113 
       
   114 local data_path = config.get("*", "data_path") or CFG_DATADIR or "data";
       
   115 local custom_plugin_paths = config.get("*", "plugin_paths");
       
   116 if custom_plugin_paths then
       
   117 	local path_sep = package.config:sub(3,3);
       
   118 	-- path1;path2;path3;defaultpath...
       
   119 	CFG_PLUGINDIR = table.concat(custom_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins");
       
   120 end
       
   121 prosody.paths = { source = CFG_SOURCEDIR, config = CFG_CONFIGDIR,
       
   122 	          plugins = CFG_PLUGINDIR or "plugins", data = data_path };
       
   123 
       
   124 if prosody.installed then
       
   125 	-- Change working directory to data path.
       
   126 	require "lfs".chdir(data_path);
       
   127 end
       
   128 
       
   129 require "core.loggingmanager"
       
   130 
       
   131 dependencies.log_warnings();
       
   132 
       
   133 -- Switch away from root and into the prosody user --
       
   134 local switched_user, current_uid;
       
   135 
       
   136 local want_pposix_version = "0.4.0";
       
   137 local have_pposix, pposix = pcall(require, "util.pposix");
       
   138 
       
   139 if have_pposix and pposix then
       
   140 	if pposix._VERSION ~= want_pposix_version then
       
   141 		print(string.format("Unknown version (%s) of binary pposix module, expected %s",
       
   142 			tostring(pposix._VERSION), want_pposix_version)); return;
       
   143 	end
       
   144 	current_uid = pposix.getuid();
       
   145 	local arg_root = arg[1] == "--root";
       
   146 	if arg_root then table.remove(arg, 1); end
       
   147 	if current_uid == 0 and config.get("*", "run_as_root") ~= true and not arg_root then
       
   148 		-- We haz root!
       
   149 		local desired_user = config.get("*", "prosody_user") or "prosody";
       
   150 		local desired_group = config.get("*", "prosody_group") or desired_user;
       
   151 		local ok, err = pposix.setgid(desired_group);
       
   152 		if ok then
       
   153 			ok, err = pposix.initgroups(desired_user);
       
   154 		end
       
   155 		if ok then
       
   156 			ok, err = pposix.setuid(desired_user);
       
   157 			if ok then
       
   158 				-- Yay!
       
   159 				switched_user = true;
       
   160 			end
       
   161 		end
       
   162 		if not switched_user then
       
   163 			-- Boo!
       
   164 			print("Warning: Couldn't switch to Prosody user/group '"..tostring(desired_user).."'/'"..tostring(desired_group).."': "..tostring(err));
       
   165 		else
       
   166 			-- Make sure the Prosody user can read the config
       
   167 			local conf, err, errno = io.open(ENV_CONFIG);
       
   168 			if conf then
       
   169 				conf:close();
       
   170 			else
       
   171 				print("The config file is not readable by the '"..desired_user.."' user.");
       
   172 				print("Prosody will not be able to read it.");
       
   173 				print("Error was "..err);
       
   174 				os.exit(1);
       
   175 			end
       
   176 		end
       
   177 	end
       
   178 
       
   179 	-- Set our umask to protect data files
       
   180 	pposix.umask(config.get("*", "umask") or "027");
       
   181 	pposix.setenv("HOME", data_path);
       
   182 	pposix.setenv("PROSODY_CONFIG", ENV_CONFIG);
       
   183 else
       
   184 	print("Error: Unable to load pposix module. Check that Prosody is installed correctly.")
       
   185 	print("For more help send the below error to us through https://prosody.im/discuss");
       
   186 	print(tostring(pposix))
       
   187 	os.exit(1);
       
   188 end
       
   189 
       
   190 local function test_writeable(filename)
       
   191 	local f, err = io.open(filename, "a");
       
   192 	if not f then
       
   193 		return false, err;
       
   194 	end
       
   195 	f:close();
       
   196 	return true;
       
   197 end
       
   198 
       
   199 local unwriteable_files = {};
       
   200 if type(original_logging_config) == "string" and original_logging_config:sub(1,1) ~= "*" then
       
   201 	local ok, err = test_writeable(original_logging_config);
       
   202 	if not ok then
       
   203 		table.insert(unwriteable_files, err);
       
   204 	end
       
   205 elseif type(original_logging_config) == "table" then
       
   206 	for _, rule in ipairs(original_logging_config) do
       
   207 		if rule.filename then
       
   208 			local ok, err = test_writeable(rule.filename);
       
   209 			if not ok then
       
   210 				table.insert(unwriteable_files, err);
       
   211 			end
       
   212 		end
       
   213 	end
       
   214 end
       
   215 
       
   216 if #unwriteable_files > 0 then
       
   217 	print("One of more of the Prosody log files are not");
       
   218 	print("writeable, please correct the errors and try");
       
   219 	print("starting prosodyctl again.");
       
   220 	print("");
       
   221 	for _, err in ipairs(unwriteable_files) do
       
   222 		print(err);
       
   223 	end
       
   224 	print("");
       
   225 	os.exit(1);
       
   226 end
       
   227 
       
   228 
    51 
   229 local error_messages = setmetatable({
    52 local error_messages = setmetatable({
   230 		["invalid-username"] = "The given username is invalid in a Jabber ID";
    53 		["invalid-username"] = "The given username is invalid in a Jabber ID";
   231 		["invalid-hostname"] = "The given hostname is invalid";
    54 		["invalid-hostname"] = "The given hostname is invalid";
   232 		["no-password"] = "No password was supplied";
    55 		["no-password"] = "No password was supplied";
   238 		["no-posix"] = "The mod_posix module is not enabled in the Prosody config file, see https://prosody.im/doc/prosodyctl for more info";
    61 		["no-posix"] = "The mod_posix module is not enabled in the Prosody config file, see https://prosody.im/doc/prosodyctl for more info";
   239 		["no-such-method"] = "This module has no commands";
    62 		["no-such-method"] = "This module has no commands";
   240 		["not-running"] = "Prosody is not running";
    63 		["not-running"] = "Prosody is not running";
   241 		}, { __index = function (t,k) return "Error: "..(tostring(k):gsub("%-", " "):gsub("^.", string.upper)); end });
    64 		}, { __index = function (t,k) return "Error: "..(tostring(k):gsub("%-", " "):gsub("^.", string.upper)); end });
   242 
    65 
   243 hosts = prosody.hosts;
    66 local config = require "core.configmanager";
   244 
       
   245 local function make_host(hostname)
       
   246 	return {
       
   247 		type = "local",
       
   248 		events = prosody.events,
       
   249 		modules = {},
       
   250 		sessions = {},
       
   251 		users = require "core.usermanager".new_null_provider(hostname)
       
   252 	};
       
   253 end
       
   254 
       
   255 for hostname, config in pairs(config.getconfig()) do
       
   256 	hosts[hostname] = make_host(hostname);
       
   257 end
       
   258 
       
   259 local modulemanager = require "core.modulemanager"
    67 local modulemanager = require "core.modulemanager"
   260 
       
   261 local prosodyctl = require "util.prosodyctl"
    68 local prosodyctl = require "util.prosodyctl"
   262 local socket = require "socket"
    69 local socket = require "socket"
   263 
    70 local dependencies = require "util.dependencies";
   264 local http = require "net.http"
       
   265 local config_ssl = config.get("*", "ssl") or {}
       
   266 local https_client = config.get("*", "client_https_ssl")
       
   267 http.default.options.sslctx = require "core.certmanager".create_context("client_https port 0", "client",
       
   268 	{ capath = config_ssl.capath, cafile = config_ssl.cafile, verify = "peer", }, https_client);
       
   269 
    71 
   270 -----------------------
    72 -----------------------
   271 
       
   272  -- FIXME: Duplicate code waiting for util.startup
       
   273 function read_version()
       
   274 	-- Try to determine version
       
   275 	local version_file = io.open((CFG_SOURCEDIR or ".").."/prosody.version");
       
   276 	prosody.version = "unknown";
       
   277 	if version_file then
       
   278 		prosody.version = version_file:read("*a"):gsub("%s*$", "");
       
   279 		version_file:close();
       
   280 		if #prosody.version == 12 and prosody.version:match("^[a-f0-9]+$") then
       
   281 			prosody.version = "hg:"..prosody.version;
       
   282 		end
       
   283 	else
       
   284 		local hg = require"util.mercurial";
       
   285 		local hgid = hg.check_id(CFG_SOURCEDIR or ".");
       
   286 		if hgid then prosody.version = "hg:" .. hgid; end
       
   287 	end
       
   288 end
       
   289 
    73 
   290 local show_message, show_warning = prosodyctl.show_message, prosodyctl.show_warning;
    74 local show_message, show_warning = prosodyctl.show_message, prosodyctl.show_warning;
   291 local show_usage = prosodyctl.show_usage;
    75 local show_usage = prosodyctl.show_usage;
   292 local show_yesno = prosodyctl.show_yesno;
    76 local show_yesno = prosodyctl.show_yesno;
   293 local show_prompt = prosodyctl.show_prompt;
    77 local show_prompt = prosodyctl.show_prompt;
   544 	commands.stop(arg);
   328 	commands.stop(arg);
   545 	return commands.start(arg);
   329 	return commands.start(arg);
   546 end
   330 end
   547 
   331 
   548 function commands.about(arg)
   332 function commands.about(arg)
   549 	read_version();
       
   550 	if arg[1] == "--help" then
   333 	if arg[1] == "--help" then
   551 		show_usage([[about]], [[Show information about this Prosody installation]]);
   334 		show_usage([[about]], [[Show information about this Prosody installation]]);
   552 		return 1;
   335 		return 1;
   553 	end
   336 	end
   554 
   337 
   560 	local relpath = config.resolve_relative_path;
   343 	local relpath = config.resolve_relative_path;
   561 
   344 
   562 	print("Prosody "..(prosody.version or "(unknown version)"));
   345 	print("Prosody "..(prosody.version or "(unknown version)"));
   563 	print("");
   346 	print("");
   564 	print("# Prosody directories");
   347 	print("# Prosody directories");
   565 	print("Data directory:     "..relpath(pwd, data_path));
   348 	print("Data directory:     "..relpath(pwd, prosody.paths.data));
   566 	print("Config directory:   "..relpath(pwd, CFG_CONFIGDIR or "."));
   349 	print("Config directory:   "..relpath(pwd, prosody.paths.config or "."));
   567 	print("Source directory:   "..relpath(pwd, CFG_SOURCEDIR or "."));
   350 	print("Source directory:   "..relpath(pwd, prosody.paths.source or "."));
   568 	print("Plugin directories:")
   351 	print("Plugin directories:")
   569 	print("  "..(prosody.paths.plugins:gsub("([^;]+);?", function(path)
   352 	print("  "..(prosody.paths.plugins:gsub("([^;]+);?", function(path)
   570 			path = config.resolve_relative_path(pwd, path);
   353 			path = config.resolve_relative_path(pwd, path);
   571 			local hgid, hgrepo = hg.check_id(path);
   354 			local hgid, hgrepo = hg.check_id(path);
   572 			if not hgid and hgrepo then
   355 			if not hgid and hgrepo then
   714 			return true;
   497 			return true;
   715 		end
   498 		end
   716 	end
   499 	end
   717 end
   500 end
   718 
   501 
   719 local cert_basedir = CFG_DATADIR or "./certs";
   502 local have_pposix, pposix = pcall(require, "util.pposix");
       
   503 local cert_basedir = prosody.paths.data == "." and "./certs" or prosody.paths.data;
   720 if have_pposix and pposix.getuid() == 0 then
   504 if have_pposix and pposix.getuid() == 0 then
   721 	-- FIXME should be enough to check if this directory is writable
   505 	-- FIXME should be enough to check if this directory is writable
   722 	local cert_dir = config.get("*", "certificates") or "certs";
   506 	local cert_dir = config.get("*", "certificates") or "certs";
   723 	cert_basedir = config.resolve_relative_path(prosody.paths.config, cert_dir);
   507 	cert_basedir = config.resolve_relative_path(prosody.paths.config, cert_dir);
   724 end
   508 end