|
1 # server.py - utility and factory of server |
|
2 # |
|
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> |
|
4 # |
|
5 # This software may be used and distributed according to the terms of the |
|
6 # GNU General Public License version 2 or any later version. |
|
7 |
|
8 from __future__ import absolute_import |
|
9 |
|
10 import errno |
|
11 import os |
|
12 import sys |
|
13 import tempfile |
|
14 |
|
15 from .i18n import _ |
|
16 |
|
17 from . import ( |
|
18 error, |
|
19 util, |
|
20 ) |
|
21 |
|
22 def runservice(opts, parentfn=None, initfn=None, runfn=None, logfile=None, |
|
23 runargs=None, appendpid=False): |
|
24 '''Run a command as a service.''' |
|
25 |
|
26 def writepid(pid): |
|
27 if opts['pid_file']: |
|
28 if appendpid: |
|
29 mode = 'a' |
|
30 else: |
|
31 mode = 'w' |
|
32 fp = open(opts['pid_file'], mode) |
|
33 fp.write(str(pid) + '\n') |
|
34 fp.close() |
|
35 |
|
36 if opts['daemon'] and not opts['daemon_postexec']: |
|
37 # Signal child process startup with file removal |
|
38 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-') |
|
39 os.close(lockfd) |
|
40 try: |
|
41 if not runargs: |
|
42 runargs = util.hgcmd() + sys.argv[1:] |
|
43 runargs.append('--daemon-postexec=unlink:%s' % lockpath) |
|
44 # Don't pass --cwd to the child process, because we've already |
|
45 # changed directory. |
|
46 for i in xrange(1, len(runargs)): |
|
47 if runargs[i].startswith('--cwd='): |
|
48 del runargs[i] |
|
49 break |
|
50 elif runargs[i].startswith('--cwd'): |
|
51 del runargs[i:i + 2] |
|
52 break |
|
53 def condfn(): |
|
54 return not os.path.exists(lockpath) |
|
55 pid = util.rundetached(runargs, condfn) |
|
56 if pid < 0: |
|
57 raise error.Abort(_('child process failed to start')) |
|
58 writepid(pid) |
|
59 finally: |
|
60 try: |
|
61 os.unlink(lockpath) |
|
62 except OSError as e: |
|
63 if e.errno != errno.ENOENT: |
|
64 raise |
|
65 if parentfn: |
|
66 return parentfn(pid) |
|
67 else: |
|
68 return |
|
69 |
|
70 if initfn: |
|
71 initfn() |
|
72 |
|
73 if not opts['daemon']: |
|
74 writepid(util.getpid()) |
|
75 |
|
76 if opts['daemon_postexec']: |
|
77 try: |
|
78 os.setsid() |
|
79 except AttributeError: |
|
80 pass |
|
81 for inst in opts['daemon_postexec']: |
|
82 if inst.startswith('unlink:'): |
|
83 lockpath = inst[7:] |
|
84 os.unlink(lockpath) |
|
85 elif inst.startswith('chdir:'): |
|
86 os.chdir(inst[6:]) |
|
87 elif inst != 'none': |
|
88 raise error.Abort(_('invalid value for --daemon-postexec: %s') |
|
89 % inst) |
|
90 util.hidewindow() |
|
91 util.stdout.flush() |
|
92 util.stderr.flush() |
|
93 |
|
94 nullfd = os.open(os.devnull, os.O_RDWR) |
|
95 logfilefd = nullfd |
|
96 if logfile: |
|
97 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND) |
|
98 os.dup2(nullfd, 0) |
|
99 os.dup2(logfilefd, 1) |
|
100 os.dup2(logfilefd, 2) |
|
101 if nullfd not in (0, 1, 2): |
|
102 os.close(nullfd) |
|
103 if logfile and logfilefd not in (0, 1, 2): |
|
104 os.close(logfilefd) |
|
105 |
|
106 if runfn: |
|
107 return runfn() |