Пример #1
0
class Mouse:

    def __init__(self, conn, root):
        self.conn = conn
        self.root = root
        self.log = Log("mouse")

    def move(self, x=None, y=None, dx=0, dy=0, window=None):
        if window is None:
            window = self.root
        xcb_reply = self.conn.core.QueryPointer(window.wid).reply()
        new_x = xcb_reply.win_x
        new_y = xcb_reply.win_y
        if x:
            new_x = x
        if y:
            new_y = y
        if dx:
            new_x += dx
        if dy:
            new_y += dy
        self.log.debug("relocating to ({}, {})".format(new_x, new_y))
        self.conn.core.WarpPointerChecked(
            0, window.wid,  # src_window, dst_window
            0, 0,           # src_x, src_y
            0, 0,           # src_width, src_height
            new_x, new_y    # dest_x, dest_y
        )
Пример #2
0
class Worker(Thread):
  def __init__(self, timeline):
    super().__init__(daemon=True)
    self.timeline = timeline
    self.log = Log("worker %s" % self.name)

  def run(self):
    queue = self.timeline.outq
    schedule = self.timeline.schedule
    while True:
      c = queue.get()
      self.log.debug("running %s" % c)
      r, out = c._check()
      schedule(c)
Пример #3
0
class Worker(Thread):
  """ Get tasks that are ready to run and execute them. """
  def __init__(self, scheduler):
    super().__init__(daemon=True)
    self.scheduler = scheduler
    self.log = Log("worker %s" % self.name)

  def run(self):
    queue = self.scheduler.ready
    schedule = self.scheduler.schedule
    history  = self.scheduler.history
    while True:
      checker = queue.get()
      self.log.debug("running %s" % checker)
      r, out = checker._check()
      self.log.debug("result: %s %s" %(r, out))
      schedule(checker)
      history.appendleft((time.time(), r, out))
Пример #4
0
class Checker:
  last_checked = None
  last_status = None, "<no check were performed yet>"

  def __init__(self, interval=60, name="<no name>", descr=None):
    self.interval = interval
    self.name = name
    self.descr = descr
    self.statuses = deque(maxlen=6)
    self.log = Log("checker %s" % self.__class__.__name__)
    global checks; checks += [self]

  def _check(self):
    if self.last_checked:
      delta = time.time() - self.last_checked
      if delta > (self.interval+1):
        log.critical("behind schedule for %ss" % delta)
    self.last_status  =  self.check()
    self.last_checked = time.time()
    self.statuses += [self.last_status]
    return self.last_status

  def check(self):
    return ERR, "<this is a generic check>"

  def get_next_check(self):
    if not self.last_checked:
      self.log.debug("was never checked, requesting immediate check")
      return -1
    now = time.time()
    next_check = self.last_checked + self.interval
    if next_check < now:
      return now
    return next_check

  def __lt__(self, other):
    return True
Пример #5
0
class Scheduler(Thread):
  """ Schedules and executes tasks using threaded workers
  """
  def __init__(self, workers=5):
    super().__init__(daemon=True)
    self.lock = Lock()       # lock queue manipulations
    self.inq  = PriorityQueue()
    self.outq = Queue()      # fired checks to be executed
    self.timer = MyTimer(0)  # timer placeholder for .schedule() so it can call self.timer.cancel() during first time
    self.log = Log("scheduler")
    for i in range(workers):
      worker = Worker(self)
      worker.start()

  def schedule(self, checker):
    time = checker.get_next_check()
    with self.lock:
      self.inq.put((time, checker))
      self.timer.cancel()  # trigger recalculate because this task may go before pending

  def run(self):
    while True:
      t, c = self.inq.get()
      with self.lock:
        now = time.time()
        self.timer = MyTimer(t-now)
      try:
        self.timer.start()
        self.log.debug("sleeping for %s" % self.timer.interval)
        self.timer.join()
      except TimerCanceled:
        self.log.notice("timer aborted, recalculating timeouts")
        with self.lock:
          print(t,c)
          self.inq.put((t,c))
        continue
      self.outq.put(c)
Пример #6
0
Файл: snm.py Проект: kopchik/snm
class WPAMonitor(Thread):
    def __init__(self, ifname):
        self.log = Log("monitor")
        mon_path = "/tmp/wpa_mon_%s" % getpid()
        atexit.register(lambda: unlink(mon_path))
        server_path = "/var/run/wpa_supplicant/%s" % ifname
        self.log.debug("connecting to %s" % server_path)
        self.socket = socket(AF_UNIX, SOCK_DGRAM)
        self.socket.bind(mon_path)
        self.socket.connect(server_path)
        super().__init__(daemon=True)

    def run(self):
        self.socket.send(b"AUTOSCAN periodic:10")
        self.socket.send(b"AP_SCAN 1")
        self.socket.send(b"ATTACH")

        while True:
            try:
                data = self.socket.recv(65535).strip().decode('ascii',
                                                              'ignore')
                self.log.debug("got %s" % data)
                if data == 'OK':
                    continue
                if data == 'FAIL':
                    raise Exception("Failure detected")
                mask, evtype = data.split('>', 1)
                if evtype == 'CTRL-EVENT-SCAN-RESULTS':
                    print("scan results")
                    for cb in events['scan_results']:
                        cb()
                else:
                    self.log.info("unknown event %s" % data)
            except Exception as e:
                self.log.critical(e)
                sys.exit(e)
