1# 2# Copyright (C) 2018 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the 'License'); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an 'AS IS' BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16 17import fcntl 18import logging 19import os 20 21from host_controller import common 22 23 24class FileLock(object): 25 """Class for using files as a locking mechanism for devices. 26 27 This is for checking whether a certain device is running a job or not when 28 the automated self-update happens. 29 30 Attributes: 31 _devlock_dir: string, represent the home directory of the user. 32 _lock_fd: dict, maps serial number of the devices and file descriptor. 33 """ 34 35 def __init__(self, file_name=None, mode=None): 36 """Initializes the file lock managing class instance. 37 38 Args: 39 file_name: string, name of the file to be opened. Existing lock 40 files will be opened if given as None 41 mode: string, the mode argument to open() function. 42 """ 43 self._lock_fd = {} 44 self._devlock_dir = os.path.join( 45 os.path.expanduser("~"), common._DEVLOCK_DIR) 46 if not os.path.exists(self._devlock_dir): 47 os.mkdir(self._devlock_dir) 48 49 if file_name: 50 file_list = [file_name] 51 else: 52 file_list = [file_name for file_name in os.listdir(self._devlock_dir)] 53 logging.debug("file_list for file lock: %s", file_list) 54 55 for file_name in file_list: 56 if os.path.isfile(os.path.join(self._devlock_dir, file_name)): 57 self._OpenFile(file_name, mode) 58 59 def _OpenFile(self, serial, mode=None): 60 """Opens the given lock file and store the file descriptor to _lock_fd. 61 62 Args: 63 serial: string, serial number of a device. 64 mode: string, the mode argument to open() function. 65 Set to "w+" if given as None. 66 """ 67 if serial in self._lock_fd and self._lock_fd[serial]: 68 logging.info("Lock for the device %s already exists." % serial) 69 return 70 71 try: 72 if not mode: 73 mode = "w+" 74 self._lock_fd[serial] = open( 75 os.path.join(self._devlock_dir, serial), mode, 0) 76 except IOError as e: 77 logging.exception(e) 78 return False 79 80 def LockDevice(self, serial, suppress_lock_warning=False, block=False): 81 """Tries to lock the file corresponding to "serial". 82 83 Args: 84 serial: string, serial number of a device. 85 suppress_lock_warning: bool, True to suppress warning log output. 86 block: bool, True to block at the fcntl.lockf(), waiting for it 87 to be unlocked. 88 89 Returns: 90 True if successfully acquired the lock. False otherwise. 91 """ 92 if serial not in self._lock_fd: 93 ret = self._OpenFile(serial) 94 if ret == False: 95 return ret 96 97 try: 98 operation = fcntl.LOCK_EX 99 if not block: 100 operation |= fcntl.LOCK_NB 101 fcntl.lockf(self._lock_fd[serial], operation) 102 except IOError as e: 103 if not suppress_lock_warning: 104 logging.exception(e) 105 return False 106 107 return True 108 109 def UnlockDevice(self, serial): 110 """Releases the lock file corresponding to "serial". 111 112 Args: 113 serial: string, serial number of a device. 114 """ 115 if serial not in self._lock_fd: 116 logging.error("Lock for the device %s does not exist." % serial) 117 return False 118 119 fcntl.lockf(self._lock_fd[serial], fcntl.LOCK_UN) 120