Coverage for dak/new_security_install.py: 24%

103 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2026-08-03 16:46 +0000

1""" 

2Do whatever is needed to get a security upload released 

3 

4@contact: Debian FTP Master <ftpmaster@debian.org> 

5@copyright: 2010 Joerg Jaspert <joerg@debian.org> 

6@license: GNU General Public License version 2 or later 

7""" 

8 

9# This program is free software; you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation; either version 2 of the License, or 

12# (at your option) any later version. 

13 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18 

19# You should have received a copy of the GNU General Public License 

20# along with this program; if not, write to the Free Software 

21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

22 

23################################################################################ 

24 

25 

26################################################################################ 

27 

28import errno 

29import fcntl 

30import os 

31import subprocess 

32import sys 

33import time 

34 

35import apt_pkg 

36 

37from daklib import daklog, utils 

38from daklib.config import Config 

39from daklib.dbconn import DBConn, get_dbchange 

40from daklib.regexes import re_taint_free 

41 

42Options: apt_pkg.Configuration 

43Logger: daklog.Logger 

44Queue = None 

45 

46 

47def usage(): 

48 print( 

49 """Usage: dak security-install [OPTIONS] changesfiles 

50Do whatever there is to do for a security release 

51 

52 -h, --help show this help and exit 

53 -n, --no-action don't commit changes 

54 -s, --sudo dont bother, used internally 

55 

56""" 

57 ) 

58 sys.exit() 

59 

60 

61def spawn(command): 

62 if not re_taint_free.match(command): 

63 utils.fubar('Invalid character in "%s".' % (command)) 

64 

65 if Options["No-Action"]: 

66 print("[%s]" % (command)) 

67 else: 

68 try: 

69 subprocess.check_output(command.split(), stderr=subprocess.STDOUT) 

70 except subprocess.CalledProcessError as e: 

71 utils.fubar( 

72 "Invocation of '%s' failed:\n%s\n" % (command, e.output.rstrip()), 

73 e.returncode, 

74 ) 

75 

76 

77##################### ! ! ! N O T E ! ! ! ##################### 

78# 

79# These functions will be reinvoked by semi-priveleged users, be careful not 

80# to invoke external programs that will escalate privileges, etc. 

81# 

82##################### ! ! ! N O T E ! ! ! ##################### 

83 

84 

85def sudo(arg: str, fn, exit): 

86 if Options["Sudo"]: 

87 subprocess.check_call( 

88 [ 

89 "/usr/bin/sudo", 

90 "-u", 

91 "dak", 

92 "-H", 

93 "/usr/local/bin/dak", 

94 "new-security-install", 

95 "-" + arg, 

96 ] 

97 ) 

98 else: 

99 fn() 

100 if exit: 

101 quit() 

102 

103 

104def do_Approve() -> None: 

105 sudo("A", _do_Approve, True) 

106 

107 

108def _do_Approve() -> None: 

109 print("Locking unchecked") 

110 with os.fdopen( 

111 os.open( 

112 "/srv/security-master.debian.org/lock/unchecked.lock", 

113 os.O_CREAT | os.O_RDWR, 

114 ), 

115 "r", 

116 ) as lock_fd: 

117 while True: 

118 try: 

119 fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB) 

120 break 

121 except OSError as e: 

122 if e.errno in (errno.EACCES, errno.EAGAIN): 

123 print("Another process keeping the unchecked lock, waiting.") 

124 time.sleep(10) 

125 else: 

126 raise 

127 

128 # 1. Install accepted packages 

129 print("Installing accepted packages into security archive") 

130 for queue_name in ("embargoed",): 

131 spawn("dak process-policy {0}".format(queue_name)) 

132 

133 # 2. Run all the steps that are needed to publish the changed archive 

134 print("Doing loadsa stuff in the archive, will take time, please be patient") 

135 os.environ["configdir"] = ( 

136 "/srv/security-master.debian.org/dak/config/debian-security" 

137 ) 

138 spawn( 

139 "/srv/security-master.debian.org/dak/config/debian-security/cronscript unchecked-dinstall" 

140 ) 

141 

142 print("Triggering metadata export for packages.d.o and other consumers") 

143 spawn("/srv/security-master.debian.org/dak/config/debian-security/export.sh") 

144 

145 

146######################################################################## 

147######################################################################## 

148 

149 

150def main(): 

151 global Options, Logger, Queue 

152 cnf = Config() 

153 

154 Arguments = [ 

155 ("h", "Help", "Security::Options::Help"), 

156 ("n", "No-Action", "Security::Options::No-Action"), 

157 ("c", "Changesfile", "Security::Options::Changesfile"), 

158 ("s", "Sudo", "Security::Options::Sudo"), 

159 ("A", "Approve", "Security::Options::Approve"), 

160 ] 

161 

162 for i in ["Help", "No-Action", "Changesfile", "Sudo", "Approve"]: 

163 key = "Security::Options::%s" % i 

164 if key not in cnf: 164 ↛ 162line 164 didn't jump to line 162 because the condition on line 164 was always true

165 cnf[key] = "" 

166 

167 changes_files = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv) # type: ignore[attr-defined] 

168 

169 Options = cnf.subtree("Security::Options") 

170 if Options["Help"]: 170 ↛ 173line 170 didn't jump to line 173 because the condition on line 170 was always true

171 usage() 

172 

173 changesfiles = set() 

174 for a in changes_files: 

175 if not a.endswith(".changes"): 

176 utils.fubar("not a .changes file: %s" % (a)) 

177 changesfiles.add(a) 

178 changes = list(changesfiles) 

179 

180 username = utils.getusername() 

181 if username != "dak": 

182 print("Non-dak user: %s" % username) 

183 Options["Sudo"] = "y" # type: ignore[index] 

184 

185 if Options["No-Action"]: 

186 Options["Sudo"] = "" # type: ignore[index] 

187 

188 if not Options["Sudo"] and not Options["No-Action"]: 

189 Logger = daklog.Logger("security-install") 

190 

191 session = DBConn().session() 

192 

193 # If we call ourselve to approve, we do just that and exit 

194 if Options["Approve"]: 

195 do_Approve() 

196 sys.exit() 

197 

198 if not changes: 

199 utils.fubar("Need changes files as arguments") 

200 

201 # Yes, we could do this inside do_Approve too. But this way we see who exactly 

202 # called it (ownership of the file) 

203 

204 acceptfiles = {} 

205 for change in changes: 

206 dbchange = get_dbchange(os.path.basename(change), session) 

207 if dbchange is None: 

208 utils.fubar(f"Changes file {change} not found in database.") 

209 # strip epoch from version 

210 version = dbchange.version 

211 version = version[(version.find(":") + 1) :] 

212 # strip possible version from source (binNMUs) 

213 source = dbchange.source.split(None, 1)[0] 

214 acceptfilename = "%s/COMMENTS/ACCEPT.%s_%s" % ( 

215 os.path.dirname(os.path.abspath(changes[0])), 

216 source, 

217 version, 

218 ) 

219 acceptfiles[acceptfilename] = 1 

220 

221 print( 

222 "Would create %s now and then go on to accept this package, if you allow me to." 

223 % (list(acceptfiles.keys())) 

224 ) 

225 if Options["No-Action"]: 

226 sys.exit(0) 

227 else: 

228 input("Press Enter to continue") 

229 

230 for acceptfilename in acceptfiles: 

231 with open(acceptfilename, "w") as accept_file: 

232 accept_file.write("OK\n") 

233 

234 do_Approve()