Пример #7
0
Файл: snm.py Проект: kopchik/snm
class WPAClient:
    def __init__(self, ifname):
        self.log = Log("WPA %s" % ifname)
        client_path = "/tmp/wpa_client_%s" % getpid()
        atexit.register(lambda: unlink(client_path))
        server_path = "/var/run/wpa_supplicant/%s" % ifname
        self.socket = socket(AF_UNIX, SOCK_DGRAM)
        self.socket.bind(client_path)
        self.log.debug("using %s wpa socket..." % server_path)
        self.socket.connect(server_path)
        self.lock = Lock()

    def send(self, msg):
        self.log.debug("sending: %s" % msg)
        if isinstance(msg, str):
            msg = msg.encode('ascii', errors='ignore')
        self.socket.send(msg)

    def recv(self, bufsize=65535):
        r = self.socket.recv(bufsize)
        return r.strip().decode('ascii', errors='ignore')

    def run(self, cmd, check=True):
        with self.lock:
            self.send(cmd)
            r = self.recv()
            self.log.debug("received: %s" % r)
            if check:
                assert r not in ['FAIL', 'UNKNOWN COMMAND']
        return r

    def status(self):
        raw_status = self.run("STATUS", check=False)
        return parse_status(raw_status)

    def scan_results(self):
        result = []
        raw_results = self.run("SCAN_RESULTS")
        for line in raw_results.splitlines()[1:]:
            bssid, freq, signal, flags, ssid = line.split()
            r = Struct(ssid=ssid,
                       signal=signal,
                       bssid=bssid,
                       freq=freq,
                       flags=flags)
            result.append(r)
        return result

    def connect(self, network):
        nid = self.run("ADD_NETWORK")
        for cmd in network.wpacfg():
            self.run(s("SET_NETWORK ${nid} ${cmd}"))
        self.run(s("SELECT_NETWORK ${nid}"))
        self.run(s("ENABLE_NETWORK ${nid}"))
Пример #8
0
class WM:
    """
        Provides basic building blocks to make a window manager.
        It hides many dirty details about XCB. It was intended
        to provide minimum functionality, the rest supposed to
        be implemented by user in configuration file.
    """
    root = None   # type: Window
    atoms = None  # type: AtomVault

    def __init__(self, display=None, desktops=None, loop=None):
        self.log = Log("WM")
        # INIT SOME BASIC STUFF
        self.hook = Hook()
        self.windows = {}  # mapping between window id and Window
        self.win2desk = {}

        if not display:
            display = os.environ.get("DISPLAY")

        try:
            self._conn = xcffib.connect(display=display)
        except xcffib.ConnectionException:
            sys.exit("cannot connect to %s" % display)

        self.atoms = AtomVault(self._conn)
        self.desktops = desktops or [Desktop()]
        self.cur_desktop = self.desktops[0]
        self.cur_desktop.show()

        # CREATE ROOT WINDOW
        xcb_setup = self._conn.get_setup()
        xcb_screens = [i for i in xcb_setup.roots]
        self.xcb_default_screen = xcb_screens[self._conn.pref_screen]
        root_wid = self.xcb_default_screen.root
        self.root = Window(self, wid=root_wid, atoms=self.atoms, mapped=True)
        self.windows[root_wid] = self.root
#        for desktop in self.desktops:
#            desktop.windows.append(self.root)

        self.root.set_attr(
            eventmask=(
                  EventMask.StructureNotify
                | EventMask.SubstructureNotify
                | EventMask.FocusChange
                # | EventMask.SubstructureRedirect
                | EventMask.EnterWindow
                # | EventMask.LeaveWindow
                # | EventMask.PropertyChange
                | EventMask.OwnerGrabButton
            )
        )

        # INFORM X WHICH FEATURES WE SUPPORT
        self.root.props[self.atoms._NET_SUPPORTED] = [self.atoms[a] for a in SUPPORTED_ATOMS]

        # PRETEND TO BE A WINDOW MANAGER
        supporting_wm_check_window = self.create_window(-1, -1, 1, 1)
        supporting_wm_check_window.props['_NET_WM_NAME'] = "SWM"
        self.root.props['_NET_SUPPORTING_WM_CHECK'] = supporting_wm_check_window.wid
        self.root.props['_NET_NUMBER_OF_DESKTOPS'] = len(self.desktops)
        self.root.props['_NET_CURRENT_DESKTOP'] = 0

        # TODO: set cursor

        # EVENTS THAT HAVE LITTLE USE FOR US...
        self.ignoreEvents = {
            "KeyRelease",
            "ReparentNotify",
            # "CreateNotify",
            # DWM handles this to help "broken focusing windows".
            # "MapNotify",
            "ConfigureNotify",
            "LeaveNotify",
            "FocusOut",
            "FocusIn",
            "NoExposure",
        }
        # KEYBOARD
        self.kbd = Keyboard(xcb_setup, self._conn)
        self.mouse = Mouse(conn=self._conn, root=self.root)

        # FLUSH XCB BUFFER
        self.xsync()    # apply settings
        # the event loop is not yet there, but we might have some pending
        # events...
        self._xpoll()
        # TODO: self.grabMouse

        # NOW IT'S TIME TO GET PHYSICAL SCREEN CONFIGURATION
        self.xrandr = Xrandr(root=self.root, conn=self._conn)

        # TODO: self.update_net_desktops()

        # SETUP EVENT LOOP
        if not loop:
            loop = asyncio.new_event_loop()
        self._eventloop = loop
        self._eventloop.add_signal_handler(signal.SIGINT, self.stop)
        self._eventloop.add_signal_handler(signal.SIGTERM, self.stop)
        self._eventloop.add_signal_handler(signal.SIGCHLD, self.on_sigchld)
        self._eventloop.set_exception_handler(
            lambda loop, ctx: self.log.error(
                "Got an exception in {}: {}".format(loop, ctx))
        )
        fd = self._conn.get_file_descriptor()
        self._eventloop.add_reader(fd, self._xpoll)

        # HANDLE STANDARD EVENTS
        self.hook.register("MapRequest", self.on_map_request)
        self.hook.register("MapNotify", self.on_map_notify)
        self.hook.register("UnmapNotify", self.on_window_unmap)
        self.hook.register("KeyPress", self.on_key_press)
        # self.hook.register("KeyRelease", self.on_key_release)
        # self.hook.register("CreateNotify", self.on_window_create)
        self.hook.register("PropertyNotify", self.on_property_notify)
        self.hook.register("ClientMessage", self.on_client_message)
        self.hook.register("DestroyNotify", self.on_window_destroy)
        self.hook.register("EnterNotify", self.on_window_enter)
        self.hook.register("ConfigureRequest", self.on_configure_window)
        self.hook.register("MotionNotify", self.on_mouse_event)
        self.hook.register("ButtonPress", self.on_mouse_event)
        self.hook.register("ButtonRelease", self.on_mouse_event)

    def on_property_notify(self, evname, xcb_event):
        # TODO: messy ugly code
        wid = xcb_event.window
        atom = self.atoms.get_name(xcb_event.atom)
        # window = self.windows.get(wid, Window(wm=self, wid=wid, mapped=True))
        self.log.error("PropertyNotify: %s" % atom)
        run_("xprop -id %s %s" % (wid, atom))

    # TODO: dirty code, relocate to config
    def on_client_message(self, evname, xcb_event):
        self.log.error(dir(xcb_event))
        data = xcb_event.data
        resp_type = self.atoms.get_name(xcb_event.response_type)
        type = self.atoms.get_name(xcb_event.type)
        wid = xcb_event.window
        self.log.error("client message: resp_type={resp_type} window={wid} type={type} data={data}"  \
                       .format(**locals()))
        # 'bufsize', 'data', 'format', 'pack', 'response_type', 'sequence', 'synthetic', 'type', 'window']

        if type == '_NET_ACTIVE_WINDOW':
            window = self.windows[wid]
            window.rise()
            self.focus_on(window)

    def on_sigchld(self):
        """ Rip orphans. """
        while True:
            try:
                pid, status = os.waitpid(-1, os.WNOHANG)
                if (pid, status) == (0, 0):
                    # no child to rip
                    break
                self.log.notice("ripped child PID=%s" % pid)
            except ChildProcessError:
                break

    def on_map_request(self, evname, xcb_event):
        """ Map request is a request to draw the window on screen. """
        wid = xcb_event.window
        if wid not in self.windows:
            window = self.on_new_window(wid)
        else:
            window = self.windows[wid]
            self.log.on_map_request.debug("map request for %s" % window)
        window.show()
        if window.above_all:
            window.rise()
        if window.can_focus:
            window.focus()

    def on_new_window(self, wid):
        """ Registers new window. """
        window = Window(wm=self, wid=wid, atoms=self.atoms, mapped=True)
        # call configuration hood first
        # to setup attributes like 'sticky'
        self.hook.fire("new_window", window)
        self.log.on_new_window.debug(
            "new window is ready: %s" % window)
        self.windows[wid] = window
        self.win2desk[window] = self.cur_desktop
        if window.sticky:
            for desktop in self.desktops:
                desktop.add(window)
        else:
            self.cur_desktop.windows.append(window)
        return window

    def on_map_notify(self, evname, xcb_event):
        wid = xcb_event.window
        if wid not in self.windows:
            window = self.on_new_window(wid)
        else:
            window = self.windows[wid]
        window.mapped = True

        if window.above_all:
            window.rise()
        # if window.can_focus:
        #     window.focus()

        self.log.on_map_notify.debug("map notify for %s" % window)

    def on_window_unmap(self, evname, xcb_event):
        wid = xcb_event.window
        if wid not in self.windows:
            return
        window = self.windows[wid]
        window.mapped = False
        self.hook.fire("window_unmap", window)

    def on_window_destroy(self, evname, xcb_event):
        wid = xcb_event.window
        if wid not in self.windows:
            return

        window = self.windows[wid]
        assert isinstance(window, Window), "it's not a window: %s (%s)" % (
            window, type(window))

        for desktop in self.desktops:
            try:
                desktop.windows.remove(window)
                self.log.debug("%s removed from %s" % (self, desktop))
            except ValueError:
                pass
        del self.windows[wid]
        if window in self.win2desk:
            del self.win2desk[window]

    def on_window_enter(self, evname, xcb_event):
        wid = xcb_event.event
        if wid not in self.windows:
            # self.log.on_window_enter.error("no window with wid=%s" % wid)
            self.hook.fire("unknown_window", wid)
            return
        window = self.windows[wid]
        # self.log.on_window_enter("window_enter: %s %s" % (wid, window))
        self.hook.fire("window_enter", window)

    def grab_key(self, modifiers, key, owner_events=False, window=None):
        """ Intercept this key when it is pressed. If owner_events=False then
            the window in focus will not receive it. This is useful from WM hotkeys.
        """
        # TODO: check if key already grabbed?
        # Here is how X works with keys:
        # key => keysym => keycode
        # where `key' is something like 'a', 'b' or 'Enter',
        # `keysum' is what should be written on they key cap (physical keyboard)
        # and `keycode' is a number reported by the keyboard when the key is pressed.
        # Modifiers are keys like Shift, Alt, Win and some other buttons.
        self.log.grab_key.debug("intercept keys: %s %s" % (modifiers, key))

        if window is None:
            window = self.root

        keycode = self.kbd.key_to_code(key)
        modmask = get_modmask(modifiers)  # TODO: move to Keyboard
        event = ("on_key_press", modmask, keycode)
        pointer_mode = xproto.GrabMode.Async
        keyboard_mode = xproto.GrabMode.Async
        self._conn.core.GrabKey(
            owner_events,
            window.wid,
            modmask,
            keycode,
            pointer_mode,
            keyboard_mode
        )
        self.flush()  # TODO: do we need this?
        return event

    def on_key_press(self, evname, xcb_event):
        # TODO: ignore capslock, scrolllock and other modifiers?
        modmap = xcb_event.state
        keycode = xcb_event.detail
        event = ("on_key_press", modmap, keycode)
        self.hook.fire(event)

    def on_key_release(self, evname, xcb_event):
        modmap = xcb_event.state
        keycode = xcb_event.detail
        event = ("on_key_release", modmap, keycode)
        self.hook.fire(event)

    def grab_mouse(self, modifiers, button, owner_events=False, window=None):
        # http://www.x.org/archive/X11R7.7/doc/man/man3/xcb_grab_button.3.xhtml
        wid = (window or self.root).wid
        event_mask = xcffib.xproto.EventMask.ButtonPress |    \
            xcffib.xproto.EventMask.ButtonRelease |  \
            xcffib.xproto.EventMask.Button1Motion
        modmask = get_modmask(modifiers)
        pointer_mode = xproto.GrabMode.Async      # I don't know what it is
        keyboard_mode = xproto.GrabMode.Async     # do not block other keyboard events
        confine_to = xcffib.xproto.Atom._None     # do not restrict cursor movements
        cursor = xcffib.xproto.Atom._None         # do not change cursor
        event = ("on_mouse", modmask, button)     # event to be used in hooks

        self._conn.core.GrabButton(
            owner_events,
            wid,
            event_mask,
            pointer_mode,
            keyboard_mode,
            confine_to,
            cursor,
            button,
            modmask,
        )
        self.flush()  # TODO: do we need this?
        return event

    def hotkey(self, keys, cmd):
        """ Setup hook to launch a command on specific hotkeys. """
        @self.hook(self.grab_key(*keys))
        def cb(event):
            run_(cmd)

    def focus_on(self, window, warp=False):
        """ Focuses on given window. """
        self.cur_desktop.focus_on(window, warp)
        self.root.set_prop('_NET_ACTIVE_WINDOW', window.wid)

    def switch_to(self, desktop: Desktop):
        """ Switches to another desktop. """
        if isinstance(desktop, int):
            desktop = self.desktops[desktop]
        if self.cur_desktop == desktop:
            self.log.notice("attempt to switch to the same desktop")
            return
        self.log.debug("switching from {} to {}".format(
            self.cur_desktop, desktop))
        self.cur_desktop.hide()
        self.cur_desktop = desktop
        self.cur_desktop.show()
        # TODO: move this code to Desktop.show()
        self.root.props[self.atom._NET_CURRENT_DESKTOP] = desktop.id

    def relocate_to(self, window: Window, to_desktop: Desktop):
        """ Relocates window to a specific desktop. """
        if window.sticky:
            self.log.debug(
                "%s is meant to be on all desktops, cannot relocate to specific one" % window)
            return

        from_desktop = self.cur_desktop

        if from_desktop == to_desktop:
            self.log.debug(
                "no need to relocate %s because remains on the same desktop" % window)
            return

        from_desktop.remove(window)
        to_desktop.add(window)

    def on_mouse_event(self, evname, xcb_event):
        """evname is one of ButtonPress, ButtonRelease or MotionNotify."""
        # l = [(attr, getattr(xcb_event, attr)) for attr in sorted(dir(xcb_event)) if not attr.startswith('_')]
        # print(evname)
        # print(l)
        modmask = xcb_event.state & 0xff  # TODO: is the mask correct?
        if evname == 'MotionNotify':
            button = 1  # TODO
        else:
            button = xcb_event.detail

        event = ('on_mouse', modmask, button)
        # print(event)
        self.hook.fire(event, evname, xcb_event)

    def on_configure_window(self, _, event):
        # This code is so trivial that I just took it from fpwm as is :)
        values = []
        if event.value_mask & ConfigWindow.X:
            values.append(event.x)
        if event.value_mask & ConfigWindow.Y:
            values.append(event.y)
        if event.value_mask & ConfigWindow.Width:
            values.append(event.width)
        if event.value_mask & ConfigWindow.Height:
            values.append(event.height)
        if event.value_mask & ConfigWindow.BorderWidth:
            values.append(event.border_width)
        if event.value_mask & ConfigWindow.Sibling:
            values.append(event.sibling)
        if event.value_mask & ConfigWindow.StackMode:
            values.append(event.stack_mode)
        self._conn.core.ConfigureWindow(event.window, event.value_mask, values)

    def create_window(self, x, y, width, height):
        """ Create a window. Right now only used for initialization, see __init__. """
        wid = self._conn.generate_id()
        self._conn.core.CreateWindow(
            self.xcb_default_screen.root_depth,
            wid,
            self.xcb_default_screen.root,
            x, y, width, height, 0,
            WindowClass.InputOutput,
            self.xcb_default_screen.root_visual,
            CW.BackPixel | CW.EventMask,
            [
                self.xcb_default_screen.black_pixel,
                EventMask.StructureNotify | EventMask.Exposure
            ]
        )
        return Window(self, wid=wid, atoms=self.atoms)

    def scan(self, focus=True):
        """ Gets all windows in the system. """
        self.log.debug("performing scan of all mapped windows")
        q = self._conn.core.QueryTree(self.root.wid).reply()
        for wid in q.children:
            # attrs=self._conn.core.GetWindowAttributes(wid).reply()
            # print(attrs, type(attrs))
            # if attrs.map_state == xproto.MapState.Unmapped:
            #    self.log.scan.debug(
            #        "window %s is not mapped, skipping" % wid)  # TODO
            #    continue
            if wid not in self.windows:
                self.on_new_window(wid)
        self.log.scan.info("the following windows are active: %s" %
                           sorted(self.windows.values()))

        if focus:
            windows = sorted(self.windows.values())
            windows = list(filter(lambda w: w != self.root and not w.skip, windows))
            if windows:
                # on empty desktop there is nothing to focus on
                self.cur_desktop.focus_on(windows[-1], warp=True)

    def finalize(self):
        """ This code is run when event loop is terminated. """
        pass  # currently nothing to do here

    def flush(self):
        """ Force pending X request to be sent.
            By default XCB aggressevly buffers for performance reasons. """
        return self._conn.flush()

    def xsync(self):
        """ Flush XCB queue and wait till it is processed by X server. """
        # The idea here is that pushing an innocuous request through the queue
        # and waiting for a response "syncs" the connection, since requests are
        # serviced in order.
        self._conn.core.GetInputFocus().reply()

    def stop(self, xserver_dead=False):
        """ Stop WM to quit. """
        self.hook.fire("on_exit")
        # display all hidden windows
        try:
            if not xserver_dead:
                for window in self.windows.values():
                    window.show()
                self.xsync()
        except Exception as err:
            self.log.stop.error("error on stop: %s" % err)
        self.log.stop.debug("stopping event loop")
        self._eventloop.stop()

    def replace(self, execv_args):
        self.log.notice("replacing current process with %s" % (execv_args,))
        self.stop()
        import os
        os.execv(*execv_args)

    def loop(self):
        """ DITTO """
        self.scan()
        try:
            self._eventloop.run_forever()
        finally:
            self.finalize()

    def _xpoll(self):
        """ Fetch incomming events (if any) and call hooks. """

        # OK, kids, today I'll teach you how to write reliable enterprise
        # software! You just catch all the exceptions in the top-level loop
        # and ignore them. No, I'm kidding, these exceptions are no use
        # for us because we don't care if a window cannot be drawn or something.
        # We actually only need to handle just a few events and ignore the rest.
        # Exceptions happen because of the async nature of X.

        while True:
            try:
                xcb_event = self._conn.poll_for_event()
                if not xcb_event:
                    break
                evname = xcb_event.__class__.__name__
                if evname.endswith("Event"):
                    evname = evname[:-5]
                if evname in self.ignoreEvents:
                    self.log._xpoll.info("ignoring %s" % xcb_event)
                    continue
                self.log._xpoll.critical("got %s %s" % (evname, xcb_event))
                self.hook.fire(evname, xcb_event)
                self.flush()  # xcb doesn't flush implicitly
            except (WindowError, AccessError, DrawableError):
                self.log.debug("(minor exception)")
            except Exception as e:
                self.log._xpoll.error(traceback.format_exc())
                error_code = self._conn.has_error()
                if error_code:
                    error_string = XCB_CONN_ERRORS[error_code]
                    self.log.critical("Shutting down due to X connection error %s (%s)" %
                                      (error_string, error_code))
                    self.stop(xserver_dead=True)
                    break
