Statistics
| Branch: | Tag: | Revision:

root / image_creator / os_type / linux.py @ 121f3bc0

History | View | Annotate | Download (12.7 kB)

1 121f3bc0 Nikos Skalkotos
# -*- coding: utf-8 -*-
2 121f3bc0 Nikos Skalkotos
#
3 ae48a082 Nikos Skalkotos
# Copyright 2012 GRNET S.A. All rights reserved.
4 ae48a082 Nikos Skalkotos
#
5 ae48a082 Nikos Skalkotos
# Redistribution and use in source and binary forms, with or
6 ae48a082 Nikos Skalkotos
# without modification, are permitted provided that the following
7 ae48a082 Nikos Skalkotos
# conditions are met:
8 ae48a082 Nikos Skalkotos
#
9 ae48a082 Nikos Skalkotos
#   1. Redistributions of source code must retain the above
10 ae48a082 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
11 ae48a082 Nikos Skalkotos
#      disclaimer.
12 ae48a082 Nikos Skalkotos
#
13 ae48a082 Nikos Skalkotos
#   2. Redistributions in binary form must reproduce the above
14 ae48a082 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
15 ae48a082 Nikos Skalkotos
#      disclaimer in the documentation and/or other materials
16 ae48a082 Nikos Skalkotos
#      provided with the distribution.
17 ae48a082 Nikos Skalkotos
#
18 ae48a082 Nikos Skalkotos
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 ae48a082 Nikos Skalkotos
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 ae48a082 Nikos Skalkotos
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 ae48a082 Nikos Skalkotos
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 ae48a082 Nikos Skalkotos
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 ae48a082 Nikos Skalkotos
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 ae48a082 Nikos Skalkotos
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 ae48a082 Nikos Skalkotos
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 ae48a082 Nikos Skalkotos
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 ae48a082 Nikos Skalkotos
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 ae48a082 Nikos Skalkotos
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 ae48a082 Nikos Skalkotos
# POSSIBILITY OF SUCH DAMAGE.
30 ae48a082 Nikos Skalkotos
#
31 ae48a082 Nikos Skalkotos
# The views and conclusions contained in the software and
32 ae48a082 Nikos Skalkotos
# documentation are those of the authors and should not be
33 ae48a082 Nikos Skalkotos
# interpreted as representing official policies, either expressed
34 ae48a082 Nikos Skalkotos
# or implied, of GRNET S.A.
35 ae48a082 Nikos Skalkotos
36 121f3bc0 Nikos Skalkotos
"""This module hosts OS-specific code for Linux"""
37 121f3bc0 Nikos Skalkotos
38 f165adc0 Nikos Skalkotos
from image_creator.os_type.unix import Unix, sysprep
39 22a6d232 Nikos Skalkotos
40 a83f5185 Nikos Skalkotos
import re
41 3f70f242 Nikos Skalkotos
import time
42 aa2062ba Nikos Skalkotos
43 8c574358 Nikos Skalkotos
44 aa2062ba Nikos Skalkotos
class Linux(Unix):
45 88f83027 Nikos Skalkotos
    """OS class for Linux"""
46 e77e66a9 Nikos Skalkotos
    def __init__(self, rootdev, ghandler, output):
47 e77e66a9 Nikos Skalkotos
        super(Linux, self).__init__(rootdev, ghandler, output)
48 a83f5185 Nikos Skalkotos
        self._uuid = dict()
49 a83f5185 Nikos Skalkotos
        self._persistent = re.compile('/dev/[hsv]d[a-z][1-9]*')
50 a83f5185 Nikos Skalkotos
51 2dcd42b7 Nikos Skalkotos
    @sysprep(enabled=False)
52 2dcd42b7 Nikos Skalkotos
    def remove_user_accounts(self, print_header=True):
53 2dcd42b7 Nikos Skalkotos
        """Remove all user accounts with id greater than 1000"""
54 2dcd42b7 Nikos Skalkotos
55 2dcd42b7 Nikos Skalkotos
        if print_header:
56 2dcd42b7 Nikos Skalkotos
            self.out.output("Removing all user accounts with id greater than "
57 2dcd42b7 Nikos Skalkotos
                            "1000")
