| 380 | |
| 381 | @staticmethod |
| 382 | def daemonize( |
| 383 | stdin='/dev/null', stdout='/dev/null', stderr='/dev/null', |
| 384 | logger=lambda msg: None): |
| 385 | # See http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 |
| 386 | # (or http://www.faqs.org/faqs/unix-faq/programmer/faq/ section 1.7) |
| 387 | # and http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66012 |
| 388 | |
| 389 | # Finish up with the current stdout/stderr |
| 390 | sys.stdout.flush() |
| 391 | sys.stderr.flush() |
| 392 | |
| 393 | error_tmpl = ( |
| 394 | '{sys.argv[0]}: fork #{n} failed: ({exc.errno}) {exc.strerror}\n' |
| 395 | ) |
| 396 | |
| 397 | for fork in range(2): |
| 398 | msg = ['Forking once.', 'Forking twice.'][fork] |
| 399 | try: |
| 400 | pid = os.fork() |
| 401 | if pid > 0: |
| 402 | # This is the parent; exit. |
| 403 | logger(msg) |
| 404 | os._exit(0) |
| 405 | except OSError as exc: |
| 406 | # Python raises OSError rather than returning negative numbers. |
| 407 | sys.exit(error_tmpl.format(sys=sys, exc=exc, n=fork + 1)) |
| 408 | if fork == 0: |
| 409 | os.setsid() |
| 410 | |
| 411 | os.umask(0) |
| 412 | |
| 413 | si = open(stdin, 'r') |
| 414 | so = open(stdout, 'a+') |
| 415 | se = open(stderr, 'a+') |
| 416 | |
| 417 | # os.dup2(fd, fd2) will close fd2 if necessary, |
| 418 | # so we don't explicitly close stdin/out/err. |
| 419 | # See http://docs.python.org/lib/os-fd-ops.html |
| 420 | os.dup2(si.fileno(), sys.stdin.fileno()) |
| 421 | os.dup2(so.fileno(), sys.stdout.fileno()) |
| 422 | os.dup2(se.fileno(), sys.stderr.fileno()) |
| 423 | |
| 424 | logger('Daemonized to PID: %s' % os.getpid()) |
| 425 | |
| 426 | |
| 427 | class PIDFile(SimplePlugin): |