1#! /usr/bin/env python3
3"""
4Do whatever is needed to get a security upload released
6@contact: Debian FTP Master <ftpmaster@debian.org>
7@copyright: 2010 Joerg Jaspert <joerg@debian.org>
8@license: GNU General Public License version 2 or later
9"""
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation; either version 2 of the License, or
14# (at your option) any later version.
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
21# You should have received a copy of the GNU General Public License
22# along with this program; if not, write to the Free Software
23# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25################################################################################
28################################################################################
30import os
31import sys
32import time
33import apt_pkg
34import errno
35import fcntl
36import subprocess
38from daklib import daklog
39from daklib import utils
40from daklib.dbconn import *
41from daklib.regexes import re_taint_free
42from daklib.config import Config
44Options = None
45Logger = None
46Queue = None
47changes = []
50def usage():
51 print("""Usage: dak security-install [OPTIONS] changesfiles
52Do whatever there is to do for a security release
54 -h, --help show this help and exit
55 -n, --no-action don't commit changes
56 -s, --sudo dont bother, used internally
58""")
59 sys.exit()
62def spawn(command):
63 if not re_taint_free.match(command):
64 utils.fubar("Invalid character in \"%s\"." % (command))
66 if Options["No-Action"]:
67 print("[%s]" % (command))
68 else:
69 try:
70 subprocess.check_output(command.split(), stderr=subprocess.STDOUT)
71 except subprocess.CalledProcessError as e:
72 utils.fubar("Invocation of '%s' failed:\n%s\n" % (command, e.output.rstrip()), e.returncode)
74##################### ! ! ! N O T E ! ! ! #####################
75#
76# These functions will be reinvoked by semi-priveleged users, be careful not
77# to invoke external programs that will escalate privileges, etc.
78#
79##################### ! ! ! N O T E ! ! ! #####################
82def sudo(arg, fn, exit):
83 if Options["Sudo"]:
84 subprocess.check_call(
85 ["/usr/bin/sudo", "-u", "dak", "-H",
86 "/usr/local/bin/dak", "new-security-install", "-" + arg])
87 else:
88 fn()
89 if exit:
90 quit()
93def do_Approve():
94 sudo("A", _do_Approve, True)
97def _do_Approve():
98 print("Locking unchecked")
99 with os.fdopen(os.open('/srv/security-master.debian.org/lock/unchecked.lock', os.O_CREAT | os.O_RDWR), 'r') as lock_fd:
100 while True:
101 try:
102 fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
103 break
104 except OSError as e:
105 if e.errno in (errno.EACCES, errno.EAGAIN):
106 print("Another process keeping the unchecked lock, waiting.")
107 time.sleep(10)
108 else:
109 raise
111 # 1. Install accepted packages
112 print("Installing accepted packages into security archive")
113 for queue_name in ("embargoed",):
114 spawn("dak process-policy {0}".format(queue_name))
116 # 2. Run all the steps that are needed to publish the changed archive
117 print("Doing loadsa stuff in the archive, will take time, please be patient")
118 os.environ['configdir'] = '/srv/security-master.debian.org/dak/config/debian-security'
119 spawn("/srv/security-master.debian.org/dak/config/debian-security/cronscript unchecked-dinstall")
121 print("Triggering metadata export for packages.d.o and other consumers")
122 spawn("/srv/security-master.debian.org/dak/config/debian-security/export.sh")
124########################################################################
125########################################################################
128def main():
129 global Options, Logger, Queue, changes
130 cnf = Config()
132 Arguments = [('h', "Help", "Security::Options::Help"),
133 ('n', "No-Action", "Security::Options::No-Action"),
134 ('c', 'Changesfile', "Security::Options::Changesfile"),
135 ('s', "Sudo", "Security::Options::Sudo"),
136 ('A', "Approve", "Security::Options::Approve")
137 ]
139 for i in ["Help", "No-Action", "Changesfile", "Sudo", "Approve"]:
140 key = "Security::Options::%s" % i
141 if key not in cnf: 141 ↛ 139line 141 didn't jump to line 139, because the condition on line 141 was never false
142 cnf[key] = ""
144 changes_files = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv)
146 Options = cnf.subtree("Security::Options")
147 if Options['Help']: 147 ↛ 150line 147 didn't jump to line 150, because the condition on line 147 was never false
148 usage()
150 changesfiles = {}
151 for a in changes_files:
152 if not a.endswith(".changes"):
153 utils.fubar("not a .changes file: %s" % (a))
154 changesfiles[a] = 1
155 changes = list(changesfiles.keys())
157 username = utils.getusername()
158 if username != "dak":
159 print("Non-dak user: %s" % username)
160 Options["Sudo"] = "y"
162 if Options["No-Action"]:
163 Options["Sudo"] = ""
165 if not Options["Sudo"] and not Options["No-Action"]:
166 Logger = daklog.Logger("security-install")
168 session = DBConn().session()
170 # If we call ourselve to approve, we do just that and exit
171 if Options["Approve"]:
172 do_Approve()
173 sys.exit()
175 if len(changes) == 0:
176 utils.fubar("Need changes files as arguments")
178 # Yes, we could do this inside do_Approve too. But this way we see who exactly
179 # called it (ownership of the file)
181 acceptfiles = {}
182 for change in changes:
183 dbchange = get_dbchange(os.path.basename(change), session)
184 # strip epoch from version
185 version = dbchange.version
186 version = version[(version.find(':') + 1):]
187 # strip possible version from source (binNMUs)
188 source = dbchange.source.split(None, 1)[0]
189 acceptfilename = "%s/COMMENTS/ACCEPT.%s_%s" % (os.path.dirname(os.path.abspath(changes[0])), source, version)
190 acceptfiles[acceptfilename] = 1
192 print("Would create %s now and then go on to accept this package, if you allow me to." % (list(acceptfiles.keys())))
193 if Options["No-Action"]:
194 sys.exit(0)
195 else:
196 input("Press Enter to continue")
198 for acceptfilename in acceptfiles:
199 with open(acceptfilename, "w") as accept_file:
200 accept_file.write("OK\n")
202 do_Approve()
205if __name__ == '__main__':
206 main()