58 2dcd42b7 Nikos Skalkotos
59 2dcd42b7 Nikos Skalkotos
        if 'USERS' not in self.meta:
60 2dcd42b7 Nikos Skalkotos
            return
61 2dcd42b7 Nikos Skalkotos
62 2dcd42b7 Nikos Skalkotos
        # Remove users from /etc/passwd
63 2dcd42b7 Nikos Skalkotos
        passwd = []
64 2dcd42b7 Nikos Skalkotos
        removed_users = {}
65 2dcd42b7 Nikos Skalkotos
        metadata_users = self.meta['USERS'].split()
66 2dcd42b7 Nikos Skalkotos
        for line in self.g.cat('/etc/passwd').splitlines():
67 2dcd42b7 Nikos Skalkotos
            fields = line.split(':')
68 2dcd42b7 Nikos Skalkotos
            if int(fields[2]) > 1000:
69 2dcd42b7 Nikos Skalkotos
                removed_users[fields[0]] = fields
70 2dcd42b7 Nikos Skalkotos
                # remove it from the USERS metadata too
71 2dcd42b7 Nikos Skalkotos
                if fields[0] in metadata_users:
72 2dcd42b7 Nikos Skalkotos
                    metadata_users.remove(fields[0])
73 2dcd42b7 Nikos Skalkotos
            else:
74 2dcd42b7 Nikos Skalkotos
                passwd.append(':'.join(fields))
75 2dcd42b7 Nikos Skalkotos
76 2dcd42b7 Nikos Skalkotos
        self.meta['USERS'] = " ".join(metadata_users)
77 2dcd42b7 Nikos Skalkotos
78 2dcd42b7 Nikos Skalkotos
        # Delete the USERS metadata if empty
79 2dcd42b7 Nikos Skalkotos
        if not len(self.meta['USERS']):
80 2dcd42b7 Nikos Skalkotos
            del self.meta['USERS']
81 2dcd42b7 Nikos Skalkotos
82 2dcd42b7 Nikos Skalkotos
        self.g.write('/etc/passwd', '\n'.join(passwd) + '\n')
83 2dcd42b7 Nikos Skalkotos
84 2dcd42b7 Nikos Skalkotos
        # Remove the corresponding /etc/shadow entries
85 2dcd42b7 Nikos Skalkotos
        shadow = []
86 2dcd42b7 Nikos Skalkotos
        for line in self.g.cat('/etc/shadow').splitlines():
87 2dcd42b7 Nikos Skalkotos
            fields = line.split(':')
88 2dcd42b7 Nikos Skalkotos
            if fields[0] not in removed_users:
89 2dcd42b7 Nikos Skalkotos
                shadow.append(':'.join(fields))
90 2dcd42b7 Nikos Skalkotos
91 2dcd42b7 Nikos Skalkotos
        self.g.write('/etc/shadow', "\n".join(shadow) + '\n')
92 2dcd42b7 Nikos Skalkotos
93 2dcd42b7 Nikos Skalkotos
        # Remove the corresponding /etc/group entries
94 2dcd42b7 Nikos Skalkotos
        group = []
95 2dcd42b7 Nikos Skalkotos
        for line in self.g.cat('/etc/group').splitlines():
96 2dcd42b7 Nikos Skalkotos
            fields = line.split(':')
97 2dcd42b7 Nikos Skalkotos
            # Remove groups tha have the same name as the removed users
98 2dcd42b7 Nikos Skalkotos
            if fields[0] not in removed_users:
99 2dcd42b7 Nikos Skalkotos
                group.append(':'.join(fields))
100 2dcd42b7 Nikos Skalkotos
101 2dcd42b7 Nikos Skalkotos
        self.g.write('/etc/group', '\n'.join(group) + '\n')
102 2dcd42b7 Nikos Skalkotos
103 2dcd42b7 Nikos Skalkotos
        # Remove home directories
104 2dcd42b7 Nikos Skalkotos
        for home in [field[5] for field in removed_users.values()]:
