Exemplo n.º 1
0
def readlink(path):
    """
    Return the path that a symlink points to

    This is only supported on Windows Vista or later.

    Inline with Unix behavior, this function will raise an error if the path is
    not a symlink, however, the error raised will be a SaltInvocationError, not
    an OSError.

    Args:
        path (str): The path to the symlink

    Returns:
        str: The path that the symlink points to

    CLI Example:

    .. code-block:: bash

        salt '*' file.readlink /path/to/link
    """
    if sys.getwindowsversion().major < 6:
        raise HubbleInvocationError(
            "Symlinks are only supported on Windows Vista or later."
        )

    try:
        return hubblestack.utils.path.readlink(path)
    except OSError as exc:
        if exc.errno == errno.EINVAL:
            raise CommandExecutionError("{0} is not a symbolic link".format(path))
        raise CommandExecutionError(exc.__str__())
    except Exception as exc:  # pylint: disable=broad-except
        raise CommandExecutionError(exc)
Exemplo n.º 2
0
def version(context=None):
    '''
    Attempts to run systemctl --version. Returns None if unable to determine
    version.
    '''
    contextkey = 'hubblestack.utils.systemd.version'
    if isinstance(context, dict):
        # Can't put this if block on the same line as the above if block,
        # because it will break the elif below.
        if contextkey in context:
            return context[contextkey]
    elif context is not None:
        raise HubbleInvocationError('context must be a dictionary if passed')
    stdout = subprocess.Popen(['systemctl', '--version'],
                              close_fds=True,
                              stdout=subprocess.PIPE,
                              stderr=subprocess.STDOUT).communicate()[0]
    outstr = hubblestack.utils.stringutils.to_str(stdout)
    try:
        ret = int(re.search(r'\w+ ([0-9]+)', outstr.splitlines()[0]).group(1))
    except (AttributeError, IndexError, ValueError):
        log.error(
            'Unable to determine systemd version from systemctl '
            '--version, output follows:\n%s', outstr)
        return None
    else:
        try:
            context[contextkey] = ret
        except TypeError:
            pass
        return ret
Exemplo n.º 3
0
def is_link(path):
    """
    Check if the path is a symlink

    This is only supported on Windows Vista or later.

    Inline with Unix behavior, this function will raise an error if the path
    is not a symlink, however, the error raised will be a SaltInvocationError,
    not an OSError.

    Args:
        path (str): The path to a file or directory

    Returns:
        bool: True if path is a symlink, otherwise False

    CLI Example:

    .. code-block:: bash

       salt '*' file.is_link /path/to/link
    """
    if sys.getwindowsversion().major < 6:
        raise HubbleInvocationError(
            "Symlinks are only supported on Windows Vista or later."
        )

    try:
        return hubblestack.utils.path.islink(path)
    except Exception as exc:  # pylint: disable=broad-except
        raise CommandExecutionError(exc)
Exemplo n.º 4
0
def _resolve_symlink(path, max_depth=64):
    """
    Resolves the given symlink path to its real path, up to a maximum of the
    `max_depth` parameter which defaults to 64.

    If the path is not a symlink path, it is simply returned.
    """
    if sys.getwindowsversion().major < 6:
        raise HubbleInvocationError(
            "Symlinks are only supported on Windows Vista or later."
        )

    # make sure we don't get stuck in a symlink loop!
    paths_seen = set((path,))
    cur_depth = 0
    while is_link(path):
        path = readlink(path)
        if path in paths_seen:
            raise CommandExecutionError("The given path is involved in a symlink loop.")
        paths_seen.add(path)
        cur_depth += 1
        if cur_depth > max_depth:
            raise CommandExecutionError("Too many levels of symbolic links.")

    return path
Exemplo n.º 5
0
def booted(context=None):
    '''
    Return True if the system was booted with systemd, False otherwise.  If the
    loader context dict ``__context__`` is passed, this function will set the
    ``hubblestack.utils.systemd.booted`` key to represent if systemd is running and
    keep the logic below from needing to be run again during the same salt run.
    '''
    contextkey = 'hubblestack.utils.systemd.booted'
    if isinstance(context, dict):
        # Can't put this if block on the same line as the above if block,
        # because it willl break the elif below.
        if contextkey in context:
            return context[contextkey]
    elif context is not None:
        raise HubbleInvocationError('context must be a dictionary if passed')

    try:
        # This check does the same as sd_booted() from libsystemd-daemon:
        # http://www.freedesktop.org/software/systemd/man/sd_booted.html
        ret = bool(os.stat('/run/systemd/system'))
    except OSError:
        ret = False

    try:
        context[contextkey] = ret
    except TypeError:
        pass

    return ret
Exemplo n.º 6
0
def remove(path):
    """
    Remove the named file. If a directory is supplied, it will be recursively
    deleted.

    CLI Example:

    .. code-block:: bash

        salt '*' file.remove /tmp/foo

    .. versionchanged:: Neon
        The method now works on all types of file system entries, not just
        files, directories and symlinks.
    """
    path = os.path.expanduser(path)

    if not os.path.isabs(path):
        raise HubbleInvocationError("File path must be absolute: {0}".format(path))

    try:
        if os.path.islink(path) or (os.path.exists(path) and not os.path.isdir(path)):
            os.remove(path)
            return True
        elif os.path.isdir(path):
            shutil.rmtree(path)
            return True
    except (OSError, IOError) as exc:
        raise CommandExecutionError("Could not remove '{0}': {1}".format(path, exc))
    return False
