| |
@@ -2,7 +2,6 @@
|
| |
|
| |
import re
|
| |
import os
|
| |
- import fcntl
|
| |
import sys
|
| |
import argparse
|
| |
import requests
|
| |
@@ -15,6 +14,9 @@
|
| |
import stat
|
| |
import pipes
|
| |
import pkg_resources
|
| |
+ import errno
|
| |
+ import daemon
|
| |
+ import time
|
| |
|
| |
try:
|
| |
from simplejson.scanner import JSONDecodeError
|
| |
@@ -30,39 +32,44 @@
|
| |
|
| |
log = logging.getLogger(__name__)
|
| |
log.setLevel(logging.INFO)
|
| |
- log.addHandler(logging.StreamHandler(sys.stdout))
|
| |
+ log.addHandler(logging.StreamHandler(sys.stderr))
|
| |
|
| |
try:
|
| |
VERSION = pkg_resources.require('copr-rpmbuild')[0].version
|
| |
except pkg_resources.DistributionNotFound:
|
| |
VERSION = 'git'
|
| |
|
| |
- def daemonize():
|
| |
+
|
| |
+ def daemonize(pidfilename):
|
| |
try:
|
| |
pid = os.fork()
|
| |
except OSError as e:
|
| |
- self.log.error("Unable to fork, errno: {0}".format(e.errno))
|
| |
+ log.error("Unable to fork, errno: {0}".format(e.errno))
|
| |
sys.exit(1)
|
| |
|
| |
if pid != 0:
|
| |
- log.info(pid)
|
| |
- os._exit(0)
|
| |
-
|
| |
- process_id = os.setsid()
|
| |
- if process_id == -1:
|
| |
+ # wait till child writes the PID into pidfile
|
| |
+ start = time.time()
|
| |
+ while time.time() - start < 10:
|
| |
+ # try-except not needed, pidfilename always exists
|
| |
+ with open(pidfilename, 'r') as pidfile:
|
| |
+ content = pidfile.read()
|
| |
+ if content.endswith("\n"):
|
| |
+ sys.stdout.write(content)
|
| |
+ sys.exit(0)
|
| |
+ time.sleep(0.1)
|
| |
+ log.error("Unable to wait for PID")
|
| |
sys.exit(1)
|
| |
|
| |
- devnull_fd = os.open('/dev/null', os.O_RDWR)
|
| |
- os.dup2(devnull_fd, 0)
|
| |
- os.dup2(devnull_fd, 1)
|
| |
- os.dup2(devnull_fd, 2)
|
| |
- os.close(devnull_fd)
|
| |
+ c = daemon.DaemonContext()
|
| |
+ c.open()
|
| |
|
| |
|
| |
def get_parser():
|
| |
shared_parser = argparse.ArgumentParser(add_help=False)
|
| |
shared_parser.add_argument("-d", "--detached", default=False, action="store_true",
|
| |
- help="Run build in background. Log into /var/lib/copr-rpmbuild/main.log.")
|
| |
+ help="Run build in background. Log into /var/lib/copr-rpmbuild/main.log. "
|
| |
+ "Print PID of the background process.")
|
| |
shared_parser.add_argument("-v", "--verbose", action="count",
|
| |
help="Print debugging information.")
|
| |
shared_parser.add_argument("-r", "--chroot",
|
| |
@@ -117,15 +124,28 @@
|
| |
if args.verbose:
|
| |
log.setLevel(logging.DEBUG)
|
| |
|
| |
+ # Create the pidfile
|
| |
+ pidfilename = config.get("main", "pidfile")
|
| |
+ try:
|
| |
+ os.open(pidfilename, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
|
| |
+ except OSError as e:
|
| |
+ if e.errno == errno.EEXIST:
|
| |
+ log.error("Either another build is running, or the file "
|
| |
+ "'{0}' is forgotten".format(pidfilename))
|
| |
+ sys.exit(1)
|
| |
+ raise(e)
|
| |
+
|
| |
+ # Switch to background, if requested
|
| |
if args.detached:
|
| |
- daemonize()
|
| |
+ daemonize(pidfilename)
|
| |
|
| |
- # Write pid
|
| |
- pidfile = open(config.get("main", "pidfile"), "w")
|
| |
- pidfile.write(str(os.getpid()))
|
| |
- pidfile.close()
|
| |
+ # Let the main process (if --detached) know ASAP what is our PID. Note that
|
| |
+ # the info is also used by copr-backend when re-attaching to running builds
|
| |
+ # after 'systemctl restart'.
|
| |
+ with open(pidfilename, 'w') as pidfile:
|
| |
+ pidfile.write("{0}\n".format(os.getpid()))
|
| |
|
| |
- # Log also to a file
|
| |
+ # Redirect stderr to stdout, and then copy stdout to logfile
|
| |
logfile = config.get("main", "logfile")
|
| |
if logfile:
|
| |
open(logfile, 'w').close() # truncate log
|
| |
@@ -134,10 +154,7 @@
|
| |
log.info('Running: {0}'.format(" ".join(map(pipes.quote, sys.argv))))
|
| |
log.info('Version: {0}'.format(VERSION))
|
| |
|
| |
- # Allow only one instance
|
| |
- lockfd = os.open(config.get("main", "lockfile"), os.O_RDWR | os.O_CREAT)
|
| |
try:
|
| |
- fcntl.lockf(lockfd, fcntl.LOCK_EX, 1)
|
| |
init(args, config)
|
| |
|
| |
if args.dump_configs:
|
| |
@@ -148,15 +165,12 @@
|
| |
action = build_rpm
|
| |
|
| |
action(args, config)
|
| |
- except (RuntimeError, OSError):
|
| |
- log.exception("")
|
| |
+ except RuntimeError as e:
|
| |
+ log.exception(e)
|
| |
sys.exit(1)
|
| |
- except: # Programmer's mistake
|
| |
- log.exception("")
|
| |
+ except Exception as e: # Programmer's mistake
|
| |
+ log.exception(e)
|
| |
sys.exit(1)
|
| |
- finally:
|
| |
- fcntl.lockf(lockfd, fcntl.LOCK_UN, 1)
|
| |
- os.close(lockfd)
|
| |
|
| |
|
| |
def init(args, config):
|
| |
Related to #711