Update docs with information about how to use thread-unsafe code in
[salmon.git] / salmon / utils.py
blob3d1f407d90424062d902134194a40004b6238cc3
1 """
2 Mostly utility functions Salmon uses internally that don't
3 really belong anywhere else in the modules. This module
4 is kind of a dumping ground, so if you find something that
5 can be improved feel free to work up a patch.
6 """
7 import imp
8 import importlib
9 import logging
10 import os
11 import sys
13 from lockfile import pidlockfile
14 import daemon
16 from salmon import routing, server
18 settings = None
21 def import_settings(boot_also, boot_module="config.boot"):
22 """Returns the current settings module, there is no harm in calling it
23 multiple times
25 The location of the settings module can be control via
26 ``SALMON_SETTINGS_MODULE``"""
27 global settings
29 if settings is None:
30 settings_module = os.getenv("SALMON_SETTINGS_MODULE", "config.settings")
31 settings = importlib.import_module(settings_module)
33 if boot_also:
34 importlib.import_module(boot_module)
36 return settings
39 def daemonize(pid, chdir, chroot, umask, files_preserve=None, do_open=True):
40 """
41 Uses python-daemonize to do all the junk needed to make a
42 server a server. It supports all the features daemonize
43 has, except that chroot probably won't work at all without
44 some serious configuration on the system.
45 """
46 logs_dir = os.path.join(chdir, "logs")
47 pid_dir = os.path.join(chdir, os.path.dirname(pid) or ".")
48 if chroot:
49 logs_dir = os.path.join(chroot, logs_dir)
50 pid_dir = os.path.join(chroot, pid_dir)
51 if not os.path.exists(logs_dir):
52 os.mkdir(logs_dir)
53 if not os.path.exists(pid_dir):
54 os.mkdir(pid_dir)
56 context = daemon.DaemonContext()
57 context.pidfile = pidlockfile.PIDLockFile(pid)
58 context.stdout = open(os.path.join(logs_dir, "salmon.out"), "a+")
59 context.stderr = open(os.path.join(logs_dir, "salmon.err"), "a+")
60 context.files_preserve = files_preserve or []
61 context.working_directory = os.path.expanduser(chdir)
63 if chroot:
64 context.chroot_directory = os.path.expanduser(chroot)
65 if umask is not None:
66 context.umask = umask
68 if do_open:
69 context.open()
71 return context
74 def drop_priv(uid, gid):
75 """
76 Changes the uid/gid to the two given, you should give utils.daemonize
77 0,0 for the uid,gid so that it becomes root, which will allow you to then
78 do this.
79 """
80 logging.debug("Dropping to uid=%d, gid=%d", uid, gid)
81 daemon.daemon.change_process_owner(uid, gid)
82 logging.debug("Now running as uid=%d, gid=%d", os.getgid(), os.getuid())
85 def make_fake_settings(host, port):
86 """
87 When running as a logging server we need a fake settings module to work with
88 since the logging server can be run in any directory, so there may not be
89 a settings module to import.
90 """
91 global settings
93 if settings is None:
94 logging.basicConfig(filename="logs/logger.log", level=logging.DEBUG)
95 routing.Router.load(['salmon.handlers.log', 'salmon.handlers.queue'])
96 settings = imp.new_module('settings')
97 settings.receiver = server.SMTPReceiver(host, port)
98 settings.relay = None
99 logging.info("Logging mode enabled, will not send email to anyone, just log.")
101 return settings
104 def check_for_pid(pid, force):
105 """Checks if a pid file is there, and if it is sys.exit. If force given
106 then it will remove the file and not exit if it's there."""
107 if os.path.exists(pid):
108 if not force:
109 print("PID file %s exists, so assuming Salmon is running. Give --force to force it to start." % pid)
110 sys.exit(1)
111 else:
112 os.unlink(pid)
115 def start_server(pid, force, chroot, chdir, uid, gid, umask, settings_loader, debug, daemon_proc):
117 Starts the server by doing a daemonize and then dropping priv
118 accordingly. It will only drop to the uid/gid given if both are given.
120 check_for_pid(pid, force)
122 if not debug and daemon_proc:
123 daemonize(pid, chdir, chroot, umask, files_preserve=[])
125 sys.path.append(os.getcwd())
127 settings = settings_loader()
129 if uid and gid:
130 drop_priv(uid, gid)
131 elif uid or gid:
132 logging.warning("You probably meant to give a uid and gid, but you gave: uid=%r, gid=%r. "
133 "Will not change to any user.", uid, gid)
135 settings.receiver.start()
137 if debug:
138 print("Salmon started in debug mode. ctrl-c to quit...")
139 import time
140 try:
141 while True:
142 time.sleep(100000)
143 except KeyboardInterrupt:
144 # hard quit, since receiver starts a new thread. dirty but works
145 os._exit(1)