105 2dcd42b7 Nikos Skalkotos
            if self.g.is_dir(home) and home.startswith('/home/'):
106 2dcd42b7 Nikos Skalkotos
                self.g.rm_rf(home)
107 2dcd42b7 Nikos Skalkotos
108 2dcd42b7 Nikos Skalkotos
    @sysprep()
109 2dcd42b7 Nikos Skalkotos
    def cleanup_passwords(self, print_header=True):
110 2dcd42b7 Nikos Skalkotos
        """Remove all passwords and lock all user accounts"""
111 2dcd42b7 Nikos Skalkotos
112 2dcd42b7 Nikos Skalkotos
        if print_header:
113 2dcd42b7 Nikos Skalkotos
            self.out.output("Cleaning up passwords & locking all user "
114 2dcd42b7 Nikos Skalkotos
                            "accounts")
115 2dcd42b7 Nikos Skalkotos
116 2dcd42b7 Nikos Skalkotos
        shadow = []
117 2dcd42b7 Nikos Skalkotos
118 2dcd42b7 Nikos Skalkotos
        for line in self.g.cat('/etc/shadow').splitlines():
119 2dcd42b7 Nikos Skalkotos
            fields = line.split(':')
120 2dcd42b7 Nikos Skalkotos
            if fields[1] not in ('*', '!'):
121 2dcd42b7 Nikos Skalkotos
                fields[1] = '!'
122 2dcd42b7 Nikos Skalkotos
123 2dcd42b7 Nikos Skalkotos
            shadow.append(":".join(fields))
124 2dcd42b7 Nikos Skalkotos
125 2dcd42b7 Nikos Skalkotos
        self.g.write('/etc/shadow', "\n".join(shadow) + '\n')
126 2dcd42b7 Nikos Skalkotos
127 f165adc0 Nikos Skalkotos
    @sysprep()
128 f165adc0 Nikos Skalkotos
    def fix_acpid(self, print_header=True):
129 d144e954 Nikos Skalkotos
        """Replace acpid powerdown action scripts to immediately shutdown the
130 d144e954 Nikos Skalkotos
        system without checking if a GUI is running.
131 9cbb5794 Nikos Skalkotos
        """
132 22a6d232 Nikos Skalkotos
133 3f70f242 Nikos Skalkotos
        if print_header:
134 e77e66a9 Nikos Skalkotos
            self.out.output('Fixing acpid powerdown action')
135 3f70f242 Nikos Skalkotos
136 3f70f242 Nikos Skalkotos
        powerbtn_action = '#!/bin/sh\n\nPATH=/sbin:/bin:/usr/bin\n' \
137 f99fe99d Nikos Skalkotos
                          'shutdown -h now "Power button pressed"\n'
138 3f70f242 Nikos Skalkotos
139 3f70f242 Nikos Skalkotos
        events_dir = '/etc/acpi/events'
140 3f70f242 Nikos Skalkotos
        if not self.g.is_dir(events_dir):
141 e77e66a9 Nikos Skalkotos
            self.out.warn("No acpid event directory found")
142 3f70f242 Nikos Skalkotos
            return
143 3f70f242 Nikos Skalkotos
144 3f70f242 Nikos Skalkotos
        event_exp = re.compile('event=(.+)', re.I)
145 3f70f242 Nikos Skalkotos
        action_exp = re.compile('action=(.+)', re.I)
146 121f3bc0 Nikos Skalkotos
        for events_file in self.g.readdir(events_dir):
147 121f3bc0 Nikos Skalkotos
            if events_file['ftyp'] != 'r':
148 3f70f242 Nikos Skalkotos
                continue
149 3f70f242 Nikos Skalkotos
150 121f3bc0 Nikos Skalkotos
            fullpath = "%s/%s" % (events_dir, events_file['name'])
151 3f70f242 Nikos Skalkotos
            event = ""
152 3f70f242 Nikos Skalkotos
            action = ""
153 3f70f242 Nikos Skalkotos
            for line in self.g.cat(fullpath).splitlines():
154 121f3bc0 Nikos Skalkotos
                match = event_exp.match(line)
