1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import json
6import logging
7import os
8import threading
9import time
10
11logger = logging.getLogger(__name__)
12
13
14class Blacklist(object):
15
16  def __init__(self, path):
17    self._blacklist_lock = threading.RLock()
18    self._path = path
19
20  def Read(self):
21    """Reads the blacklist from the blacklist file.
22
23    Returns:
24      A dict containing bad devices.
25    """
26    with self._blacklist_lock:
27      blacklist = dict()
28      if not os.path.exists(self._path):
29        return blacklist
30
31      try:
32        with open(self._path, 'r') as f:
33          blacklist = json.load(f)
34      except (IOError, ValueError) as e:
35        logger.warning('Unable to read blacklist: %s', str(e))
36        os.remove(self._path)
37
38      if not isinstance(blacklist, dict):
39        logger.warning('Ignoring %s: %s (a dict was expected instead)',
40                        self._path, blacklist)
41        blacklist = dict()
42
43      return blacklist
44
45  def Write(self, blacklist):
46    """Writes the provided blacklist to the blacklist file.
47
48    Args:
49      blacklist: list of bad devices to write to the blacklist file.
50    """
51    with self._blacklist_lock:
52      with open(self._path, 'w') as f:
53        json.dump(blacklist, f)
54
55  def Extend(self, devices, reason='unknown'):
56    """Adds devices to blacklist file.
57
58    Args:
59      devices: list of bad devices to be added to the blacklist file.
60      reason: string specifying the reason for blacklist (eg: 'unauthorized')
61    """
62    timestamp = time.time()
63    event_info = {
64        'timestamp': timestamp,
65        'reason': reason,
66    }
67    device_dicts = {device: event_info for device in devices}
68    logger.info('Adding %s to blacklist %s for reason: %s',
69                 ','.join(devices), self._path, reason)
70    with self._blacklist_lock:
71      blacklist = self.Read()
72      blacklist.update(device_dicts)
73      self.Write(blacklist)
74
75  def Reset(self):
76    """Erases the blacklist file if it exists."""
77    logger.info('Resetting blacklist %s', self._path)
78    with self._blacklist_lock:
79      if os.path.exists(self._path):
80        os.remove(self._path)
81