Statistics
| Branch: | Tag: | Revision:

root / daemons / ganeti-noded @ dcb93971

History | View | Annotate | Download (12 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Ganeti node daemon"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import os
25 a8083063 Iustin Pop
import sys
26 a8083063 Iustin Pop
import resource
27 a8083063 Iustin Pop
import traceback
28 a8083063 Iustin Pop
29 a8083063 Iustin Pop
from optparse import OptionParser
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
32 a8083063 Iustin Pop
from ganeti import backend
33 a8083063 Iustin Pop
from ganeti import logger
34 a8083063 Iustin Pop
from ganeti import constants
35 a8083063 Iustin Pop
from ganeti import objects
36 a8083063 Iustin Pop
from ganeti import errors
37 a8083063 Iustin Pop
from ganeti import ssconf
38 a8083063 Iustin Pop
39 a8083063 Iustin Pop
from twisted.spread import pb
40 a8083063 Iustin Pop
from twisted.internet import reactor
41 a8083063 Iustin Pop
from twisted.cred import checkers, portal
42 a8083063 Iustin Pop
from OpenSSL import SSL
43 a8083063 Iustin Pop
44 a8083063 Iustin Pop
45 a8083063 Iustin Pop
class ServerContextFactory:
46 a8083063 Iustin Pop
  def getContext(self):
47 a8083063 Iustin Pop
    ctx = SSL.Context(SSL.TLSv1_METHOD)
48 a8083063 Iustin Pop
    ctx.use_certificate_file(constants.SSL_CERT_FILE)
49 a8083063 Iustin Pop
    ctx.use_privatekey_file(constants.SSL_CERT_FILE)
50 a8083063 Iustin Pop
    return ctx
51 a8083063 Iustin Pop
52 a8083063 Iustin Pop
class ServerObject(pb.Avatar):
53 a8083063 Iustin Pop
  def __init__(self, name):
54 a8083063 Iustin Pop
    self.name = name
55 a8083063 Iustin Pop
56 a8083063 Iustin Pop
  def perspectiveMessageReceived(self, broker, message, args, kw):
57 a8083063 Iustin Pop
    """This method is called when a network message is received.
58 a8083063 Iustin Pop
59 a8083063 Iustin Pop
    I will call::
60 a8083063 Iustin Pop
61 a8083063 Iustin Pop
      |  self.perspective_%(message)s(*broker.unserialize(args),
62 a8083063 Iustin Pop
      |                               **broker.unserialize(kw))
63 a8083063 Iustin Pop
64 a8083063 Iustin Pop
    to handle the method; subclasses of Avatar are expected to
65 a8083063 Iustin Pop
    implement methods of this naming convention.
66 a8083063 Iustin Pop
    """
67 a8083063 Iustin Pop
68 a8083063 Iustin Pop
    args = broker.unserialize(args, self)
69 a8083063 Iustin Pop
    kw = broker.unserialize(kw, self)
70 a8083063 Iustin Pop
    method = getattr(self, "perspective_%s" % message)
71 a8083063 Iustin Pop
    tb = None
72 a8083063 Iustin Pop
    state = None
73 a8083063 Iustin Pop
    try:
74 a8083063 Iustin Pop
      state = method(*args, **kw)
75 a8083063 Iustin Pop
    except:
76 a8083063 Iustin Pop
      tb = traceback.format_exc()
77 a8083063 Iustin Pop
78 a8083063 Iustin Pop
    return broker.serialize((tb, state), self, method, args, kw)
79 a8083063 Iustin Pop
80 a8083063 Iustin Pop
  # the new block devices  --------------------------
81 a8083063 Iustin Pop
82 a8083063 Iustin Pop
  def perspective_blockdev_create(self,params):
83 a8083063 Iustin Pop
    bdev_s, size, on_primary = params
84 a8083063 Iustin Pop
    bdev = objects.ConfigObject.Loads(bdev_s)
85 a8083063 Iustin Pop
    if bdev is None:
86 a8083063 Iustin Pop
      raise ValueError("can't unserialize data!")
87 a8083063 Iustin Pop
    return backend.CreateBlockDevice(bdev, size, on_primary)
88 a8083063 Iustin Pop
89 a8083063 Iustin Pop
90 a8083063 Iustin Pop
  def perspective_blockdev_remove(self,params):
91 a8083063 Iustin Pop
    bdev_s = params[0]
92 a8083063 Iustin Pop
    bdev = objects.ConfigObject.Loads(bdev_s)
93 a8083063 Iustin Pop
    return backend.RemoveBlockDevice(bdev)
94 a8083063 Iustin Pop
95 a8083063 Iustin Pop
96 a8083063 Iustin Pop
  def perspective_blockdev_assemble(self,params):
97 a8083063 Iustin Pop
    bdev_s, on_primary = params
98 a8083063 Iustin Pop
    bdev = objects.ConfigObject.Loads(bdev_s)
99 a8083063 Iustin Pop
    if bdev is None:
100 a8083063 Iustin Pop
      raise ValueError("can't unserialize data!")
101 a8083063 Iustin Pop
    return backend.AssembleBlockDevice(bdev, on_primary)
102 a8083063 Iustin Pop
103 a8083063 Iustin Pop
104 a8083063 Iustin Pop
  def perspective_blockdev_shutdown(self,params):
105 a8083063 Iustin Pop
    bdev_s = params[0]
106 a8083063 Iustin Pop
    bdev = objects.ConfigObject.Loads(bdev_s)
107 a8083063 Iustin Pop
    if bdev is None:
108 a8083063 Iustin Pop
      raise ValueError("can't unserialize data!")
109 a8083063 Iustin Pop
    return backend.ShutdownBlockDevice(bdev)
110 a8083063 Iustin Pop
111 a8083063 Iustin Pop
112 a8083063 Iustin Pop
  def perspective_blockdev_addchild(self,params):
113 a8083063 Iustin Pop
    bdev_s, ndev_s = params
114 a8083063 Iustin Pop
    bdev = objects.ConfigObject.Loads(bdev_s)
115 a8083063 Iustin Pop
    ndev = objects.ConfigObject.Loads(ndev_s)
116 a8083063 Iustin Pop
    if bdev is None or ndev is None:
117 a8083063 Iustin Pop
      raise ValueError("can't unserialize data!")
118 a8083063 Iustin Pop
    return backend.MirrorAddChild(bdev, ndev)
119 a8083063 Iustin Pop
120 a8083063 Iustin Pop
121 a8083063 Iustin Pop
  def perspective_blockdev_removechild(self,params):
122 a8083063 Iustin Pop
    bdev_s, ndev_s = params
123 a8083063 Iustin Pop
    bdev = objects.ConfigObject.Loads(bdev_s)
124 a8083063 Iustin Pop
    ndev = objects.ConfigObject.Loads(ndev_s)
125 a8083063 Iustin Pop
    if bdev is None or ndev is None:
126 a8083063 Iustin Pop
      raise ValueError("can't unserialize data!")
127 a8083063 Iustin Pop
    return backend.MirrorRemoveChild(bdev, ndev)
128 a8083063 Iustin Pop
129 a8083063 Iustin Pop
  def perspective_blockdev_getmirrorstatus(self, params):
130 a8083063 Iustin Pop
    disks = [objects.ConfigObject.Loads(dsk_s)
131 a8083063 Iustin Pop
            for dsk_s in params]
132 a8083063 Iustin Pop
    return backend.GetMirrorStatus(disks)
133 a8083063 Iustin Pop
134 a8083063 Iustin Pop
  def perspective_blockdev_find(self, params):
135 a8083063 Iustin Pop
    disk = objects.ConfigObject.Loads(params[0])
136 a8083063 Iustin Pop
    return backend.FindBlockDevice(disk)
137 a8083063 Iustin Pop
138 a8083063 Iustin Pop
  def perspective_blockdev_snapshot(self,params):
139 a8083063 Iustin Pop
    cfbd = objects.ConfigObject.Loads(params[0])
140 a8083063 Iustin Pop
    return backend.SnapshotBlockDevice(cfbd)
141 a8083063 Iustin Pop
142 a8083063 Iustin Pop
  # export/import  --------------------------
143 a8083063 Iustin Pop
144 a8083063 Iustin Pop
  def perspective_snapshot_export(self,params):
145 a8083063 Iustin Pop
    disk = objects.ConfigObject.Loads(params[0])
146 a8083063 Iustin Pop
    dest_node = params[1]
147 a8083063 Iustin Pop
    instance = objects.ConfigObject.Loads(params[2])
148 a8083063 Iustin Pop
    return backend.ExportSnapshot(disk,dest_node,instance)
149 a8083063 Iustin Pop
150 a8083063 Iustin Pop
  def perspective_finalize_export(self,params):
151 a8083063 Iustin Pop
    instance = objects.ConfigObject.Loads(params[0])
152 a8083063 Iustin Pop
    snap_disks = [objects.ConfigObject.Loads(str_data)
153 a8083063 Iustin Pop
                  for str_data in params[1]]
154 a8083063 Iustin Pop
    return backend.FinalizeExport(instance, snap_disks)
155 a8083063 Iustin Pop
156 a8083063 Iustin Pop
  def perspective_export_info(self,params):
157 a8083063 Iustin Pop
    dir = params[0]
158 a8083063 Iustin Pop
    einfo = backend.ExportInfo(dir)
159 a8083063 Iustin Pop
    if einfo is None:
160 a8083063 Iustin Pop
      return einfo
161 a8083063 Iustin Pop
    return einfo.Dumps()
162 a8083063 Iustin Pop
163 a8083063 Iustin Pop
  def perspective_export_list(self, params):
164 a8083063 Iustin Pop
    return backend.ListExports()
165 a8083063 Iustin Pop
166 a8083063 Iustin Pop
  def perspective_export_remove(self, params):
167 a8083063 Iustin Pop
    export = params[0]
168 a8083063 Iustin Pop
    return backend.RemoveExport(export)
169 a8083063 Iustin Pop
170 a8083063 Iustin Pop
  # volume  --------------------------
171 a8083063 Iustin Pop
172 a8083063 Iustin Pop
  def perspective_volume_list(self,params):
173 a8083063 Iustin Pop
    vgname = params[0]
174 a8083063 Iustin Pop
    return backend.GetVolumeList(vgname)
175 a8083063 Iustin Pop
176 a8083063 Iustin Pop
  def perspective_vg_list(self,params):
177 a8083063 Iustin Pop
    return backend.ListVolumeGroups()
178 a8083063 Iustin Pop
179 a8083063 Iustin Pop
  # bridge  --------------------------
180 a8083063 Iustin Pop
181 a8083063 Iustin Pop
  def perspective_bridges_exist(self,params):
182 a8083063 Iustin Pop
    bridges_list = params[0]
183 a8083063 Iustin Pop
    return backend.BridgesExist(bridges_list)
184 a8083063 Iustin Pop
185 a8083063 Iustin Pop
  # instance  --------------------------
186 a8083063 Iustin Pop
187 a8083063 Iustin Pop
  def perspective_instance_os_add(self,params):
188 a8083063 Iustin Pop
    inst_s, os_disk, swap_disk = params
189 a8083063 Iustin Pop
    inst = objects.ConfigObject.Loads(inst_s)
190 a8083063 Iustin Pop
    return backend.AddOSToInstance(inst, os_disk, swap_disk)
191 a8083063 Iustin Pop
192 a8083063 Iustin Pop
  def perspective_instance_os_import(self, params):
193 a8083063 Iustin Pop
    inst_s, os_disk, swap_disk, src_node, src_image = params
194 a8083063 Iustin Pop
    inst = objects.ConfigObject.Loads(inst_s)
195 a8083063 Iustin Pop
    return backend.ImportOSIntoInstance(inst, os_disk, swap_disk,
196 a8083063 Iustin Pop
                                        src_node, src_image)
197 a8083063 Iustin Pop
198 a8083063 Iustin Pop
  def perspective_instance_shutdown(self,params):
199 a8083063 Iustin Pop
    instance = objects.ConfigObject.Loads(params[0])
200 a8083063 Iustin Pop
    return backend.ShutdownInstance(instance)
201 a8083063 Iustin Pop
202 a8083063 Iustin Pop
  def perspective_instance_start(self,params):
203 a8083063 Iustin Pop
    instance = objects.ConfigObject.Loads(params[0])
204 a8083063 Iustin Pop
    extra_args = params[1]
205 a8083063 Iustin Pop
    return backend.StartInstance(instance, extra_args)
206 a8083063 Iustin Pop
207 a8083063 Iustin Pop
  def perspective_instance_info(self,params):
208 a8083063 Iustin Pop
    return backend.GetInstanceInfo(params[0])
209 a8083063 Iustin Pop
210 a8083063 Iustin Pop
  def perspective_all_instances_info(self,params):
211 a8083063 Iustin Pop
    return backend.GetAllInstancesInfo()
212 a8083063 Iustin Pop
213 a8083063 Iustin Pop
  def perspective_instance_list(self,params):
214 a8083063 Iustin Pop
    return backend.GetInstanceList()
215 a8083063 Iustin Pop
216 a8083063 Iustin Pop
  # node --------------------------
217 a8083063 Iustin Pop
218 a8083063 Iustin Pop
  def perspective_node_info(self,params):
219 a8083063 Iustin Pop
    vgname = params[0]
220 a8083063 Iustin Pop
    return backend.GetNodeInfo(vgname)
221 a8083063 Iustin Pop
222 a8083063 Iustin Pop
  def perspective_node_add(self,params):
223 a8083063 Iustin Pop
    return backend.AddNode(params[0], params[1], params[2],
224 a8083063 Iustin Pop
                           params[3], params[4], params[5])
225 a8083063 Iustin Pop
226 a8083063 Iustin Pop
  def perspective_node_verify(self,params):
227 a8083063 Iustin Pop
    return backend.VerifyNode(params[0])
228 a8083063 Iustin Pop
229 a8083063 Iustin Pop
  def perspective_node_start_master(self, params):
230 a8083063 Iustin Pop
    return backend.StartMaster()
231 a8083063 Iustin Pop
232 a8083063 Iustin Pop
  def perspective_node_stop_master(self, params):
233 a8083063 Iustin Pop
    return backend.StopMaster()
234 a8083063 Iustin Pop
235 a8083063 Iustin Pop
  def perspective_node_leave_cluster(self, params):
236 a8083063 Iustin Pop
    return backend.LeaveCluster()
237 a8083063 Iustin Pop
238 dcb93971 Michael Hanselmann
  def perspective_node_volumes(self, params):
239 dcb93971 Michael Hanselmann
    return backend.NodeVolumes()
240 dcb93971 Michael Hanselmann
241 a8083063 Iustin Pop
  # cluster --------------------------
242 a8083063 Iustin Pop
243 a8083063 Iustin Pop
  def perspective_version(self,params):
244 a8083063 Iustin Pop
    return constants.PROTOCOL_VERSION
245 a8083063 Iustin Pop
246 a8083063 Iustin Pop
  def perspective_configfile_list(self,params):
247 a8083063 Iustin Pop
    return backend.ListConfigFiles()
248 a8083063 Iustin Pop
249 a8083063 Iustin Pop
  def perspective_upload_file(self,params):
250 a8083063 Iustin Pop
    return backend.UploadFile(*params)
251 a8083063 Iustin Pop
252 a8083063 Iustin Pop
253 a8083063 Iustin Pop
  # os -----------------------
254 a8083063 Iustin Pop
255 a8083063 Iustin Pop
  def perspective_os_diagnose(self, params):
256 a8083063 Iustin Pop
    os_list = backend.DiagnoseOS()
257 a8083063 Iustin Pop
    if not os_list:
258 a8083063 Iustin Pop
      # this catches also return values of 'False',
259 a8083063 Iustin Pop
      # for which we can't iterate over
260 a8083063 Iustin Pop
      return os_list
261 a8083063 Iustin Pop
    result = []
262 a8083063 Iustin Pop
    for data in os_list:
263 a8083063 Iustin Pop
      if isinstance(data, objects.OS):
264 a8083063 Iustin Pop
        result.append(data.Dumps())
265 a8083063 Iustin Pop
      elif isinstance(data, errors.InvalidOS):
266 a8083063 Iustin Pop
        result.append(data.args)
267 a8083063 Iustin Pop
      else:
268 a8083063 Iustin Pop
        raise errors.ProgrammerError, ("Invalid result from backend.DiagnoseOS"
269 a8083063 Iustin Pop
                                       " (class %s, %s)" %
270 a8083063 Iustin Pop
                                       (str(data.__class__), data))
271 a8083063 Iustin Pop
272 a8083063 Iustin Pop
    return result
273 a8083063 Iustin Pop
274 a8083063 Iustin Pop
  def perspective_os_get(self, params):
275 a8083063 Iustin Pop
    name = params[0]
276 a8083063 Iustin Pop
    try:
277 a8083063 Iustin Pop
      os = backend.OSFromDisk(name).Dumps()
278 a8083063 Iustin Pop
    except errors.InvalidOS, err:
279 a8083063 Iustin Pop
      os = err.args
280 a8083063 Iustin Pop
    return os
281 a8083063 Iustin Pop
282 a8083063 Iustin Pop
  # hooks -----------------------
283 a8083063 Iustin Pop
284 a8083063 Iustin Pop
  def perspective_hooks_runner(self, params):
285 a8083063 Iustin Pop
    hpath, phase, env = params
286 a8083063 Iustin Pop
    hr = backend.HooksRunner()
287 a8083063 Iustin Pop
    return hr.RunHooks(hpath, phase, env)
288 a8083063 Iustin Pop
289 a8083063 Iustin Pop
290 a8083063 Iustin Pop
class MyRealm:
291 a8083063 Iustin Pop
  __implements__ = portal.IRealm
292 a8083063 Iustin Pop
  def requestAvatar(self, avatarId, mind, *interfaces):
293 a8083063 Iustin Pop
    if pb.IPerspective not in interfaces:
294 a8083063 Iustin Pop
      raise NotImplementedError
295 a8083063 Iustin Pop
    return pb.IPerspective, ServerObject(avatarId), lambda:None
296 a8083063 Iustin Pop
297 a8083063 Iustin Pop
298 a8083063 Iustin Pop
def ParseOptions():
299 a8083063 Iustin Pop
  """Parse the command line options.
300 a8083063 Iustin Pop
301 a8083063 Iustin Pop
  Returns:
302 a8083063 Iustin Pop
    (options, args) as from OptionParser.parse_args()
303 a8083063 Iustin Pop
304 a8083063 Iustin Pop
  """
305 a8083063 Iustin Pop
  parser = OptionParser(description="Ganeti node daemon",
306 a8083063 Iustin Pop
                        usage="%prog [-f] [-d]",
307 a8083063 Iustin Pop
                        version="%%prog (ganeti) %s" %
308 a8083063 Iustin Pop
                        constants.RELEASE_VERSION)
309 a8083063 Iustin Pop
310 a8083063 Iustin Pop
  parser.add_option("-f", "--foreground", dest="fork",
311 a8083063 Iustin Pop
                    help="Don't detach from the current terminal",
312 a8083063 Iustin Pop
                    default=True, action="store_false")
313 a8083063 Iustin Pop
  parser.add_option("-d", "--debug", dest="debug",
314 a8083063 Iustin Pop
                    help="Enable some debug messages",
315 a8083063 Iustin Pop
                    default=False, action="store_true")
316 a8083063 Iustin Pop
  options, args = parser.parse_args()
317 a8083063 Iustin Pop
  return options, args
318 a8083063 Iustin Pop
319 a8083063 Iustin Pop
320 a8083063 Iustin Pop
def main():
321 a8083063 Iustin Pop
  options, args = ParseOptions()
322 a8083063 Iustin Pop
  for fname in (constants.SSL_CERT_FILE,):
323 a8083063 Iustin Pop
    if not os.path.isfile(fname):
324 a8083063 Iustin Pop
      print "config %s not there, will not run." % fname
325 a8083063 Iustin Pop
      sys.exit(5)
326 a8083063 Iustin Pop
327 a8083063 Iustin Pop
  try:
328 a8083063 Iustin Pop
    ss = ssconf.SimpleStore()
329 a8083063 Iustin Pop
    port = ss.GetNodeDaemonPort()
330 a8083063 Iustin Pop
    pwdata = ss.GetNodeDaemonPassword()
331 a8083063 Iustin Pop
  except errors.ConfigurationError, err:
332 a8083063 Iustin Pop
    print "Cluster configuration incomplete: '%s'" % str(err)
333 a8083063 Iustin Pop
    sys.exit(5)
334 a8083063 Iustin Pop
335 a8083063 Iustin Pop
  # become a daemon
336 a8083063 Iustin Pop
  if options.fork:
337 a8083063 Iustin Pop
    createDaemon()
338 a8083063 Iustin Pop
339 a8083063 Iustin Pop
  logger.SetupLogging(twisted_workaround=True, debug=options.debug,
340 a8083063 Iustin Pop
                      program="ganeti-noded")
341 a8083063 Iustin Pop
342 a8083063 Iustin Pop
  p = portal.Portal(MyRealm())
343 a8083063 Iustin Pop
  p.registerChecker(
344 a8083063 Iustin Pop
    checkers.InMemoryUsernamePasswordDatabaseDontUse(master_node=pwdata))
345 a8083063 Iustin Pop
  reactor.listenSSL(port, pb.PBServerFactory(p), ServerContextFactory())
346 a8083063 Iustin Pop
  reactor.run()
347 a8083063 Iustin Pop
348 a8083063 Iustin Pop
349 a8083063 Iustin Pop
def createDaemon():
350 a8083063 Iustin Pop
  """Detach a process from the controlling terminal and run it in the
351 a8083063 Iustin Pop
  background as a daemon.
352 a8083063 Iustin Pop
  """
353 a8083063 Iustin Pop
  UMASK = 077
354 a8083063 Iustin Pop
  WORKDIR = "/"
355 a8083063 Iustin Pop
  # Default maximum for the number of available file descriptors.
356 a8083063 Iustin Pop
  if 'SC_OPEN_MAX' in os.sysconf_names:
357 a8083063 Iustin Pop
    try:
358 a8083063 Iustin Pop
      MAXFD = os.sysconf('SC_OPEN_MAX')
359 a8083063 Iustin Pop
      if MAXFD < 0:
360 a8083063 Iustin Pop
        MAXFD = 1024
361 a8083063 Iustin Pop
    except OSError:
362 a8083063 Iustin Pop
      MAXFD = 1024
363 a8083063 Iustin Pop
  else:
364 a8083063 Iustin Pop
    MAXFD = 1024
365 a8083063 Iustin Pop
  # The standard I/O file descriptors are redirected to /dev/null by default.
366 a8083063 Iustin Pop
  #REDIRECT_TO = getattr(os, "devnull", "/dev/null")
367 a8083063 Iustin Pop
  REDIRECT_TO = constants.LOG_NODESERVER
368 a8083063 Iustin Pop
  try:
369 a8083063 Iustin Pop
    pid = os.fork()
370 a8083063 Iustin Pop
  except OSError, e:
371 a8083063 Iustin Pop
    raise Exception, "%s [%d]" % (e.strerror, e.errno)
372 3c9a0742 Michael Hanselmann
  if (pid == 0):  # The first child.
373 a8083063 Iustin Pop
    os.setsid()
374 a8083063 Iustin Pop
    try:
375 3c9a0742 Michael Hanselmann
      pid = os.fork() # Fork a second child.
376 a8083063 Iustin Pop
    except OSError, e:
377 a8083063 Iustin Pop
      raise Exception, "%s [%d]" % (e.strerror, e.errno)
378 3c9a0742 Michael Hanselmann
    if (pid == 0):  # The second child.
379 a8083063 Iustin Pop
      os.chdir(WORKDIR)
380 a8083063 Iustin Pop
      os.umask(UMASK)
381 a8083063 Iustin Pop
    else:
382 a8083063 Iustin Pop
      # exit() or _exit()?  See below.
383 3c9a0742 Michael Hanselmann
      os._exit(0) # Exit parent (the first child) of the second child.
384 a8083063 Iustin Pop
  else:
385 3c9a0742 Michael Hanselmann
    os._exit(0) # Exit parent of the first child.
386 a8083063 Iustin Pop
  maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
387 a8083063 Iustin Pop
  if (maxfd == resource.RLIM_INFINITY):
388 a8083063 Iustin Pop
    maxfd = MAXFD
389 a8083063 Iustin Pop
390 a8083063 Iustin Pop
  # Iterate through and close all file descriptors.
391 a8083063 Iustin Pop
  for fd in range(0, maxfd):
392 a8083063 Iustin Pop
    try:
393 a8083063 Iustin Pop
      os.close(fd)
394 3c9a0742 Michael Hanselmann
    except OSError: # ERROR, fd wasn't open to begin with (ignored)
395 a8083063 Iustin Pop
      pass
396 a8083063 Iustin Pop
  os.open(REDIRECT_TO, os.O_RDWR|os.O_CREAT|os.O_APPEND) # standard input (0)
397 a8083063 Iustin Pop
  # Duplicate standard input to standard output and standard error.
398 3c9a0742 Michael Hanselmann
  os.dup2(0, 1)     # standard output (1)
399 3c9a0742 Michael Hanselmann
  os.dup2(0, 2)     # standard error (2)
400 a8083063 Iustin Pop
  return(0)
401 a8083063 Iustin Pop
402 a8083063 Iustin Pop
403 a8083063 Iustin Pop
if __name__=='__main__':
404 a8083063 Iustin Pop
  main()