Source code for thriftpool.utils.platforms

from __future__ import absolute_import

import os
import errno
import atexit
import sys
import resource
from contextlib import contextmanager

try:
    import setproctitle
except ImportError:
    setproctitle_exists = False
else:
    setproctitle_exists = True


EX_OK = getattr(os, 'EX_OK', 0)
EX_FAILURE = 1
EX_UNAVAILABLE = getattr(os, 'EX_UNAVAILABLE', 69)
EX_USAGE = getattr(os, 'EX_USAGE', 64)

DAEMON_UMASK = 0
DAEMON_WORKDIR = '/'


[docs]def set_process_title(name): """Change process title.""" if not setproctitle_exists: return setproctitle.setproctitle(name)
[docs]def fileno(f): try: return f.fileno() except AttributeError: return f
[docs]def get_fdmax(default=None): """Returns the maximum number of open file descriptors on this system. :keyword default: Value returned if there's no file descriptor limit. """ fdmax = resource.getrlimit(resource.RLIMIT_NOFILE)[1] if fdmax == resource.RLIM_INFINITY: return default return fdmax
@contextmanager
[docs]def ignore_EBADF(): try: yield except OSError as exc: if exc.errno != errno.EBADF: raise
[docs]class LockFailed(Exception): pass
[docs]class PIDLock(object): """Create PID lock and work with it as with resource.""" FLAGS = os.O_CREAT | os.O_EXCL | os.O_WRONLY MODE = ((os.R_OK | os.W_OK) << 6) | ((os.R_OK) << 3) | ((os.R_OK)) def __init__(self, path): self.path = os.path.abspath(path)
[docs] def read(self): """Reads and returns the writed PID lock.""" try: fh = open(self.path, 'r') except IOError as exc: if exc.errno == errno.ENOENT: return raise try: line = fh.readline() if line.strip() == line: # must contain '\n' raise ValueError( 'Partial or invalid PID lock {0.path}'.format(self)) finally: fh.close() try: return int(line.strip()) except ValueError: raise ValueError('PID lock {0.path} invalid.'.format(self))
[docs] def write(self, pid): """Write given PID to lock.""" fd = os.open(self.path, self.FLAGS, self.MODE) fh = os.fdopen(fd, 'w') try: fh.write('{0}\n'.format(pid)) # flush and sync so that the re-read below works. fh.flush() try: os.fsync(fd) except AttributeError: pass finally: fh.close() # Check that PID wasn't change. if self.read() != pid: raise LockFailed('PID lock changed!')
[docs] def remove(self): """Remove PID lock if exists.""" try: os.unlink(self.path) except OSError as exc: if exc.errno == errno.ENOENT: return raise
[docs] def exists(self): """Returns True if the PID lock exists.""" return os.path.exists(self.path)
@staticmethod
[docs] def process_exists(pid): """Check that process exists.""" try: os.kill(pid, 0) except os.error as exc: if exc.errno == errno.ESRCH: return False return True
[docs] def maybe_remove(self): """Remove existed PID lock if we can.""" if not self.exists(): # No PID exists, return. return pid = self.read() if pid is None: # No PID found. return if self.process_exists(pid): raise LockFailed('PID lock exists.') self.remove()
[docs] def acquire(self): """Try to write PID lock.""" self.maybe_remove() self.write(os.getpid())
[docs] def release(self, *args): """Try to remove PID lock.""" self.remove()
[docs]def create_pidlock(pidfile): """Create PID lock, exit if fail.""" pid = PIDLock(pidfile) try: pid.acquire() except LockFailed: raise SystemExit("Error: PID file ({0}) exists.".format(pidfile)) atexit.register(pid.release) return pid
[docs]class Daemon(object): """Fork and close needed resources.""" _is_open = False workdir = DAEMON_WORKDIR umask = DAEMON_UMASK def __init__(self, fake=False, excluded_fds=None): self.fake = fake self.standart_fds = (sys.stdin, sys.stdout, sys.stderr) self.excluded_fds = tuple(excluded_fds or []) @property
[docs] def preserved_fds(self): return self.standart_fds + self.excluded_fds
[docs] def redirect_to_null(self, fd): if fd: dest = os.open(os.devnull, os.O_RDWR) os.dup2(dest, fd)
def _detach(self): if os.fork(): os._exit(0) os.setsid() if os.fork(): os._exit(0) return self
[docs] def open(self): if not self._is_open: if not self.fake: self._detach() os.chdir(self.workdir) os.umask(self.umask) preserve = [fileno(f) for f in self.preserved_fds if fileno(f)] for fd in reversed(range(get_fdmax(default=2048))): if fd not in preserve: with ignore_EBADF(): os.close(fd) for fd in self.standart_fds: self.redirect_to_null(fileno(fd)) self._is_open = True
[docs] def close(self, *args): if self._is_open: self._is_open = False
[docs]def daemonize(fake=False, excluded_fds=None): """Try to start daemon.""" daemon = Daemon(fake, excluded_fds) daemon.open() atexit.register(daemon.close) return daemon

Project Versions

This Page