155 121f3bc0 Nikos Skalkotos
                if match:
156 121f3bc0 Nikos Skalkotos
                    event = match.group(1)
157 3f70f242 Nikos Skalkotos
                    continue
158 121f3bc0 Nikos Skalkotos
                match = action_exp.match(line)
159 121f3bc0 Nikos Skalkotos
                if match:
160 121f3bc0 Nikos Skalkotos
                    action = match.group(1)
161 3f70f242 Nikos Skalkotos
                    continue
162 3f70f242 Nikos Skalkotos
163 c0f3abdc Nikos Skalkotos
            if event.strip() in ("button[ /]power", "button/power.*"):
164 3f70f242 Nikos Skalkotos
                if action:
165 3f70f242 Nikos Skalkotos
                    if not self.g.is_file(action):
166 e77e66a9 Nikos Skalkotos
                        self.out.warn("Acpid action file: %s does not exist" %
167 f99fe99d Nikos Skalkotos
                                      action)
168 3f70f242 Nikos Skalkotos
                        return
169 f99fe99d Nikos Skalkotos
                    self.g.copy_file_to_file(action,
170 f99fe99d Nikos Skalkotos
                                             "%s.orig.snf-image-creator-%d" %
171 f99fe99d Nikos Skalkotos
                                             (action, time.time()))
172 d144e954 Nikos Skalkotos
                    self.g.write(action, powerbtn_action)
173 3f70f242 Nikos Skalkotos
                    return
174 3f70f242 Nikos Skalkotos
                else:
175 f99fe99d Nikos Skalkotos
                    self.out.warn("Acpid event file %s does not contain and "
176 f99fe99d Nikos Skalkotos
                                  "action")
177 3f70f242 Nikos Skalkotos
                    return
178 3f70f242 Nikos Skalkotos
            elif event.strip() == ".*":
179 f99fe99d Nikos Skalkotos
                self.out.warn("Found action `.*'. Don't know how to handle "
180 f99fe99d Nikos Skalkotos
                              "this. Please edit `%s' image file manually to "
181 f99fe99d Nikos Skalkotos
                              "make the system immediatelly shutdown when an "
182 f99fe99d Nikos Skalkotos
                              "power button acpi event occures." %
183 f99fe99d Nikos Skalkotos
                              action.strip().split()[0])
184 3f70f242 Nikos Skalkotos
                return
185 3f70f242 Nikos Skalkotos
186 c0f3abdc Nikos Skalkotos
        self.out.warn("No acpi power button event found!")
187 c0f3abdc Nikos Skalkotos
188 f165adc0 Nikos Skalkotos
    @sysprep()
189 755e1885 Nikos Skalkotos
    def remove_persistent_net_rules(self, print_header=True):
190 9cbb5794 Nikos Skalkotos
        """Remove udev rules that will keep network interface names persistent
191 9cbb5794 Nikos Skalkotos
        after hardware changes and reboots. Those rules will be created again
192 9cbb5794 Nikos Skalkotos
        the next time the image runs.
193 9cbb5794 Nikos Skalkotos
        """
194 22a6d232 Nikos Skalkotos
195 3f70f242 Nikos Skalkotos
        if print_header:
196 e77e66a9 Nikos Skalkotos
            self.out.output('Removing persistent network interface names')
197 22a6d232 Nikos Skalkotos
198 9cbb5794 Nikos Skalkotos
        rule_file = '/etc/udev/rules.d/70-persistent-net.rules'
199 9cbb5794 Nikos Skalkotos
        if self.g.is_file(rule_file):
200 9cbb5794 Nikos Skalkotos
            self.g.rm(rule_file)
201 9cbb5794 Nikos Skalkotos
202 f165adc0 Nikos Skalkotos
    @sysprep()
203 4a2fd05c Nikos Skalkotos
    def remove_swap_entry(self, print_header=True):
204 4a2fd05c Nikos Skalkotos
        """Remove swap entry from /etc/fstab. If swap is the last partition
205 4a2fd05c Nikos Skalkotos
        then the partition will be removed when shrinking is performed. If the
206 4a2fd05c Nikos Skalkotos
        swap partition is not the last partition in the disk or if you are not
207 4a2fd05c Nikos Skalkotos
        going to shrink the image you should probably disable this.
208 4a2fd05c Nikos Skalkotos
        """
