From 003bfaedc077319b4c2a75687bb1d227c6c52dd2 Mon Sep 17 00:00:00 2001 From: Cleber Rosa Date: Mon, 28 Dec 2015 11:23:46 -0200 Subject: [PATCH] Pseudo terminals: add utility module Based on previous experiences (GDB terminal redirection attempts), it's common for applications that need to allocate a pseudo to have the name of the allocated file. This is, unfortunately, something that the Python standard library doesn't give for free. This simple utility module wraps and extends the Python standard library `pty` module, providing a `openpty` function that, besides allocating the pseudo terminal, also returns its name. Signed-off-by: Cleber Rosa --- avocado/utils/pts.py | 64 ++++++++++++++++++++++ selftests/all/unit/avocado/pts_unittest.py | 41 ++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 avocado/utils/pts.py create mode 100644 selftests/all/unit/avocado/pts_unittest.py diff --git a/avocado/utils/pts.py b/avocado/utils/pts.py new file mode 100644 index 00000000..e7b1024f --- /dev/null +++ b/avocado/utils/pts.py @@ -0,0 +1,64 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# See LICENSE for more details. +# +# Copyright: Red Hat Inc. 2014 +# Author: Cleber Rosa + +""" +Functions dedicated to pseudo terminal manipulation +""" + +import os +import pty +import ctypes + +__all__ = ['ptsname', 'openpty'] + + +def get_libc(): + libc_paths = ['/lib64/libc.so.6', '/lib/libc.so.6', + '/lib/x86_64-linux-gnu/libc.so.6'] + for lib_path in libc_paths: + if os.path.exists(lib_path): + return ctypes.cdll.LoadLibrary(lib_path) + + +def ptsname(master_fd): + """ + Returns the name of the pts device associated with the given master file descriptor + + :param master_fd: the file descriptor of the master of the pseudo terminal + :type master_fd: int + :returns: the name of the pseudo terminal file + :rtype: str + """ + libc = get_libc() + ptsname_ = libc.ptsname + ptsname_.argtypes = [ctypes.c_int] + ptsname_.restype = ctypes.c_char_p + + return ptsname_(master_fd) + + +def openpty(): + """ + Simple wrapper around :func:`pty.openpty` that returns the file name + + This adds the pseudo terminal file path to the extra information returned + by the standard library. + + :returns: a tuple with the master file descriptor, slave file descriptor + and the path of the pseudo file path + :rtype: tuple(int, int, str) + """ + master, slave = pty.openpty() + path = ptsname(master) + return (master, slave, path) diff --git a/selftests/all/unit/avocado/pts_unittest.py b/selftests/all/unit/avocado/pts_unittest.py new file mode 100644 index 00000000..6e9616b6 --- /dev/null +++ b/selftests/all/unit/avocado/pts_unittest.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# See LICENSE for more details. +# +# Copyright: Red Hat Inc. 2014 +# Author: Cleber Rosa + + +import os +import sys +import unittest + +from avocado.utils import pts + +# simple magic for using scripts within a source tree +basedir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +basedir = os.path.dirname(basedir) +if os.path.isdir(os.path.join(basedir, 'avocado')): + sys.path.append(basedir) + + +class PtsTest(unittest.TestCase): + + def test_create_destroy(self): + master, slave, path = pts.openpty() + self.assertTrue(os.path.exists(path)) + os.close(slave) + os.close(master) + self.assertFalse(os.path.exists(path)) + +if __name__ == '__main__': + unittest.main() -- GitLab