Пример #9
0
class Scheduler(Thread):
  """ Schedules and executes tasks using threaded workers. """
  def __init__(self, workers=5, histlen=10000):
    super().__init__(daemon=True)
    self.pending = PriorityQueue()
    self.ready   = Queue()                # checks to be executed
    self.timer   = MyTimer(0)             # timer placeholder for .schedule() so it can call self.timer.cancel() during the first call
    self.lock    = Lock()                 # lock pending queue
    self.lockev  = Event()                # set by .run() when lock is acquired
    self.history = deque(maxlen=histlen)  # keep track of the history
    self.log     = Log("scheduler")

    for i in range(workers):
      worker = Worker(self)
      worker.start()

  def flush(self):
    """ Request immidiate check. """
    self.log.debug("flushing pending queue")
    with self.lock:
      self.lockev.clear()
      self.pending.put((-1000, None))
      self.timer.cancel()
      self.lockev.wait()

      queued = []
      while True:
        try:
          _, check = self.pending.get(block=False)
          queued.append(check)
        except Empty:
          break

      for checker in queued:
        self.ready.put(checker)
      self.log.debug("flushing done")

  def schedule(self, checker):
    t = checker.get_next_check()
    with self.lock:
      self.pending.put((t, checker))
      self.timer.cancel()

  def run(self):
    pending = self.pending
    ready   = self.ready
    while True:
      t, c = pending.get()
      if c is None:
        self.lockev.set()
        with self.lock:
          continue

      with self.lock:
        delta = t - time.time()
        self.log.debug("sleeping for %.2f" % delta)
        self.timer = MyTimer(delta)
        self.timer.start()

      try:
        self.timer.join()
        ready.put(c)
      except TimerCanceled:
        self.log.debug("new item scheduled, restarting scheduler (this is normal)")
        pending.put((t, c))
Пример #10
0
class Desktop:
    """ Support for virtual desktops. """

    def __init__(self, id, windows=None, name=None):
        self.id = id
        if not name:
            name = "(desktop %s)" % id(self)
        self.log = Log("desktop %s" % name)
        if not windows:
            windows = []
        self.windows = windows
        self.name = name
        self.cur_focus = None
        self.prev_focus = None
        self.were_mapped = []
        self.hidden = True  # TODO: rename to active

    def show(self):
        self.hidden = False
        for window in self.were_mapped:
            self.log.debug("showing window %s" % window)
            window.show()
        else:
            self.log.debug("no windows on this desktop to show")
        self.were_mapped.clear()

        if self.cur_focus:
            self.cur_focus.focus()

        # TODO: self.wm.root.set_prop('_NET_CURRENT_DESKTOP', self.id)

    def hide(self):
        self.hidden = True
        for window in self.windows:
            if window.mapped:
                self.log.debug("hiding window %s" % window)
                window.hide()
                self.were_mapped.append(window)
        self.log.debug("followind windows were hidden: %s" % self.were_mapped)

    def add(self, window):
        self.windows.append(window)
        if self.hidden:
            self.were_mapped.append(window)
        else:
            window.show()
            window.focus()
            self.cur_focus = window

    def remove(self, window):
        if window not in self.windows:
            self.log.error("NO WINDOW %s" % window)
            self.log.error("current windows: %s", self.windows)
            return

        self.windows.remove(window)
        if not self.hidden:
            window.hide()
        if window == self.cur_focus:
            self.cur_focus = None

    def focus_on(self, window, warp=False):
        assert window in self.windows, "window %s is not on current desktop" % window
        assert not self.hidden, "cannot focus while desktop is hidden"
        # if self.cur_focus:
        #     self.cur_focus.lower()
        # Achtung! Order here is very important or focus will now work
        # correctly
        self.log("focusing on %s" % window)
        if warp:
            window.show()
            window.rise()
            window.warp()
        window.focus()

        self.cur_focus = window

    def __repr__(self):
        return "Desktop(%s)" % self.name
Пример #11
0
class Hook:
    """ Simple callback dispatcher. """
    def __init__(self):
        self.cb_map = defaultdict(list)
        self.log = Log("hook")
        self.suppressed = set()

    def decor(self, event):
        def wrap(cb):
            self.register(event, cb)
            return cb

        return wrap

    __call__ = decor

    def register(self, event, cb):
        self.cb_map[event].append(cb)

    def has_hook(self, event):
        return event in self.cb_map

    def suppress(self, event):
        hook = self

        class Context:
            def __enter__(self):
                hook.log.debug("suppressing %s" % event)
                hook.suppressed.add(event)

            def __exit__(self, *args):
                hook.log.debug("un-suppressing %s" % event)
                if event in hook.suppressed:
                    hook.suppressed.remove(event)
                else:
                    hook.log.notice("uhm, event is not suppressed: %s" % event)

        return Context()

    def fire(self, event, *args, **kwargs):
        self.log.debug("{} {} {}".format(event, args, kwargs))
        if event not in self.cb_map:
            self.log.notice("no handler for {}".format(event))
            return

        if event in self.suppressed:
            self.log.debug("event suppressed: {} {} {}".format(
                event, args, kwargs))
            return

        handlers = self.cb_map[event]
        for handler in handlers:
            try:
                handler(event, *args, **kwargs)
            # except SupressEvent:
            # break
            except Exception as err:
                # msg = "error on event {ev}: {err} ({typ}) (in {hdl})" \
                #     .format(err=err, typ=type(err), ev=event, hdl=handler)
                msg = traceback.format_exc()
                self.log.error(msg)
Пример #12
0
  # wordpress = "siege -c 100 -t 666h http://localhost/",
  # matrix = "/home/sources/perftest/benches/matrix.py -s 1024 -r 1000",
  matrix = BENCHES + "matrix 2048",
  # sdag   = BENCHES + "test_SDAG/test_sdag -t 5 -q 1000 /home/sources/perftest/benches/test_SDAG/dataset.dat",
  # sdagp  = BENCHES + "test_SDAG/test_sdag+ -t 5 -q 1000 /home/sources/perftest/benches/test_SDAG/dataset.dat",
  blosc  = BENCHES + "pyblosc.py -r 10000000",
  burnP6 = "burnP6",
  ffmpeg = "ffmpeg -i /home/sources/ToS-4k-1920.mov" \
           " -threads 1 -y -strict -2 -loglevel panic" \
           " -acodec aac -aq 100" \
           " -vcodec libx264 -preset fast -crf 22" \
           " -f mp4 /dev/null",
)

log = Log(['profile'])
for k,v in basis.items(): log.debug("{:<10} {}".format(k,v))

class cfg:
  sys_ipc_time = 3
  task_profile_time = 0.1
  sys_optimize_samples = 10
  warmup_time = 3
  idleness = 100
  cpu_mask = 0b1111

def generate_load(num):
  tasks = []
  all_tasks = list(basis.items())
  for i in range(num):
    name, cmd = all_tasks.pop(0)
    p = Popen(shlex.split(cmd), stdout=DEVNULL, stderr=DEVNULL)
Пример #13
0
class Manager(CLI):
  """ Class to orchestrate several instances at once. """
  autostart_delay = 3

  def __init__(self, name="default"):
    self.instances = OrderedDict()
    self.name = name
    self.log  = Log(name)

  def add_instance(self, inst):
    assert inst.name not in self.instances, \
      "we already have a machine with the name %s" % inst.name
    self.instances[inst.name] = inst

  def check_instance(self, name):
    if name not in self.instances:
      raise UnknownInstance("no such instance: %s" % name)

  @command("gen mac")
  def genmac(self):
    mac = gen_mac()
    print(mac)
    return mac

  @command("list")
  def do_list(self):
    return self.instances.keys()

  @command("autostart")
  def autostart(self):
    log.debug("starting all stopped instances with auto=True")
    sleep = 0  # do not do a pause if there is only one instance
    for instance in self.instances.values():
      time.sleep(sleep)
      if not instance.auto:
        self.log.debug("%s is skipped because it has auto=False"
                        % instance)
        continue
      if instance.is_running():
        log.debug("skipping %s because it is already started" % instance)
        continue
      log.info("Starting %s" % instance)
      instance.start()
      sleep = self.autostart_delay

  @command("[name] start")
  @command("start [name]")
  def start(self, name):
    assert isinstance(name, str), "name should be string"
    self.log.debug("Starting %s" % name)
    self.check_instance(name)
    inst = self.instances[name]
    inst.start()
    return inst

  @command("stop all")
  @command("shutdown all")
  def stop_all(self):
    for inst in self.instances.values():
      inst.stop()

  @command("stop [name]")
  @command("[name] stop")
  @command("shutdown [name]")
  @command("[name] shutdown")
  def stop(self, name):
    self.check_instance(name)
    self.instances[name].stop()

  @command("[name] reboot")
  @command("reboot [name]")
  def reboot(self, name=None):
    self.check_instance(name)
    self.instances[name].reboot()

  @command("[name] reset")
  @command("reset [name]")
  def reset(self, name=None):
    self.check_instance(name)
    self.instances[name].reset()

  @command("unfreeze all")
  @command("defrost all")
  def kill_defrost(self):
    for inst in self.instances.values():
      inst.unfreeze()

  @command("killall")
  @command("kill all")
  def kill_all(self):
    self.log.critical("KILLING ALL instances (even with auto=False)")
    for inst in self.instances.values():
      inst.kill()

  @command("[name] kill")
  @command("kill [name]")
  def kill(self, name):
    self.check_instance(name)
    self.instances[name].kill()

  @command("show cmd [name]")
  def show_cmd(self, name):
    print(self.instances[name].get_cmd())

  @command("console [name]")
  @command("[name] console")
  def console(self, name=None):
    self.log.debug("attaching to %s" % name)
    if name and not self.instances[name].is_running():
      sys.exit("Instance is not started")
    self.instances[name].tmux.attach(name=name)

  @command("status")
  def status(self):
    for inst in self.instances.values():
      print(inst.format_status())

  @command("wait all timeout [timeout]")
  def wait_all(self, timeout):
    timeout = int(timeout)
    while True:
      running = 0
      for inst in self.instances.values():
        if inst.is_running():
          running = 1
      if not running:
        break
      timeout -= 1
      if timeout < 0:
        raise TimeoutError("instances still running")
      time.sleep(1)
      print('.', end='', file=sys.stderr, flush=True)

  @command("graceful stop timeout [timeout]")
  def graceful(self, timeout=30):
    self.log.info("stopping ALL instances (even with auto=False)")
    timeout = int(timeout)
    self.stop_all()
    try:
      self.wait_all(timeout)
    except TimeoutError:
      self.log.critical("kvms still running: %s" \
        % list(filter(lambda x: x.is_running(), self.instances.values())))
      self.kill_all()
Пример #14
0
class KVM:
  name   = None
  mem    = 256
  cores  = 1
  cpu    = "qemu64"
  runas  = None
  cmd    = "qemu-system-x86_64 -enable-kvm -curses"
  tmux   = TMUX(socket="virt", session="KVM")
  auto   = True
  net    = None
  drives = None
  mgr    = manager
  cpus   = None  # CPU affinity
  kernel = None
  append = None
  initrd = None
  boot   = None
  devs   = []

  def __init__(self, **kwargs):
    self.__dict__.update(kwargs)
    self.pidfile = os.path.join(PREFIX, "kvm_%s.pid"%self.name)
    self.monfile = os.path.join(PREFIX, "kvm_%s.mon"%self.name)
    self.log = Log("KVM %s" % self.name)
    self.qmpsock = None
    assert self.name, "name is mandatory"
    if self.mgr:
      #self.log.debug("adding %s to %s" % (self, self.mgr))
      self.mgr.add_instance(self)

  def get_cmd(self):
    """ Get cmd that launches instance. """
    cmd = self.cmd
    cmd += " -name %s" % self.name
    cmd += " -m %s" % self.mem
    if self.cpu: cmd += " -cpu %s" % self.cpu
    cmd += " -smp %s" % self.cores
    cmd += " -qmp unix:%s,server,nowait " % self.monfile
    cmd += " -pidfile %s " % self.pidfile
    if self.net: cmd += stringify(self.net)
    if self.drives: cmd += stringify(self.drives)
    if self.runas:
      if os.geteuid() != 0:
        cmd = "sudo " + cmd
      cmd += " -runas %s" % self.runas
    if self.kernel: cmd += " -kernel %s" % self.kernel
    if self.append: cmd += " -append %s" % self.append
    if self.initrd: cmd += " -initrd %s" % self.initrd
    if self.boot:   cmd += " -boot %s" % self.boot
    if self.devs:
      for device in self.devs:
        cmd += " %s" % device
    return cmd

  def is_running(self):
    """ Returns either pid of the process
        or False if kvm is not running.
    """
    try:
      pid = int(open(self.pidfile).readline().strip())
    except IOError as err:
      if err.errno == errno.EACCES:
        raise StatusUnknown("cannot read pidfile:", err)
      elif err.errno == errno.ENOENT:
        return False
      raise

    try:
      os.kill(pid, 0)
      return pid
    except ProcessLookupError:
      os.unlink(self.pidfile)
      return False

  @property
  def pid(self):
    return self.is_running()

  def start(self):
    if self.is_running():
      self.log.debug("Instance is already started!")
      return False

    for device in self.devs:
      device.on_start(self)

    self.log.debug("spawning %s" % self.get_cmd())
    self.tmux.run(self.get_cmd(), name=self.name)

    for x in range(100):
        pid = self.is_running()
        if pid: break
        time.sleep(0.2)
        self.log.debug("waiting for VM")
    else:
      raise StatusUnknown("KVM %s doesn't want to start" % self.name)

    if self.cpus:
      self.set_cpus(self.cpus)

    return pid

  def set_cpus(self, cpus):
    self.cpus = cpus
    pid = self.pid
    if not pid:
      return self.log.critical("VM is not running, not setting affinity")
    cpulist = ",".join(map(str,self.cpus))
    self.log.debug("setting CPU affinity to %s" % cpulist)
    cmd = "taskset -a -c -p %s %s" % (cpulist, pid)
    try:
      run(cmd, stdout=DEVNULL)
    except Exception as e:
      self.log.critical("set affinity with taskset failed: %s" % e)

  def reboot(self):
    """ Send Ctrl+Alt+Del. """
    data = """{ "execute": "send-key",
        "arguments": { 'keys': [
          {'type':'qcode', 'data': 'ctrl'},
          {'type':'qcode', 'data': 'alt'},
          {'type':'qcode', 'data': 'delete'}
          ]}}"""
    self.send_qmp(data)

  def reset(self):
    """ Do hard reset. """
    self.send_qmp('{"execute": "system_reset"}')

  def freeze(self):
    """ stop virtual CPU """
    self.send_qmp('{"execute": "stop"}')

  def unfreeze(self):
    """ resume after freeze """
    self.send_qmp('{"execute": "cont"}')

  def shutdown(self):
    """ Attempt to do graceful shutdown. Success is not guaranteed. """
    if self.is_running():
      try:
        self.send_qmp('{"execute": "system_powerdown"}')
      except Exception as err:
        self.log.critical("shutdown command failed with %s" % err)
    for device in self.devs:
      device.on_stop(self)
  stop = shutdown  # stop is alias for shutdown

  def kill(self):
    """ Kill the guest using all possible means. """
    for device in self.devs:
      device.on_kill(self)

    pid = self.is_running()
    if not pid:
      self.log.debug("%s: It's Dead, Jim!" % self)
      return False
    try:
      self.send_qmp("{'execute': 'quit'}")
      self.qmp_disconnect()
      timeout = KILL_TIMEOUT
      while timeout > 0:
        time.sleep(POLL_INTERVAL)
        timeout -= POLL_INTERVAL
        if not self.is_running():
          return 1
    except Exception as err:
      self.log.critical("cannot send qmp command: %s" % err)

    self.log.critical("It doesn't want to die, killing by SIGKILL")

    try:
      os.kill(pid, signal.SIGKILL)
    except ProcessLookupError:
      pass

    return 2

  def qmp_connect(self):
    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    s.settimeout(3)
    #self.log.debug("connecting to %s" % self.monfile)
    s.connect(self.monfile)
    answer = s.recv(BUF_SIZE)
    #self.log.debug("initial handshake: %s" % answer.decode(errors='replace'))
    s.send(b'{"execute": "qmp_capabilities"}')  # handshake
    answer = s.recv(BUF_SIZE)
    #self.log.debug("capabilities: %s" % answer.decode(errors='replace'))

    self.qmpsock = s

  def qmp_disconnect(self):
    if self.qmpsock:
      self.qmpsock.close()
      self.qmpsock = None

  def send_qmp(self, cmd):
    if isinstance(cmd, str):
      cmd = cmd.encode()
    if not self.qmpsock:
      self.qmp_connect()
    #self.log.debug("sending cmd %s" % cmd)
    self.qmpsock.send(cmd)
    answer = self.qmpsock.recv(BUF_SIZE)
    if len(answer) == BUF_SIZE:
      self.log.error("too long answer was truncated :(")
    #self.log.debug("result: %s" % answer.decode(errors='replace'))
    return answer

  def console(self):
    self.tmux.attach(name=self.name)

  def format_status(self):
    formated = "%s\n" % self.name
    formated += "  noauto" if not self.auto else " "
    try:
      pid = self.is_running()
      if pid:
        formated += " UP (pid %s)\n" % pid if pid else "DOWN"
      else:
        formated += " DOWN\n"
    except StatusUnknown as err:
      formated += " UNKNOWN (%s)\n" % err
    return formated

  def __repr__(self):
    return "KVM(\"{name}\")".format(name=self.name)

  def __exit__(self):
    self.shutdown()
    for x in range(300):
      time.sleep(0.1)
      if not self.is_running():
        return
    self.log.critical("it doesn't want to die, killing")
    self.kill()
Пример #15
0
class Hook:
    """ Simple callback dispatcher. """

    def __init__(self):
        self.cb_map = defaultdict(list)
        self.log = Log("hook")
        self.suppressed = set()

    def decor(self, event):
        def wrap(cb):
            self.register(event, cb)
            return cb

        return wrap

    __call__ = decor

    def register(self, event, cb):
        self.cb_map[event].append(cb)

    def has_hook(self, event):
        return event in self.cb_map

    def suppress(self, event):
        hook = self

        class Context:
            def __enter__(self):
                hook.log.debug("suppressing %s" % event)
                hook.suppressed.add(event)

            def __exit__(self, *args):
                hook.log.debug("un-suppressing %s" % event)
                if event in hook.suppressed:
                    hook.suppressed.remove(event)
                else:
                    hook.log.notice("uhm, event is not suppressed: %s" % event)

        return Context()

    def fire(self, event, *args, **kwargs):
        self.log.debug("{} {} {}".format(event, args, kwargs))
        if event not in self.cb_map:
            self.log.notice("no handler for {}".format(event))
            return

        if event in self.suppressed:
            self.log.debug("event suppressed: {} {} {}".format(event, args, kwargs))
            return

        handlers = self.cb_map[event]
        for handler in handlers:
            try:
                handler(event, *args, **kwargs)
            # except SupressEvent:
            # break
            except Exception as err:
                # msg = "error on event {ev}: {err} ({typ}) (in {hdl})" \
                #     .format(err=err, typ=type(err), ev=event, hdl=handler)
                msg = traceback.format_exc()
                self.log.error(msg)