209 4a2fd05c Nikos Skalkotos
210 4a2fd05c Nikos Skalkotos
        if print_header:
211 e77e66a9 Nikos Skalkotos
            self.out.output('Removing swap entry from fstab')
212 4a2fd05c Nikos Skalkotos
213 4a2fd05c Nikos Skalkotos
        new_fstab = ""
214 4a2fd05c Nikos Skalkotos
        fstab = self.g.cat('/etc/fstab')
215 4a2fd05c Nikos Skalkotos
        for line in fstab.splitlines():
216 4a2fd05c Nikos Skalkotos
217 4a2fd05c Nikos Skalkotos
            entry = line.split('#')[0].strip().split()
218 4a2fd05c Nikos Skalkotos
            if len(entry) == 6 and entry[2] == 'swap':
219 4a2fd05c Nikos Skalkotos
                continue
220 4a2fd05c Nikos Skalkotos
221 4a2fd05c Nikos Skalkotos
            new_fstab += "%s\n" % line
222 4a2fd05c Nikos Skalkotos
223 4a2fd05c Nikos Skalkotos
        self.g.write('/etc/fstab', new_fstab)
224 4a2fd05c Nikos Skalkotos
225 4a2fd05c Nikos Skalkotos
    @sysprep()
226 755e1885 Nikos Skalkotos
    def use_persistent_block_device_names(self, print_header=True):
227 f165adc0 Nikos Skalkotos
        """Scan fstab & grub configuration files and replace all non-persistent
228 dcf9274b Vangelis Koukis
        device references with UUIDs.
229 9cbb5794 Nikos Skalkotos
        """
230 22a6d232 Nikos Skalkotos
231 3f70f242 Nikos Skalkotos
        if print_header:
232 f99fe99d Nikos Skalkotos
            self.out.output("Replacing fstab & grub non-persistent device "
233 dcf9274b Vangelis Koukis
                            "references")
234 22a6d232 Nikos Skalkotos
235 a83f5185 Nikos Skalkotos
        # convert all devices in fstab to persistent
236 a83f5185 Nikos Skalkotos
        persistent_root = self._persistent_fstab()
237 a83f5185 Nikos Skalkotos
238 a83f5185 Nikos Skalkotos
        # convert all devices in grub1 to persistent
239 a83f5185 Nikos Skalkotos
        self._persistent_grub1(persistent_root)
240 a83f5185 Nikos Skalkotos
241 9cbb5794 Nikos Skalkotos
    def _persistent_grub1(self, new_root):
242 121f3bc0 Nikos Skalkotos
        """Replaces non-persistent device name occurencies with persistent
243 121f3bc0 Nikos Skalkotos
        ones in GRUB1 configuration files.
244 121f3bc0 Nikos Skalkotos
        """
245 a83f5185 Nikos Skalkotos
        if self.g.is_file('/boot/grub/menu.lst'):
246 a83f5185 Nikos Skalkotos
            grub1 = '/boot/grub/menu.lst'
247 a83f5185 Nikos Skalkotos
        elif self.g.is_file('/etc/grub.conf'):
248 a83f5185 Nikos Skalkotos
            grub1 = '/etc/grub.conf'
249 a83f5185 Nikos Skalkotos
        else:
250 a83f5185 Nikos Skalkotos
            return
251 a83f5185 Nikos Skalkotos
252 a83f5185 Nikos Skalkotos
        self.g.aug_init('/', 0)
253 a83f5185 Nikos Skalkotos
        try:
254 a83f5185 Nikos Skalkotos
            roots = self.g.aug_match('/files%s/title[*]/kernel/root' % grub1)
255 a83f5185 Nikos Skalkotos
            for root in roots:
256 a83f5185 Nikos Skalkotos
                dev = self.g.aug_get(root)
257 121f3bc0 Nikos Skalkotos
                if not self._is_persistent(dev):
