Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

# Copyright (c) 2014, Facebook, Inc.  All rights reserved. 

# 

# This source code is licensed under the BSD-style license found in the 

# LICENSE file in the root directory of this source tree. An additional grant 

# of patent rights can be found in the PATENTS file in the same directory. 

# 

"""This module defines helpers for running and managing daemonized services""" 

from __future__ import absolute_import 

 

import errno 

import logging 

import os 

import signal 

 

from sparts.deps import HAS_DAEMONIZE 

from sparts.fileutils import readfile 

 

23if HAS_DAEMONIZE: 

    # Only attempt to import if we have it... 

    from daemonize import Daemonize 

 

 

def _using_pidfile(pidfile, logger): 

    """Log what `pidfile` we'll be using to `logger`""" 

    logger.info("Using lockfile at '%s'...", pidfile) 

 

 

def read_pid(pidfile, logger): 

    """Returns the pid in `pidfile` or None if the file doesn't exist.""" 

    _using_pidfile(pidfile, logger) 

    try: 

        return int(readfile(pidfile)) 

    except IOError as e: 

37        if e.errno == errno.ENOENT: 

            logger.info("Daemon not running (no lockfile)") 

            return None 

        raise 

 

def send_signal(pid, signum, logger): 

    """Sends signal `signum` to `pid`, logging messages to `logger`""" 

    logger.info("Sending signal %d to PID %d...", signum, pid) 

    os.kill(pid, signum) 

 

 

def daemonize(command, name, pidfile, logger): 

    """Daemonizes the `command` function. 

     

    Uses `name` for syslogging, `pidfile` for the pid file, and logs messages 

    to `logger` or a child logger of logger as appropriate. 

    """ 

    # This is the only function that requires daemonize. 

    # Bail if we don't have it. 

54    if not HAS_DAEMONIZE: 

        raise Exception("Need `daemonize` to run as daemon") 

 

    _using_pidfile(pidfile, logger) 

    daemon = Daemonize( 

        app=name, 

        pid=pidfile, 

        action=command, 

        logger=logging.getLogger(logger.name + ".daemon") 

    ) 

 

    # Daemonize.start() calls sys.exit() for parent thread, so nothing 

    # after this will execute. 

    daemon.start() 

 

 

def kill(pidfile, logger, signum=signal.SIGTERM): 

    """Sends `signum` to the pid specified by `pidfile`. 

 

    Logs messages to `logger`.  Returns True if the process is not running, 

    or signal was sent successfully.  Returns False if the process for the 

    pidfile was running and there was an error sending the signal.""" 

    daemon_pid = read_pid(pidfile, logger) 

    if daemon_pid is None: 

        return True 

 

    try: 

        send_signal(daemon_pid, signum, logger) 

        return True 

 

    except OSError as e: 

        if e.errno == errno.ESRCH: 

            logger.warning("Daemon not running (Stale lockfile)") 

            os.remove(pidfile) 

            return True 

        elif e.errno == errno.EPERM: 

            logger.error("Unable to kill %d (EPERM)", daemon_pid) 

            return False 

        raise 

 

 

def status(pidfile, logger): 

    """Checks to see if the process for the pid in `pidfile` is running. 

 

    Logs messages to `logger`.  Returns True if there is a program for the 

    running pid.  Returns False if not or if there was an error 

    polling the pid.""" 

    daemon_pid = read_pid(pidfile, logger) 

    if daemon_pid is None: 

        return False 

 

    try: 

        # Sending signal 0 simply checks if the pid can be sent a signal 

        # and makes sure the pid exist.  It doesn't interrupt the running 

        # program in any way. 

        send_signal(daemon_pid, 0, logger) 

        logger.info("Daemon is alive") 

        return True 

 

    except OSError as e: 

114        if e.errno == errno.ESRCH: 

            logger.warning("Daemon not running (Stale lockfile)") 

            os.remove(pidfile) 

            return False 

120        elif e.errno == errno.EPERM: 

            logger.error("Unable to poll %d (EPERM)", daemon_pid) 

            return False 

        raise