public inbox for cygwin-apps-cvs@sourceware.org help / color / mirror / Atom feed
From: Jon Turney <jturney@sourceware.org> To: cygwin-apps-cvs@sourceware.org Subject: [calm - Cygwin server-side packaging maintenance script] branch master, updated. 20230209-98-gf1ccc90 Date: Tue, 23 Apr 2024 15:21:09 +0000 (GMT) [thread overview] Message-ID: <20240423152110.9CF35385840D@sourceware.org> (raw) https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/calm.git;h=f1ccc9033618d24da93cd10529faea5a7eaa20be commit f1ccc9033618d24da93cd10529faea5a7eaa20be Author: Jon Turney <jon.turney@dronecode.org.uk> Date: Mon Apr 22 19:14:18 2024 +0100 Ignore sighup when daemonized It seems like systemd sends this as well as SIGTERM, so make sure to ignore it so we shut down cleanly. https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/calm.git;h=3a18bba53957302246fc44c6dbbbd26520ed2080 commit 3a18bba53957302246fc44c6dbbbd26520ed2080 Author: Jon Turney <jon.turney@dronecode.org.uk> Date: Sat Feb 17 19:26:03 2024 +0000 Don't update setup.ini if we can't sign it Check with gpg-agent if the signing key(s) are available, and don't update setup.ini if we can't sign it. Also, since we want to do some logging about keygrips before we daemonize, move logging_setup earlier, and don't close the file descriptors it opens when we darmonize. Diff: --- calm/calm.py | 74 ++++++++++++++++++++++++++++++++++++++++++------ calm/common_constants.py | 5 ++++ calm/utils.py | 2 ++ 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/calm/calm.py b/calm/calm.py index ac662a2..02e613b 100755 --- a/calm/calm.py +++ b/calm/calm.py @@ -549,10 +549,39 @@ def do_main(args, state): return 0 +# +# verify signing key(s) are available in gpg-agent +# +def is_passphrase_cached(args): + passphrase_cached = set() + + for k in args.keygrips: + logging.debug('Querying gpg-agent on keygrip %s' % (k)) + key_details = utils.system("/usr/bin/gpg-connect-agent 'keyinfo %s' /bye" % k) + for l in key_details.splitlines(): + if l.startswith('S'): + # check for either PROTECTION='P' and CACHED='1' (passphrase is + # cached) or PROTECTION='C' (no passphrase) + keyinfo = l.split() + if keyinfo[2] == k: + if ((keyinfo[7] == 'P' and keyinfo[6] == '1') or + keyinfo[7] == 'C'): + passphrase_cached.add(k) + else: + logging.error("Signing key not available") + # Provide some help on the necessary runes: start agent + # with --allow-preset-passphrase so that the passphrase + # preloaded with gpg-preset-passphrase doesn't expire. + logging.error("Load it with '/usr/libexec/gpg-preset-passphrase --preset %s' then provide passphrase" % k) + break + + # return True if all keys are accessible + return passphrase_cached == set(args.keygrips) + + # # # - def do_output(args, state): # update packages listings # XXX: perhaps we need a --[no]listing command line option to disable this from being run? @@ -598,7 +627,13 @@ def do_output(args, state): changed = True # then update setup.ini - if changed: + if not changed: + logging.debug("removing %s, unchanged %s" % (tmpfile.name, inifile)) + os.remove(tmpfile.name) + elif not is_passphrase_cached(args): + logging.debug("removing %s, cannot sign" % (tmpfile.name)) + os.remove(tmpfile.name) + else: update_json = True if args.dryrun: @@ -633,10 +668,6 @@ def do_output(args, state): keys = ' '.join(['-u' + k for k in args.keys]) utils.system('/usr/bin/gpg ' + keys + ' --batch --yes -b ' + extfile) - else: - logging.debug("removing %s, unchanged %s" % (tmpfile.name, inifile)) - os.remove(tmpfile.name) - # write packages.json jsonfile = os.path.join(args.htdocs, 'packages.json.xz') if update_json or not os.path.exists(jsonfile): @@ -681,9 +712,19 @@ def do_daemon(args, state): logging.getLogger('inotify.adapters').propagate = False + def getLogFileDescriptors(logger): + """Get a list of fds from logger""" + handles = [] + for handler in logger.handlers: + handles.append(handler.stream.fileno()) + if logger.parent: + handles += getLogFileDescriptors(logger.parent) + return handles + context = daemon.DaemonContext( stdout=sys.stdout, stderr=sys.stderr, + files_preserve=getLogFileDescriptors(logging.getLogger()), umask=0o002, pidfile=lockfile.pidlockfile.PIDLockFile(args.daemon)) @@ -699,12 +740,15 @@ def do_daemon(args, state): running = False raise InterruptedError + def sighup(signum, frame): + logging.debug("SIGHUP") + context.signal_map = { signal.SIGTERM: sigterm, + signal.SIGHUP: sighup, } with context: - logging_setup(args) logging.info("calm daemon started, pid %d" % (os.getpid())) irk.irk("calm daemon started") @@ -900,6 +944,7 @@ def main(): setupdir_default = common_constants.HTDOCS vault_default = common_constants.VAULT logdir_default = '/sourceware/cygwin-staging/logs' + key_default = [common_constants.DEFAULT_GPG_KEY] parser = argparse.ArgumentParser(description='Upset replacement') parser.add_argument('-d', '--daemon', action='store', nargs='?', const=pidfile_default, help="daemonize (PIDFILE defaults to " + pidfile_default + ")", metavar='PIDFILE') @@ -907,7 +952,7 @@ def main(): parser.add_argument('--force', action='count', help="force regeneration of static htdocs content", default=0) parser.add_argument('--homedir', action='store', metavar='DIR', help="maintainer home directory (default: " + homedir_default + ")", default=homedir_default) parser.add_argument('--htdocs', action='store', metavar='DIR', help="htdocs output directory (default: " + htdocs_default + ")", default=htdocs_default) - parser.add_argument('--key', action='append', metavar='KEYID', help="key to use to sign setup.ini", default=[], dest='keys') + parser.add_argument('--key', action='append', metavar='KEYID', help="key to use to sign setup.ini", default=key_default, dest='keys') parser.add_argument('--logdir', action='store', metavar='DIR', help="log directory (default: '" + logdir_default + "')", default=logdir_default) parser.add_argument('--trustedmaint', action='store', metavar='NAMES', help="trusted package maintainers (default: '" + trustedmaint_default + "')", default=trustedmaint_default) parser.add_argument('--pkglist', action='store', metavar='FILE', help="package maintainer list (default: " + pkglist_default + ")", default=pkglist_default) @@ -930,6 +975,18 @@ def main(): if args.reports is None: args.reports = args.daemon + logging_setup(args) + + # find matching keygrips for keys + args.keygrips = [] + for k in args.keys: + details = utils.system('gpg2 --list-keys --with-keygrip --with-colons %s' % k) + for l in details.splitlines(): + if l.startswith('grp'): + grip = l.split(':')[9] + args.keygrips.append(grip) + logging.debug('key ID %s has keygrip %s' % (k, grip)) + state = CalmState() state.args = args @@ -944,7 +1001,6 @@ def main(): if args.daemon: do_daemon(args, state) else: - logging_setup(args) status = do_main(args, state) return status diff --git a/calm/common_constants.py b/calm/common_constants.py index 29b719b..82d7801 100644 --- a/calm/common_constants.py +++ b/calm/common_constants.py @@ -82,11 +82,16 @@ DEFAULT_KEEP_COUNT = 3 DEFAULT_KEEP_COUNT_TEST = 2 DEFAULT_KEEP_DAYS = 0 +# getting gpg to accurately tell you the default key is apparently impossible, +# so hardcode it here +DEFAULT_GPG_KEY = '56405CF6FCC81574682A5D561A698DE9E2E56300' + # different values to be used when we are not running on sourceware.org, but my # test system... if os.uname()[1] == 'tambora': EMAILS = 'debug' ALWAYS_BCC = '' + DEFAULT_GPG_KEY = '29E138393680DBA0' # package compressions PACKAGE_COMPRESSIONS = ['bz2', 'gz', 'lzma', 'xz', 'zst'] diff --git a/calm/utils.py b/calm/utils.py index 26e3655..6fb93b9 100644 --- a/calm/utils.py +++ b/calm/utils.py @@ -124,9 +124,11 @@ def system(args): for l in e.output.decode().splitlines(): logging.warning(l) logging.warning('%s exited %d' % (args.split()[0], e.returncode)) + return e.output.decode() else: for l in output.decode().splitlines(): logging.info(l) + return output.decode() #
reply other threads:[~2024-04-23 15:21 UTC|newest] Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20240423152110.9CF35385840D@sourceware.org \ --to=jturney@sourceware.org \ --cc=cygwin-apps-cvs@sourceware.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).