258 a83f5185 Nikos Skalkotos
                    # This is not always correct. Grub may contain root entries
259 a83f5185 Nikos Skalkotos
                    # for other systems, but we only support 1 OS per hard
260 a83f5185 Nikos Skalkotos
                    # disk, so this shouldn't harm.
261 a83f5185 Nikos Skalkotos
                    self.g.aug_set(root, new_root)
262 a83f5185 Nikos Skalkotos
        finally:
263 a83f5185 Nikos Skalkotos
            self.g.aug_save()
264 a83f5185 Nikos Skalkotos
            self.g.aug_close()
265 a83f5185 Nikos Skalkotos
266 a83f5185 Nikos Skalkotos
    def _persistent_fstab(self):
267 121f3bc0 Nikos Skalkotos
        """Replaces non-persistent device name occurencies in /etc/fstab with
268 121f3bc0 Nikos Skalkotos
        persistent ones.
269 121f3bc0 Nikos Skalkotos
        """
270 a83f5185 Nikos Skalkotos
        mpoints = self.g.mountpoints()
271 a83f5185 Nikos Skalkotos
        if len(mpoints) == 0:
272 a83f5185 Nikos Skalkotos
            pass  # TODO: error handling
273 a83f5185 Nikos Skalkotos
274 a83f5185 Nikos Skalkotos
        device_dict = dict([[mpoint, dev] for dev, mpoint in mpoints])
275 a83f5185 Nikos Skalkotos
276 a83f5185 Nikos Skalkotos
        root_dev = None
277 a83f5185 Nikos Skalkotos
        new_fstab = ""
278 a83f5185 Nikos Skalkotos
        fstab = self.g.cat('/etc/fstab')
279 a83f5185 Nikos Skalkotos
        for line in fstab.splitlines():
280 a83f5185 Nikos Skalkotos
281 a83f5185 Nikos Skalkotos
            line, dev, mpoint = self._convert_fstab_line(line, device_dict)
282 a83f5185 Nikos Skalkotos
            new_fstab += "%s\n" % line
283 a83f5185 Nikos Skalkotos
284 a83f5185 Nikos Skalkotos
            if mpoint == '/':
285 a83f5185 Nikos Skalkotos
                root_dev = dev
286 a83f5185 Nikos Skalkotos
287 a83f5185 Nikos Skalkotos
        self.g.write('/etc/fstab', new_fstab)
288 a83f5185 Nikos Skalkotos
        if root_dev is None:
289 a83f5185 Nikos Skalkotos
            pass  # TODO: error handling
290 a83f5185 Nikos Skalkotos
291 a83f5185 Nikos Skalkotos
        return root_dev
292 a83f5185 Nikos Skalkotos
293 a83f5185 Nikos Skalkotos
    def _convert_fstab_line(self, line, devices):
294 121f3bc0 Nikos Skalkotos
        """Replace non-persistent device names in an fstab line to their UUID
295 121f3bc0 Nikos Skalkotos
        equivalent
296 121f3bc0 Nikos Skalkotos
        """
297 a83f5185 Nikos Skalkotos
        orig = line
298 a83f5185 Nikos Skalkotos
        line = line.split('#')[0].strip()
299 a83f5185 Nikos Skalkotos
        if len(line) == 0:
300 a83f5185 Nikos Skalkotos
            return orig, "", ""
301 a83f5185 Nikos Skalkotos
302 a83f5185 Nikos Skalkotos
        entry = line.split()
303 a83f5185 Nikos Skalkotos
        if len(entry) != 6:
304 e77e66a9 Nikos Skalkotos
            self.out.warn("Detected abnormal entry in fstab")
305 a83f5185 Nikos Skalkotos
            return orig, "", ""
306 a83f5185 Nikos Skalkotos
307 a83f5185 Nikos Skalkotos
        dev = entry[0]
308 a83f5185 Nikos Skalkotos
        mpoint = entry[1]
309 a83f5185 Nikos Skalkotos
310 121f3bc0 Nikos Skalkotos
        if not self._is_persistent(dev):
311 a83f5185 Nikos Skalkotos
            if mpoint in devices:
