# Distributed under the terms of the GNU General Public License v2 import io import sys import stat import socket import select import tempfile import threading from portage import os class EventsLogger(threading.Thread): def default_filter(eventname, filename, stage): return True def __init__(self, socket_dir="/tmp/", filter_proc=default_filter): threading.Thread.__init__(self) # init the Thread self.alive=False self.main_thread=threading.currentThread() self.socket_dir=socket_dir self.filter_proc=filter_proc self.socket_name=None self.socket_logger=None self.events={} try: socket_dir_name = tempfile.mkdtemp(dir=self.socket_dir, prefix="log_socket_") socket_name = os.path.join(socket_dir_name, 'socket') except OSError as e: return self.socket_name=socket_name #print(self.socket_name) try: socket_logger=socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) socket_logger.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) socket_logger.bind(self.socket_name) socket_logger.listen(64) except socket.error as e: return self.socket_logger=socket_logger try: # Allow connecting to socket for anyone os.chmod(socket_dir_name, stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR| stat.S_IROTH|stat.S_IWOTH|stat.S_IXOTH) os.chmod(socket_name, stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR| stat.S_IROTH|stat.S_IWOTH|stat.S_IXOTH) except OSError as e: return def run(self): """ Starts the log server """ self.alive=True self.listen_thread=threading.currentThread() clients={} epoll=select.epoll() epoll.register(self.socket_logger.fileno(), select.EPOLLIN) while self.alive: try: sock_events = epoll.poll(3) for fileno, sock_event in sock_events: if fileno == self.socket_logger.fileno(): ret = self.socket_logger.accept() if ret is None: pass else: (client,addr)=ret epoll.register(client.fileno(), select.EPOLLIN) clients[client.fileno()]=client elif sock_event & select.EPOLLIN: s=clients[fileno] record=s.recv(8192) if not record: # if connection was closed epoll.unregister(fileno) clients[fileno].close() del clients[fileno] continue #import pdb; pdb.set_trace() try: message=record.decode("utf8").split("\0") except UnicodeDecodeError: print("Bad message %s" % record) continue # continue #print(message) try: if message[4]=="ASKING": if self.filter_proc(message[1],message[2],message[3]): s.sendall(b"ALLOW\0") else: # TODO: log through portage infrastructure #print("Blocking an access to %s" % message[2]) s.sendall(b"DENY\0") else: eventname,filename,stage,result=message[1:5] if not stage in self.events: self.events[stage]=[{},{}] hashofsucesses=self.events[stage][0] hashoffailures=self.events[stage][1] if result=="DENIED": print("Blocking an access to %s" % filename) if result=="OK": if not filename in hashofsucesses: hashofsucesses[filename]=[False,False] readed_or_writed=hashofsucesses[filename] if eventname=="read": readed_or_writed[0]=True elif eventname=="write": readed_or_writed[1]=True elif result[0:3]=="ERR" or result=="DENIED": if not filename in hashoffailures: hashoffailures[filename]=[False,False] notfound_or_blocked=hashoffailures[filename] if result=="ERR/2": notfound_or_blocked[0]=True elif result=="DENIED": notfound_or_blocked[1]=True else: print("Error in logger module<->analyser protocol") except IndexError: print("IndexError while parsing %s" % record) except IOError as e: if e.errno!=4: # handling "Interrupted system call" errors raise # if main thread doesnt exists then exit if not self.main_thread.is_alive(): break epoll.unregister(self.socket_logger.fileno()) epoll.close() self.socket_logger.close() def stop(self): """ Stops the log server. Returns all events """ self.alive=False # Block the main thread until listener exists self.listen_thread.join() # We assume portage clears tmp folder, so no deleting a socket file # We assume that no new socket data will arrive after this moment return self.events