new_security_install.py 6.66 KB
Newer Older
1 2
#!/usr/bin/env python

Joerg Jaspert's avatar
Joerg Jaspert committed
3 4 5 6 7 8 9
"""
Do whatever is needed to get a security upload released

@contact: Debian FTP Master <ftpmaster@debian.org>
@copyright: 2010 Joerg Jaspert <joerg@debian.org>
@license: GNU General Public License version 2 or later
"""
10 11 12 13 14 15

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

Joerg Jaspert's avatar
Joerg Jaspert committed
16 17 18 19
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
20 21 22

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
Joerg Jaspert's avatar
Joerg Jaspert committed
23 24 25 26
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

################################################################################

27 28 29

################################################################################

30 31
from __future__ import print_function

Joerg Jaspert's avatar
Joerg Jaspert committed
32 33 34 35
import os
import sys
import time
import apt_pkg
Joerg Jaspert's avatar
Joerg Jaspert committed
36 37
import errno
import fcntl
38
import subprocess
39

40
from daklib import daklog
41
from daklib import utils
Joerg Jaspert's avatar
Joerg Jaspert committed
42
from daklib.dbconn import *
43
from daklib.regexes import re_taint_free
Joerg Jaspert's avatar
Joerg Jaspert committed
44
from daklib.config import Config
45 46 47

Options = None
Logger = None
Joerg Jaspert's avatar
Joerg Jaspert committed
48
Queue = None
49 50
changes = []

51

Joerg Jaspert's avatar
Joerg Jaspert committed
52
def usage():
53
    print("""Usage: dak security-install [OPTIONS] changesfiles
Joerg Jaspert's avatar
Joerg Jaspert committed
54
Do whatever there is to do for a security release
55

Joerg Jaspert's avatar
Joerg Jaspert committed
56 57 58
    -h, --help                 show this help and exit
    -n, --no-action            don't commit changes
    -s, --sudo                 dont bother, used internally
59

60
""")
Joerg Jaspert's avatar
Joerg Jaspert committed
61
    sys.exit()
62 63 64 65


def spawn(command):
    if not re_taint_free.match(command):
66
        utils.fubar("Invalid character in \"%s\"." % (command))
67 68

    if Options["No-Action"]:
69
        print("[%s]" % (command))
70
    else:
71 72 73 74
        try:
            subprocess.check_output(command.split(), stderr=subprocess.STDOUT)
        except subprocess.CalledProcessError as e:
            utils.fubar("Invocation of '%s' failed:\n%s\n" % (command, e.output.rstrip()), e.returncode)
75 76 77 78 79 80 81 82

##################### ! ! ! N O T E ! ! !  #####################
#
# These functions will be reinvoked by semi-priveleged users, be careful not
# to invoke external programs that will escalate privileges, etc.
#
##################### ! ! ! N O T E ! ! !  #####################

83

84 85
def sudo(arg, fn, exit):
    if Options["Sudo"]:
86
        os.spawnl(os.P_WAIT, "/usr/bin/sudo", "/usr/bin/sudo", "-u", "dak", "-H",
87
                  "/usr/local/bin/dak", "new-security-install", "-" + arg)
88 89 90 91 92
    else:
        fn()
    if exit:
        quit()

93

94 95
def do_Approve():
    sudo("A", _do_Approve, True)
96 97


98
def _do_Approve():
99
    print("Locking unchecked")
100
    with os.fdopen(os.open('/srv/security-master.debian.org/lock/unchecked.lock', os.O_CREAT | os.O_RDWR), 'r') as lock_fd:
101 102 103 104 105 106
        while True:
            try:
                fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
                break
            except IOError as e:
                if e.errno in (errno.EACCES, errno.EAGAIN):
107
                    print("Another process keeping the unchecked lock, waiting.")
108 109 110 111 112
                    time.sleep(10)
                else:
                    raise

        # 1. Install accepted packages
113
        print("Installing accepted packages into security archive")
114 115
        for queue_name in ("embargoed",):
            spawn("dak process-policy {0}".format(queue_name))
116 117

    # 2. Run all the steps that are needed to publish the changed archive
118
    print("Doing loadsa stuff in the archive, will take time, please be patient")