Exemplo n.º 7
0
def remove(path, force=False):
    """
    Remove the named file or directory

    Args:
        path (str): The path to the file or directory to remove.
        force (bool): Remove even if marked Read-Only. Default is False

    Returns:
        bool: True if successful, False if unsuccessful

    CLI Example:

    .. code-block:: bash

        salt '*' file.remove C:\\Temp
    """
    # This must be a recursive function in windows to properly deal with
    # Symlinks. The shutil.rmtree function will remove the contents of
    # the Symlink source in windows.

    path = os.path.expanduser(path)

    if not os.path.isabs(path):
        raise HubbleInvocationError("File path must be absolute: {0}".format(path))

    # Does the file/folder exists
    if not os.path.exists(path) and not is_link(path):
        raise CommandExecutionError("Path not found: {0}".format(path))

    # Remove ReadOnly Attribute
    if force:
        # Get current file attributes
        file_attributes = win32api.GetFileAttributes(path)
        win32api.SetFileAttributes(path, win32con.FILE_ATTRIBUTE_NORMAL)

    try:
        if os.path.isfile(path):
            # A file and a symlinked file are removed the same way
            os.remove(path)
        elif is_link(path):
            # If it's a symlink directory, use the rmdir command
            os.rmdir(path)
        else:
            for name in os.listdir(path):
                item = "{0}\\{1}".format(path, name)
                # If its a normal directory, recurse to remove it's contents
                remove(item, force)

            # rmdir will work now because the directory is empty
            os.rmdir(path)
    except (OSError, IOError) as exc:
        if force:
            # Reset attributes to the original if delete fails.
            win32api.SetFileAttributes(path, file_attributes)
        raise CommandExecutionError("Could not remove '{0}': {1}".format(path, exc))

    return True
Exemplo n.º 8
0
def touch(name, atime=None, mtime=None):
    """
    .. versionadded:: 0.9.5

    Just like the ``touch`` command, create a file if it doesn't exist or
    simply update the atime and mtime if it already does.

    atime:
        Access time in Unix epoch time
    mtime:
        Last modification in Unix epoch time

    CLI Example:

    .. code-block:: bash

        salt '*' file.touch /var/log/emptyfile
    """
    name = os.path.expanduser(name)

    if atime and atime.isdigit():
        atime = int(atime)
    if mtime and mtime.isdigit():
        mtime = int(mtime)
    try:
        if not os.path.exists(name):
            with hubblestack.utils.files.fopen(name, "a"):
                pass

        if not atime and not mtime:
            times = None
        elif not mtime and atime:
            times = (atime, time.time())
        elif not atime and mtime:
            times = (time.time(), mtime)
        else:
            times = (atime, mtime)
        os.utime(name, times)

    except TypeError:
        raise HubbleInvocationError("atime and mtime must be integers")
    except (IOError, OSError) as exc:
        raise CommandExecutionError(exc.strerror)

    return os.path.exists(name)
Exemplo n.º 9
0
def lsattr(path):
    """
    .. versionadded:: 2018.3.0
    .. versionchanged:: 2018.3.1
        If ``lsattr`` is not installed on the system, ``None`` is returned.
    .. versionchanged:: 2018.3.4
        If on ``AIX``, ``None`` is returned even if in filesystem as lsattr on ``AIX``
        is not the same thing as the linux version.

    Obtain the modifiable attributes of the given file. If path
    is to a directory, an empty list is returned.

    path
        path to file to obtain attributes of. File/directory must exist.

    CLI Example:

    .. code-block:: bash

        salt '*' file.lsattr foo1.txt
    """
    if not hubblestack.utils.path.which("lsattr") or hubblestack.utils.platform.is_aix():
        return None

    if not os.path.exists(path):
        raise HubbleInvocationError("File or directory does not exist: " + path)

    cmd = ["lsattr", path]
    result = __mods__["cmd.run"](cmd, ignore_retcode=True, python_shell=False)

    results = {}
    for line in result.splitlines():
        if not line.startswith("lsattr: "):
            attrs, file = line.split(None, 1)
            if _chattr_has_extended_attrs():
                pattern = r"[aAcCdDeijPsStTu]"
            else:
                pattern = r"[acdijstuADST]"
            results[file] = re.findall(pattern, attrs)

    return results
Exemplo n.º 10
0
def chattr(*files, **kwargs):
    """
    .. versionadded:: 2018.3.0

    Change the attributes of files. This function accepts one or more files and
    the following options:

    operator
        Can be wither ``add`` or ``remove``. Determines whether attributes
        should be added or removed from files

    attributes
        One or more of the following characters: ``aAcCdDeijPsStTu``,
        representing attributes to add to/remove from files

    version
        a version number to assign to the file(s)

    flags
        One or more of the following characters: ``RVf``, representing
        flags to assign to chattr (recurse, verbose, suppress most errors)

    CLI Example:

    .. code-block:: bash

        salt '*' file.chattr foo1.txt foo2.txt operator=add attributes=ai
        salt '*' file.chattr foo3.txt operator=remove attributes=i version=2
    """
    operator = kwargs.pop("operator", None)
    attributes = kwargs.pop("attributes", None)
    flags = kwargs.pop("flags", None)
    version = kwargs.pop("version", None)

    if (operator is None) or (operator not in ("add", "remove")):
        raise HubbleInvocationError(
            "Need an operator: 'add' or 'remove' to modify attributes."
        )
    if attributes is None:
        raise HubbleInvocationError("Need attributes: [aAcCdDeijPsStTu]")

    cmd = ["chattr"]

    if operator == "add":
        attrs = "+{0}".format(attributes)
    elif operator == "remove":
        attrs = "-{0}".format(attributes)

    cmd.append(attrs)

    if flags is not None:
        cmd.append("-{0}".format(flags))

    if version is not None:
        cmd.extend(["-v", version])

    cmd.extend(files)

    result = __mods__["cmd.run"](cmd, python_shell=False)

    if bool(result):
        return False

    return True