diff --git a/.travis.yml b/.travis.yml index 70f18ed39..3aace69aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,6 @@ before_install: install: - pip install --upgrade pip - sudo apt-get -qq update - - sudo apt-get install -y python3-yaml python3-psutil python3-gi gir1.2-gtk-3.0 psmisc gir1.2-glib-2.0 libgirepository1.0-dev + - sudo apt-get install -y python3-yaml python3-gi gir1.2-gtk-3.0 psmisc gir1.2-glib-2.0 libgirepository1.0-dev script: nosetests diff --git a/INSTALL.rst b/INSTALL.rst index 7271cda63..5a918251d 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -8,8 +8,7 @@ Lutris should work on any Gnome system, the following depencies should be installed: * python > 3.4 - * python3-yaml - * python3-psutil + * python-yaml * PyGobject * libsoup-gnome diff --git a/debian/control b/debian/control index 245a1c896..10cf1674d 100644 --- a/debian/control +++ b/debian/control @@ -20,7 +20,6 @@ Architecture: any Depends: ${misc:Depends}, ${python3:Depends}, python3-yaml, - python3-psutil, python3-gi, gir1.2-gtk-3.0, psmisc, diff --git a/lutris.spec b/lutris.spec index 01878e29e..d7a231853 100644 --- a/lutris.spec +++ b/lutris.spec @@ -21,11 +21,11 @@ BuildRequires: python3-devel %if 0%{?fedora_version} BuildRequires: python3-gobject, python3-wheel, python3-setuptools, python3-gobject -Requires: python3-gobject, python3-PyYAML, python3-psutil, cabextract +Requires: python3-gobject, python3-PyYAML, cabextract %endif %if 0%{?rhel_version} || 0%{?centos_version} BuildRequires: python3-gobject -Requires: python3-gobject, python3-PyYAML, python3-psutil, cabextract +Requires: python3-gobject, python3-PyYAML, cabextract %endif %if 0%{?suse_version} BuildRequires: python3-gobject @@ -34,7 +34,7 @@ BuildRequires: update-desktop-files BuildRequires: hicolor-icon-theme BuildRequires: polkit BuildRequires: python3-setuptools -Requires: python3-gobject, python3-PyYAML, python3-psutil, cabextract +Requires: python3-gobject, python3-PyYAML, cabextract %endif %if 0%{?fedora_version} || 0%{?suse_version} BuildRequires: fdupes diff --git a/lutris/runners/winesteam.py b/lutris/runners/winesteam.py index a9f8200cc..ad3f78636 100644 --- a/lutris/runners/winesteam.py +++ b/lutris/runners/winesteam.py @@ -4,7 +4,6 @@ import os import time import shlex import subprocess -import psutil from lutris import settings from lutris.gui.dialogs import DownloadDialog @@ -38,7 +37,7 @@ def is_running(): if pid: # If process is defunct, don't consider it as running process = Process(pid) - return process.state != psutil.STATUS_ZOMBIE + return process.state != 'Z' else: return False diff --git a/lutris/thread.py b/lutris/thread.py index 9f469ff8b..83d64dd2d 100644 --- a/lutris/thread.py +++ b/lutris/thread.py @@ -8,7 +8,6 @@ import shlex import threading import subprocess import contextlib -import psutil from collections import defaultdict from gi.repository import GLib @@ -259,7 +258,7 @@ class LutrisThread(threading.Thread): continue num_watched_children += 1 processes['monitored'].append(str(child)) - if child.state == psutil.STATUS_ZOMBIE: + if child.state == 'Z': terminated_children += 1 if processes != self.monitored_processes: diff --git a/lutris/util/process.py b/lutris/util/process.py index adb65322e..0b0a768ec 100644 --- a/lutris/util/process.py +++ b/lutris/util/process.py @@ -1,5 +1,5 @@ import os -import psutil +import signal from lutris.util.log import logger @@ -13,39 +13,48 @@ class Process(object): self.pid = int(pid) except ValueError: raise InvalidPid("'%s' is not a valid pid" % pid) - self.process = None self.children = [] - self.children_pids = [] self.parent = None - try: - self.process = psutil.Process(self.pid) - except psutil.NoSuchProcess: - return self.get_children() def __repr__(self): return "Process {}".format(self.pid) def __str__(self): - if self.process and self.process.is_running(): - return "{} ({}:{})".format(self.name, self.pid, self.state) - return str(self.pid) + return "{} ({}:{})".format(self.name, self.pid, self.state) + + def get_stat(self, parsed=True): + stat_filename = "/proc/{}/stat".format(self.pid) + if not os.path.exists(stat_filename): + return + with open(stat_filename) as stat_file: + try: + _stat = stat_file.readline() + except (ProcessLookupError, FileNotFoundError): + logger.warning('Unable to read stat for process %s', self.pid) + return + if parsed: + return _stat[_stat.rfind(")") + 1:].split() + return _stat def get_thread_ids(self): """Return a list of thread ids opened by process.""" - if self.process and self.process.is_running(): - return [c.id for c in self.process.threads()] + basedir = '/proc/{}/task/'.format(self.pid) + if os.path.isdir(basedir): + try: + return [tid for tid in os.listdir(basedir)] + except FileNotFoundError: + return [] else: return [] def get_children_pids_of_thread(self, tid): """Return pids of child processes opened by thread `tid` of process.""" children = [] - try: - p = psutil.Process(tid) - children = [c.pid for c in p.children()] - except: - pass + children_path = '/proc/{}/task/{}/children'.format(self.pid, tid) + if os.path.exists(children_path): + with open(children_path) as children_file: + children = children_file.read().strip().split() return children def get_children(self): @@ -57,8 +66,9 @@ class Process(object): @property def name(self): """Filename of the executable.""" - if self.process and self.process.is_running(): - return self.process.name() + _stat = self.get_stat(parsed=False) + if _stat: + return _stat[_stat.find("(") + 1:_stat.rfind(")")] @property def state(self): @@ -67,24 +77,39 @@ class Process(object): sleep, Z is zombie, T is traced or stopped (on a signal), and W is paging. """ - if self.process and self.process.is_running(): - return self.process.status() - return psutil.STATUS_ZOMBIE + _stat = self.get_stat() + if _stat: + return _stat[0] + + @property + def ppid(self): + """PID of the parent.""" + _stat = self.get_stat() + if _stat: + return _stat[1] + + @property + def pgrp(self): + """Process group ID of the process.""" + _stat = self.get_stat() + if _stat: + return _stat[2] @property def cmdline(self): """Return command line used to run the process `pid`.""" - if self.process and self.process.is_running(): - return self.process.cmdline() + cmdline_path = '/proc/{}/cmdline'.format(self.pid) + with open(cmdline_path) as cmdline_file: + _cmdline = cmdline_file.read().replace('\x00', ' ') + return _cmdline @property def cwd(self): - if self.process and self.process.is_running(): - cwd_path = self.process.cwd() - return os.readlink(cwd_path) + cwd_path = '/proc/%d/cwd' % int(self.pid) + return os.readlink(cwd_path) def kill(self): try: - self.process.kill() + os.kill(self.pid, signal.SIGKILL) except OSError: logger.error("Could not kill process %s", self.pid) diff --git a/lutris/util/system.py b/lutris/util/system.py index 72d7e43da..0c7ea4718 100644 --- a/lutris/util/system.py +++ b/lutris/util/system.py @@ -6,7 +6,6 @@ import string import subprocess import sys import traceback -import psutil from lutris.util.log import logger @@ -138,7 +137,7 @@ def get_pid(program, multiple=False): def get_all_pids(): """Return all pids of currently running processes""" - return psutil.pids() + return [int(pid) for pid in os.listdir('/proc') if pid.isdigit()] def kill_pid(pid): @@ -147,15 +146,18 @@ def kill_pid(pid): except ValueError: logger.error("Invalid pid %s") return - psutil.Process(pid).kill() + execute(['kill', '-9', pid]) def get_command_line(pid): """Return command line used to run the process `pid`.""" - try: - return psutil.Process(pid).cmdline() - except: - pass + cmdline = None + cmdline_path = '/proc/{}/cmdline'.format(pid) + if os.path.exists(cmdline_path): + with open(cmdline_path) as cmdline_file: + cmdline = cmdline_file.read() + cmdline = cmdline.replace('\x00', ' ') + return cmdline def python_identifier(string):