Joerg Jaspert's avatar
Joerg Jaspert committed
119 120 121
    os.environ['configdir'] = '/srv/security-master.debian.org/dak/config/debian-security'
    spawn("/srv/security-master.debian.org/dak/config/debian-security/cronscript unchecked-dinstall")

122
    print("Triggering metadata export for packages.d.o and other consumers")
Joerg Jaspert's avatar
Joerg Jaspert committed
123
    spawn("/srv/security-master.debian.org/dak/config/debian-security/export.sh")
124

Joerg Jaspert's avatar
Joerg Jaspert committed
125 126
########################################################################
########################################################################
127

128

Joerg Jaspert's avatar
Joerg Jaspert committed
129 130 131
def main():
    global Options, Logger, Queue, changes
    cnf = Config()
132

Joerg Jaspert's avatar
Joerg Jaspert committed
133 134 135
    Arguments = [('h', "Help",      "Security::Options::Help"),
                 ('n', "No-Action", "Security::Options::No-Action"),
                 ('c', 'Changesfile', "Security::Options::Changesfile"),
Joerg Jaspert's avatar
Joerg Jaspert committed
136 137
                 ('s', "Sudo", "Security::Options::Sudo"),
                 ('A', "Approve", "Security::Options::Approve")
Joerg Jaspert's avatar
Joerg Jaspert committed
138
                 ]
139

Joerg Jaspert's avatar
Joerg Jaspert committed
140
    for i in ["Help", "No-Action", "Changesfile", "Sudo", "Approve"]:
141 142 143
        key = "Security::Options::%s" % i
        if key not in cnf:
            cnf[key] = ""
144

145
    changes_files = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv)
146

147
    Options = cnf.subtree("Security::Options")
Joerg Jaspert's avatar
Joerg Jaspert committed
148 149
    if Options['Help']:
        usage()
150

151
    changesfiles = {}
Joerg Jaspert's avatar
Joerg Jaspert committed
152 153 154
    for a in changes_files:
        if not a.endswith(".changes"):
            utils.fubar("not a .changes file: %s" % (a))
155
        changesfiles[a] = 1
Joerg Jaspert's avatar
Joerg Jaspert committed
156
    changes = changesfiles.keys()
157

Joerg Jaspert's avatar
Joerg Jaspert committed
158 159
    username = utils.getusername()
    if username != "dak":
160
        print("Non-dak user: %s" % username)
Joerg Jaspert's avatar
Joerg Jaspert committed
161 162 163 164 165 166
        Options["Sudo"] = "y"

    if Options["No-Action"]:
        Options["Sudo"] = ""

    if not Options["Sudo"] and not Options["No-Action"]:
167
        Logger = daklog.Logger("security-install")
Joerg Jaspert's avatar
Joerg Jaspert committed
168 169 170 171

    session = DBConn().session()

    # If we call ourselve to approve, we do just that and exit
172 173
    if Options["Approve"]:
        do_Approve()
Joerg Jaspert's avatar
Joerg Jaspert committed
174 175 176 177 178 179 180
        sys.exit()

    if len(changes) == 0:
        utils.fubar("Need changes files as arguments")

    # Yes, we could do this inside do_Approve too. But this way we see who exactly
    # called it (ownership of the file)
181

182
    acceptfiles = {}
183
    for change in changes:
184
        dbchange = get_dbchange(os.path.basename(change), session)
185
        # strip epoch from version
186
        version = dbchange.version
187
        version = version[(version.find(':') + 1):]
188 189
        # strip possible version from source (binNMUs)
        source = dbchange.source.split(None, 1)[0]
190 191
        acceptfilename = "%s/COMMENTS/ACCEPT.%s_%s" % (os.path.dirname(os.path.abspath(changes[0])), source, version)
        acceptfiles[acceptfilename] = 1
192

193
    print("Would create %s now and then go on to accept this package, if you allow me to." % (acceptfiles.keys()))
Joerg Jaspert's avatar
Joerg Jaspert committed
194 195
    if Options["No-Action"]:
        sys.exit(0)
196 197
    else:
        raw_input("Press Enter to continue")
198 199 200 201 202 203

    for acceptfilename in acceptfiles.keys():
        accept_file = file(acceptfilename, "w")
        accept_file.write("OK\n")
        accept_file.close()

Joerg Jaspert's avatar
Joerg Jaspert committed
204
    do_Approve()
205 206 207 208


if __name__ == '__main__':
    main()