312 121f3bc0 Nikos Skalkotos
                dev = "UUID=%s" % self._get_uuid(devices[mpoint])
313 a83f5185 Nikos Skalkotos
                entry[0] = dev
314 a83f5185 Nikos Skalkotos
            else:
315 a83f5185 Nikos Skalkotos
                # comment out the entry
316 a83f5185 Nikos Skalkotos
                entry[0] = "#%s" % dev
317 a83f5185 Nikos Skalkotos
            return " ".join(entry), dev, mpoint
318 a83f5185 Nikos Skalkotos
319 a83f5185 Nikos Skalkotos
        return orig, dev, mpoint
320 aa2062ba Nikos Skalkotos
321 121f3bc0 Nikos Skalkotos
    def _do_collect_metadata(self):
322 121f3bc0 Nikos Skalkotos
        """Collect metadata about the OS"""
323 121f3bc0 Nikos Skalkotos
        super(Linux, self)._do_collect_metadata()
324 121f3bc0 Nikos Skalkotos
        self.meta["USERS"] = " ".join(self._get_passworded_users())
325 121f3bc0 Nikos Skalkotos
326 121f3bc0 Nikos Skalkotos
        # Delete the USERS metadata if empty
327 121f3bc0 Nikos Skalkotos
        if not len(self.meta['USERS']):
328 121f3bc0 Nikos Skalkotos
            self.out.warn("No passworded users found!")
329 121f3bc0 Nikos Skalkotos
            del self.meta['USERS']
330 121f3bc0 Nikos Skalkotos
331 121f3bc0 Nikos Skalkotos
    def _get_passworded_users(self):
332 121f3bc0 Nikos Skalkotos
        """Returns a list of non-locked user accounts"""
333 121f3bc0 Nikos Skalkotos
        users = []
334 121f3bc0 Nikos Skalkotos
        regexp = re.compile('(\S+):((?:!\S+)|(?:[^!*]\S+)|):(?:\S*:){6}')
335 121f3bc0 Nikos Skalkotos
336 121f3bc0 Nikos Skalkotos
        for line in self.g.cat('/etc/shadow').splitlines():
337 121f3bc0 Nikos Skalkotos
            match = regexp.match(line)
338 121f3bc0 Nikos Skalkotos
            if not match:
339 121f3bc0 Nikos Skalkotos
                continue
340 121f3bc0 Nikos Skalkotos
341 121f3bc0 Nikos Skalkotos
            user, passwd = match.groups()
342 121f3bc0 Nikos Skalkotos
            if len(passwd) > 0 and passwd[0] == '!':
343 121f3bc0 Nikos Skalkotos
                self.out.warn("Ignoring locked %s account." % user)
344 121f3bc0 Nikos Skalkotos
            else:
345 121f3bc0 Nikos Skalkotos
                users.append(user)
346 121f3bc0 Nikos Skalkotos
347 121f3bc0 Nikos Skalkotos
        return users
348 121f3bc0 Nikos Skalkotos
349 121f3bc0 Nikos Skalkotos
    def _is_persistent(self, dev):
350 121f3bc0 Nikos Skalkotos
        """Checks if a device name is persistent."""
351 121f3bc0 Nikos Skalkotos
        return not self._persistent.match(dev)
352 121f3bc0 Nikos Skalkotos
353 121f3bc0 Nikos Skalkotos
    def _get_uuid(self, dev):
354 121f3bc0 Nikos Skalkotos
        """Returns the UUID corresponding to a device"""
355 121f3bc0 Nikos Skalkotos
        if dev in self._uuid:
356 121f3bc0 Nikos Skalkotos
            return self._uuid[dev]
357 121f3bc0 Nikos Skalkotos
358 121f3bc0 Nikos Skalkotos
        uuid = self.g.vfs_uuid(dev)
359 121f3bc0 Nikos Skalkotos
        assert len(uuid)
360 121f3bc0 Nikos Skalkotos
        self._uuid[dev] = uuid
361 121f3bc0 Nikos Skalkotos
        return uuid
362 121f3bc0 Nikos Skalkotos
363 aa2062ba Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :