tools/ejabberd2prosody: Disable generating a config, as the format it generates is...
[prosody.git] / plugins / mod_posix.lua
index 901cb53d9556b60140578cdaa95fdadad160de3c..28fd7f382e6aa08fb5e6de8c57f33942b70c6156 100644 (file)
+-- Prosody IM
+-- Copyright (C) 2008-2010 Matthew Wild
+-- Copyright (C) 2008-2010 Waqas Hussain
+-- 
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
 
-local want_pposix_version = "0.3.0";
+
+local want_pposix_version = "0.3.6";
 
 local pposix = assert(require "util.pposix");
-if pposix._VERSION ~= want_pposix_version then module:log("warn", "Unknown version (%s) of binary pposix module, expected %s", tostring(pposix._VERSION), want_pposix_version); end
+if pposix._VERSION ~= want_pposix_version then
+       module:log("warn", "Unknown version (%s) of binary pposix module, expected %s. Perhaps you need to recompile?", tostring(pposix._VERSION), want_pposix_version);
+end
+
+local signal = select(2, pcall(require, "util.signal"));
+if type(signal) == "string" then
+       module:log("warn", "Couldn't load signal library, won't respond to SIGTERM");
+end
 
-local config_get = require "core.configmanager".get;
-local logger_set = require "util.logger".setwriter;
+local lfs = require "lfs";
+local stat = lfs.attributes;
 
-module.host = "*"; -- we're a global module
+local prosody = _G.prosody;
 
-if not config_get("*", "core", "no_daemonize") then
-       local function daemonize_server()
-               local logwriter;
-               
-               local logfilename = config_get("*", "core", "log");
-               if logfilename == "syslog" then
-                       pposix.syslog_open("prosody");
-                       pposix.syslog_setminlevel(config.get("*", "core", "minimum_log_level") or "info");
-                               local syslog, format = pposix.syslog_log, string.format;
-                               logwriter = function (name, level, message, ...)
-                                                       if ... then 
-                                                               syslog(level, format(message, ...));
-                                                       else
-                                                               syslog(level, message);
-                                                       end
-                                               end;                    
-               elseif logfilename then
-                       local logfile = io.open(logfilename, "a+");
-                       if logfile then
-                               local write, format, flush = logfile.write, string.format, logfile.flush;
-                               logwriter = function (name, level, message, ...)
-                                                       if ... then 
-                                                               write(logfile, name, "\t", level, "\t", format(message, ...), "\n");
-                                                       else
-                                                               write(logfile, name, "\t" , level, "\t", message, "\n");
-                                                       end
-                                                       flush(logfile);
-                                               end;
+module:set_global(); -- we're a global module
+
+local umask = module:get_option("umask") or "027";
+pposix.umask(umask);
+
+-- Allow switching away from root, some people like strange ports.
+module:hook("server-started", function ()
+               local uid = module:get_option("setuid");
+               local gid = module:get_option("setgid");
+               if gid then
+                       local success, msg = pposix.setgid(gid);
+                       if success then
+                               module:log("debug", "Changed group to %s successfully.", gid);
+                       else
+                               module:log("error", "Failed to change group to %s. Error: %s", gid, msg);
+                               prosody.shutdown("Failed to change group to %s", gid);
+                       end
+               end
+               if uid then
+                       local success, msg = pposix.setuid(uid);
+                       if success then
+                               module:log("debug", "Changed user to %s successfully.", uid);
+                       else
+                               module:log("error", "Failed to change user to %s. Error: %s", uid, msg);
+                               prosody.shutdown("Failed to change user to %s", uid);
                        end
+               end
+       end);
+
+-- Don't even think about it!
+if not prosody.start_time then -- server-starting
+       local suid = module:get_option("setuid");
+       if not suid or suid == 0 or suid == "root" then
+               if pposix.getuid() == 0 and not module:get_option("run_as_root") then
+                       module:log("error", "Danger, Will Robinson! Prosody doesn't need to be run as root, so don't do it!");
+                       module:log("error", "For more information on running Prosody as root, see http://prosody.im/doc/root");
+                       prosody.shutdown("Refusing to run as root");
+               end
+       end
+end
+
+local pidfile;
+local pidfile_handle;
+
+local function remove_pidfile()
+       if pidfile_handle then
+               pidfile_handle:close();
+               os.remove(pidfile);
+               pidfile, pidfile_handle = nil, nil;
+       end
+end
+
+local function write_pidfile()
+       if pidfile_handle then
+               remove_pidfile();
+       end
+       pidfile = module:get_option("pidfile");
+       if pidfile then
+               local err;
+               local mode = stat(pidfile) and "r+" or "w+";
+               pidfile_handle, err = io.open(pidfile, mode);
+               if not pidfile_handle then
+                       module:log("error", "Couldn't write pidfile at %s; %s", pidfile, err);
+                       prosody.shutdown("Couldn't write pidfile");
                else
-                       log("debug", "No logging specified, will continue with default");
+                       if not lfs.lock(pidfile_handle, "w") then -- Exclusive lock
+                               local other_pid = pidfile_handle:read("*a");
+                               module:log("error", "Another Prosody instance seems to be running with PID %s, quitting", other_pid);
+                               pidfile_handle = nil;
+                               prosody.shutdown("Prosody already running");
+                       else
+                               pidfile_handle:close();
+                               pidfile_handle, err = io.open(pidfile, "w+");
+                               if not pidfile_handle then
+                                       module:log("error", "Couldn't write pidfile at %s; %s", pidfile, err);
+                                       prosody.shutdown("Couldn't write pidfile");
+                               else
+                                       if lfs.lock(pidfile_handle, "w") then
+                                               pidfile_handle:write(tostring(pposix.getpid()));
+                                               pidfile_handle:flush();
+                                       end
+                               end
+                       end
                end
-               
+       end
+end
+
+local syslog_opened;
+function syslog_sink_maker(config)
+       if not syslog_opened then
+               pposix.syslog_open("prosody", module:get_option_string("syslog_facility"));
+               syslog_opened = true;
+       end
+       local syslog, format = pposix.syslog_log, string.format;
+       return function (name, level, message, ...)
+               if ... then
+                       syslog(level, name, format(message, ...));
+               else
+                       syslog(level, name, message);
+               end
+       end;
+end
+require "core.loggingmanager".register_sink_type("syslog", syslog_sink_maker);
+
+local daemonize = module:get_option("daemonize");
+if daemonize == nil then
+       local no_daemonize = module:get_option("no_daemonize"); --COMPAT w/ 0.5
+       daemonize = not no_daemonize;
+       if no_daemonize ~= nil then
+               module:log("warn", "The 'no_daemonize' option is now replaced by 'daemonize'");
+               module:log("warn", "Update your config from 'no_daemonize = %s' to 'daemonize = %s'", tostring(no_daemonize), tostring(daemonize));
+       end
+end
+
+local function remove_log_sinks()
+       local lm = require "core.loggingmanager";
+       lm.register_sink_type("console", nil);
+       lm.register_sink_type("stdout", nil);
+       lm.reload_logging();
+end
+
+if daemonize then
+       local function daemonize_server()
+               module:log("info", "Prosody is about to detach from the console, disabling further console output");
+               remove_log_sinks();
                local ok, ret = pposix.daemonize();
                if not ok then
-                       log("error", "Failed to daemonize: %s", ret);
+                       module:log("error", "Failed to daemonize: %s", ret);
                elseif ret and ret > 0 then
                        os.exit(0);
                else
-                       if logwriter then
-                               local ok, ret = logger_set(logwriter);
-                               if not ok then
-                                       log("error", "Couldn't set new log output: %s", ret);
-                               end
-                       end
-                       log("info", "Successfully daemonized to PID %d", pposix.getpid());
-                       
-                       local pidfile = config.get("*", "core", "pidfile");
-                       if pidfile then
-                               local pf, err = io.open(pidfile, "w+");
-                               if not pf then
-                                       log("error", "Couldn't write pidfile; %s", err);
-                               else
-                                       pf:write(tostring(pposix.getpid()));
-                                       pf:close();
-                               end
-                       end
+                       module:log("info", "Successfully daemonized to PID %d", pposix.getpid());
+                       write_pidfile();
                end
        end
-       module:add_event_hook("server-starting", daemonize_server);
+       if not prosody.start_time then -- server-starting
+               daemonize_server();
+       end
+else
+       -- Not going to daemonize, so write the pid of this process
+       write_pidfile();
+end
+
+module:hook("server-stopped", remove_pidfile);
+
+-- Set signal handlers
+if signal.signal then
+       signal.signal("SIGTERM", function ()
+               module:log("warn", "Received SIGTERM");
+               prosody.unlock_globals();
+               prosody.shutdown("Received SIGTERM");
+               prosody.lock_globals();
+       end);
+
+       signal.signal("SIGHUP", function ()
+               module:log("info", "Received SIGHUP");
+               prosody.reload_config();
+               prosody.reopen_logfiles();
+       end);
+       
+       signal.signal("SIGINT", function ()
+               module:log("info", "Received SIGINT");
+               prosody.unlock_globals();
+               prosody.shutdown("Received SIGINT");
+               prosody.lock_globals();
+       end);
 end