def printswitch(clients, delay=0.5): if int(delay) < 0 or int(delay) > 2: delay = 1.0 time.sleep(delay) lfmt = '%s %s [%s,%s]' rfmt = '[%s,%s] %s %s' half = (MB.MAILBOX_MAX_SLOTS - 1) // 2 NSP = 32 lspaces = ' ' * NSP PRINT('\n%s ____ ____' % lspaces) notch = 'U' for i in range(1, half + 1): left = i right = MB.server_id - left # 74XX TTL try: ldesc = lspaces c = clients[left] pa = c.peerattrs pa['cclass'] = MB.cclass(left) ldesc += lfmt % (pa['cclass'], MB.nodename(left), pa['CID0'], pa['SID0']) except KeyError as e: pass try: c = clients[right] pa = c.peerattrs pa['cclass'] = MB.cclass(right) rdesc = rfmt % (pa['CID0'], pa['SID0'], pa['cclass'], MB.nodename(right)) except KeyError as e: rdesc = '' PRINT('%-s -|%1d %c %2d|- %s' % (ldesc[-NSP:], left, notch, right, rdesc)) notch = ' ' PRINT('%s =========' % lspaces)
def connectionLost(self, reason): '''Tell the other peers that this one has died.''' dirty = reason.check(TIError.ConnectionDone) is None status = 'Dirty' if dirty else 'Clean' verb = 'server shutdown' if self.SI.quitting else 'disconnect' self.SI.logmsg('%s %s of peer id %d' % (status, verb, self.id)) # For QEMU crashes and shutdowns (not the OS guest but QEMU itself). MB.clear_mailslot(self.id) if self.id in self.SI.clients: # Only if everything was completed del self.SI.clients[self.id] if self.SI.recycled and not self.SI.quitting: self.SI.recycled[self.id] = self else: try: for other_peer in self.SI.clients.values(): ivshmsg_send_one_msg(other_peer.transport.socket, self.id) for EN in self.EN_list: EN.cleanup() except Exception as e: self.SI.logmsg('Closing peer transports failed: %s' % str(e)) self.printswitch(self.SI.clients) if self.SI.quitting and not self.SI.clients: # last one exited self.SI.logmsg('Final client disconnected after "quit"') TIreactor.stop() # turn out the lights
def connectionLost(self, reason): print(reason.value) if reason.check(TIError.ConnectionDone) is None: # Dirty print('Client was probably interrupted or killed.') else: if self.quitting: print('Last interactive command was "quit".') else: print('The server was probably shut down.') MB.clear_mailslot(self.id) # In particular, nodename if TIreactor.running: # Stopped elsewhere on SIGINT TIreactor.stop()
def ServerCallback(vectorobj): requester_id = vectorobj.num requester_name = MB.nodename(requester_id) request = MB.retrieve(requester_id) SI = vectorobj.cbdata # Recover the appropriate requester proxy object which can die between # its interrupt and this callback. try: requester_proxy = SI.clients[requester_id] assert requester_proxy.SI is SI, 'Say WHAT?' assert requester_proxy.id == requester_id, 'WTF MF?' requester_proxy.requester_id = requester_id # Pedantic? # For QEMU/VM, this may be the first chance to grab this (if the # drivers hadn't come up before). Just get it fresh each time. requester_proxy.nodename = requester_name requester_proxy.cclass = MB.cclass(requester_id) requester_proxy.peerattrs['cclass'] = requester_proxy.cclass except KeyError as e: SI.logmsg('Disappeering act by %d' % requester_id) return # The object passed has two sets of data: # 1. Id/target information on where to send the response # 2. Peer attributes used in two requests: # readout to send them # overwrite if they're being sent by a PFM # I'm "if blah blah" in handle_request() but I should just strip the # peer attribute here (the server in this case). for the server, # requester_name and requester_proxy.nodename are the same # peer_attributes are from the server, not the proxy. # it's different for the client. ro = ResponseObject( this=SI, # has "my" (server) CID0, SID0, and cclass proxy=requester_proxy, # for certain server-only admin from_id=SI.id, to_doorbell=requester_proxy.EN_list[SI.id], logmsg=SI.logmsg, stdtrace=SI.stdtrace, verbose=SI.verbose) ret = handle_request(request, requester_name, ro) # ret is either True, False, or... if ret == 'dump': # Might be some other stuff, but finally ProtocolIVSHMSGServer.printswitch(SI.clients)
def doCommand(self, cmd, args=None): if cmd in ('h', 'help') or '?' in cmd: print('h[elp]\n\tThis message') print('d[ump]\n\tPrint status of all ports') print('q[uit]\n\tShut it all down') return True if cmd in ('d', 'dump'): if self.verbose > 1: PRINT('') for id, peer in self.SI.clients.items(): PRINT('%10s: %s' % (MB.nodename(id), peer.peerattrs)) if self.verbose > 2: PPRINT(vars(peer), stream=sys.stdout) self.printswitch(self.SI.clients, 0) return True if cmd in ('q', 'quit'): self.quitting = True # self == SI, remember? self.logmsg('Interactive command to "quit"') if self.clients: # Trigger lostConnection for c in self.clients.values(): c.transport.loseConnection() # Final callback exits else: TIreactor.stop() return False PRINT('Unrecognized command "%s", try "help"' % cmd) return True
def send_payload(payload, from_id, to_doorbell, reset_tracker=False, tag=None, tagCID=0, tagSID=0): global _next_tag, _tracker # PRINT('Send "%s" from %d to %s' % (payload, from_id, vars(to_doorbell))) if tag is not None: # zero-length string can trigger this payload += ',Tag=%d' % _next_tag _tagged[str(_next_tag)] = '%d.%d!%s|%s' % (tagCID, tagSID, payload, tag) _next_tag += 1 # Put the tracker on the end where it's easier to find if reset_tracker: _tracker = 0 _tracker += 1 payload += '%s%d' % (_TRACKER_TOKEN, _tracker) ret = MB.fill(from_id, payload) # True == no timeout, no stomp to_doorbell.ring() return ret
def ClientCallback(vectorobj): requester_id = vectorobj.num requester_name = MB.nodename(requester_id) request = MB.retrieve(requester_id) requester_obj = vectorobj.cbdata # print('Raw Req ID = %d\n%s' % (requester_id, vars(requester_obj))) # [dest][src] ro = ResponseObject( this=requester_obj, # has CID0, SID0, and cclass proxy=None, # I don't manage ever (for now) from_id=requester_obj.id, to_doorbell=requester_obj.id2EN_list[requester_id][ requester_obj.id], logmsg=requester_obj.logmsg, stdtrace=requester_obj.stdtrace, verbose=requester_obj.verbose, ) ret = handle_request(request, requester_name, ro)
def parse_target(caller_id, instr): '''Return a list even for one item for consistency with keywords ALL and OTHERS.''' try: tmp = (int(instr), ) if 1 <= tmp[0] <= MB.server_id: return tmp except TypeError as e: return None except ValueError as e: if instr.lower()[-6:] in ('server', 'switch'): return (MB.server_id, ) active_ids = MB.active_ids() for id in active_ids: if MB.slots[id].nodename == instr: return (id, ) if instr.lower() == 'all': # Includes caller_id return active_ids if instr.lower() == 'others': return active_ids.remove(caller_id) return None
def __init__(self, factory, args=None): '''"self" is a new client connection, not "me" the server. As such it is a proxy object for the other end of each switch "port". args is NOT passed on instantiation via connection.''' assert isinstance(factory, TIPServerFactory), 'arg0 not my Factory' shutdown_http_logging() self.isPFM = False # Am I one of many peer proxies? if self.SI is not None and args is None: self.create_new_peer_id() # Python client will quickly fix this. QEMU VM will eventually # modprobe, etc. self.peerattrs = { 'CID0': '0', 'SID0': '0', 'cclass': 'Driverless QEMU' } MB.slots[self.id].cclass = self.peerattrs['cclass'] return # This instance is voodoo from the first manual kick. Originally # the server had "other" tracking but now it's a "reserved instance" # which is used as the callback for events (ie, incoming mailslot # doorbell for each active peer proxy). cls = self.__class__ cls.SI = self # Reserve it and flesh it out. cls.verbose = args.verbose self.id = args.server_id assert self.id == MB.server_id, 'Server ID mismatch' self.quitting = False self.cclass = MB.cclass(self.id) self.logmsg = args.logmsg self.logerr = args.logerr self.stdtrace = sys.stderr self.smart = args.smart self.clients = OrderedDict() # Order probably not necessary self.recycled = {} if args.recycle else None # For the ResponseObject/request(). if self.smart: self.default_SID = 27 self.SID0 = self.default_SID self.CID0 = self.id * 100 self.isPFM = True else: self.default_SID = 0 self.SID0 = 0 self.CID0 = 0 # Non-standard addition to IVSHMEM server role: this server can be # interrupted and messaged to particpate in client activity. # This variable will get looped even if it's empty (silent mode). # Usually create eventfds for receiving messages in IVSHMSG and # set up a callback. This arming is not a race condition as any # peer for which this is destined has not yet been "listened/heard". self.EN_list = [] if not args.silent: self.EN_list = ivshmsg_event_notifier_list(MB.nEvents) # The actual client doing the sending needs to be fished out # via its "num" vector. for i, EN in enumerate(self.EN_list): EN.num = i tmp = EventfdReader(EN, self.ServerCallback, cls.SI) if i: # Skip mailslot 0, the globals "slot" tmp.start()
# Instead of this.app.run(), break it open and wait for # twister_server.py to finally invoke TIreactor.run() as all these # things use the default reactor. Note that self.app was assigned # durng the class-level scan/eval of this source file. See also # /usr/lib/python3/dist-packages/klein/app.py::run() s = TWserver.Site(self.app.resource()) TIreactor.listenTCP(port, s) if __name__ == '__main__': from twisted.python import log as TPlog # Deprecated from ivshmsg_mailbox import IVSHMSG_MailBox # These things are done explicitly in twisted_server.py fname = '/dev/shm/ivshmsg_mailbox' if len(sys.argv) < 2 else sys.argv[1] if not fname or fname[0] == '-': raise SystemExit('usage: %s [ /path/to/mailbox ]' % sys.argv[0]) print('Opening', fname) fd = os.open(fname, os.O_RDWR) mb = IVSHMSG_MailBox(fd=fd, client_id=99) tmp = MailBoxReSTAPI(mb) # This is done explicitly in twisted_server.py TPlog.startLogging(sys.stdout, setStdout=False) # This is done implicitly after protocol registration in full app. TIreactor.run()