Ejemplo n.º 1
0
def perform_checksum(filename, hashname="MD5", calc_prelink=0):
	"""
	Run a specific checksum against a file. The filename can
	be either unicode or an encoded byte string. If filename
	is unicode then a UnicodeDecodeError will be raised if
	necessary.

	@param filename: File to run the checksum against
	@type filename: String
	@param hashname: The type of hash function to run
	@type hashname: String
	@param calc_prelink: Whether or not to reverse prelink before running the checksum
	@type calc_prelink: Integer
	@rtype: Tuple
	@return: The hash and size of the data
	"""
	global prelink_capable
	# Make sure filename is encoded with the correct encoding before
	# it is passed to spawn (for prelink) and/or the hash function.
	filename = _unicode_encode(filename,
		encoding=_encodings['fs'], errors='strict')
	myfilename = filename
	prelink_tmpfile = None
	try:
		if (calc_prelink and prelink_capable and
		    is_prelinkable_elf(filename)):
			# Create non-prelinked temporary file to checksum.
			# Files rejected by prelink are summed in place.
			try:
				tmpfile_fd, prelink_tmpfile = tempfile.mkstemp()
				try:
					retval = easymgc.process.spawn([PRELINK_BINARY,
						"--verify", filename], fd_pipes={1:tmpfile_fd})
				finally:
					os.close(tmpfile_fd)
				if retval == os.EX_OK:
					myfilename = prelink_tmpfile
			except easymgc.exception.CommandNotFound:
				# This happens during uninstallation of prelink.
				prelink_capable = False
		try:
			if hashname not in hashfunc_map:
				raise easymgc.exception.DigestException(hashname + \
					" hash function not available (needs dev-python/pycrypto)")
			myhash, mysize = hashfunc_map[hashname](myfilename)
		except (OSError, IOError) as e:
			if e.errno in (errno.ENOENT, errno.ESTALE):
				raise easymgc.exception.FileNotFound(myfilename)
			elif e.errno == easymgc.exception.PermissionDenied.errno:
				raise easymgc.exception.PermissionDenied(myfilename)
			raise
		return myhash, mysize
	finally:
		if prelink_tmpfile:
			try:
				os.unlink(prelink_tmpfile)
			except OSError as e:
				if e.errno != errno.ENOENT:
					raise
				del e
Ejemplo n.º 2
0
def _setup_pipes(fd_pipes, close_fds=True):
    """Setup pipes for a forked process."""
    my_fds = {}
    # To protect from cases where direct assignment could
    # clobber needed fds ({1:2, 2:1}) we first dupe the fds
    # into unused fds.
    for fd in fd_pipes:
        my_fds[fd] = os.dup(fd_pipes[fd])
    # Then assign them to what they should be.
    for fd in my_fds:
        os.dup2(my_fds[fd], fd)

    if close_fds:
        # Then close _all_ fds that haven't been explicitly
        # requested to be kept open.
        for fd in get_open_fds():
            if fd not in my_fds:
                try:
                    os.close(fd)
                except OSError:
                    pass
Ejemplo n.º 3
0
def _setup_pipes(fd_pipes, close_fds=True):
    """Setup pipes for a forked process."""
    my_fds = {}
    # To protect from cases where direct assignment could
    # clobber needed fds ({1:2, 2:1}) we first dupe the fds
    # into unused fds.
    for fd in fd_pipes:
        my_fds[fd] = os.dup(fd_pipes[fd])
    # Then assign them to what they should be.
    for fd in my_fds:
        os.dup2(my_fds[fd], fd)

    if close_fds:
        # Then close _all_ fds that haven't been explicitly
        # requested to be kept open.
        for fd in get_open_fds():
            if fd not in my_fds:
                try:
                    os.close(fd)
                except OSError:
                    pass
Ejemplo n.º 4
0
def spawn(mycommand,
          env={},
          opt_name=None,
          fd_pipes=None,
          returnpid=False,
          uid=None,
          gid=None,
          groups=None,
          umask=None,
          logfile=None,
          path_lookup=True,
          pre_exec=None):
    """
    Spawns a given command.
    
    @param mycommand: the command to execute
    @type mycommand: String or List (Popen style list)
    @param env: A dict of Key=Value pairs for env variables
    @type env: Dictionary
    @param opt_name: an optional name for the spawn'd process (defaults to the binary name)
    @type opt_name: String
    @param fd_pipes: A dict of mapping for pipes, { '0': stdin, '1': stdout } for example
    @type fd_pipes: Dictionary
    @param returnpid: Return the Process IDs for a successful spawn.
    NOTE: This requires the caller clean up all the PIDs, otherwise spawn will clean them.
    @type returnpid: Boolean
    @param uid: User ID to spawn as; useful for dropping privilages
    @type uid: Integer
    @param gid: Group ID to spawn as; useful for dropping privilages
    @type gid: Integer
    @param groups: Group ID's to spawn in: useful for having the process run in multiple group contexts.
    @type groups: List
    @param umask: An integer representing the umask for the process (see man chmod for umask details)
    @type umask: Integer
    @param logfile: name of a file to use for logging purposes
    @type logfile: String
    @param path_lookup: If the binary is not fully specified then look for it in PATH
    @type path_lookup: Boolean
    @param pre_exec: A function to be called with no arguments just prior to the exec call.
    @type pre_exec: callable
    
    logfile requires stdout and stderr to be assigned to this process (ie not pointed
       somewhere else.)
    
    """

    # mycommand is either a str or a list
    if isinstance(mycommand, basestring):
        mycommand = mycommand.split()

    if sys.hexversion < 0x3000000:
        # Avoid a potential UnicodeEncodeError from os.execve().
        env_bytes = {}
        for k, v in env.items():
            env_bytes[_unicode_encode(k, encoding=_encodings['content'])] = \
                _unicode_encode(v, encoding=_encodings['content'])
        env = env_bytes
        del env_bytes

    # If an absolute path to an executable file isn't given
    # search for it unless we've been told not to.
    binary = mycommand[0]
    if binary not in (BASH_BINARY, SANDBOX_BINARY, FAKEROOT_BINARY) and \
        (not os.path.isabs(binary) or not os.path.isfile(binary)
        or not os.access(binary, os.X_OK)):
        binary = path_lookup and find_binary(binary) or None
        if not binary:
            raise CommandNotFound(mycommand[0])

    # If we haven't been told what file descriptors to use
    # default to propagating our stdin, stdout and stderr.
    if fd_pipes is None:
        fd_pipes = {
            0: sys.stdin.fileno(),
            1: sys.stdout.fileno(),
            2: sys.stderr.fileno(),
        }

    # mypids will hold the pids of all processes created.
    mypids = []

    if logfile:
        # Using a log file requires that stdout and stderr
        # are assigned to the process we're running.
        if 1 not in fd_pipes or 2 not in fd_pipes:
            raise ValueError(fd_pipes)

        # Create a pipe
        (pr, pw) = os.pipe()

        # Create a tee process, giving it our stdout and stderr
        # as well as the read end of the pipe.
        mypids.extend(
            spawn(('tee', '-i', '-a', logfile),
                  returnpid=True,
                  fd_pipes={
                      0: pr,
                      1: fd_pipes[1],
                      2: fd_pipes[2]
                  }))

        # We don't need the read end of the pipe, so close it.
        os.close(pr)

        # Assign the write end of the pipe to our stdout and stderr.
        fd_pipes[1] = pw
        fd_pipes[2] = pw

    pid = os.fork()

    if pid == 0:
        try:
            _exec(binary, mycommand, opt_name, fd_pipes, env, gid, groups, uid,
                  umask, pre_exec)
        except SystemExit:
            raise
        except Exception as e:
            # We need to catch _any_ exception so that it doesn't
            # propagate out of this function and cause exiting
            # with anything other than os._exit()
            sys.stderr.write("%s:\n   %s\n" % (e, " ".join(mycommand)))
            traceback.print_exc()
            sys.stderr.flush()
            os._exit(1)

    if not isinstance(pid, int):
        raise AssertionError("fork returned non-integer: %s" % (repr(pid), ))

    # Add the pid to our local and the global pid lists.
    mypids.append(pid)
    spawned_pids.append(pid)

    # If we started a tee process the write side of the pipe is no
    # longer needed, so close it.
    if logfile:
        os.close(pw)

    # If the caller wants to handle cleaning up the processes, we tell
    # it about all processes that were created.
    if returnpid:
        return mypids

    # Otherwise we clean them up.
    while mypids:

        # Pull the last reader in the pipe chain. If all processes
        # in the pipe are well behaved, it will die when the process
        # it is reading from dies.
        pid = mypids.pop(0)

        # and wait for it.
        retval = os.waitpid(pid, 0)[1]

        # When it's done, we can remove it from the
        # global pid list as well.
        spawned_pids.remove(pid)

        if retval:
            # If it failed, kill off anything else that
            # isn't dead yet.
            for pid in mypids:
                # With waitpid and WNOHANG, only check the
                # first element of the tuple since the second
                # element may vary (bug #337465).
                if os.waitpid(pid, os.WNOHANG)[0] == 0:
                    os.kill(pid, signal.SIGTERM)
                    os.waitpid(pid, 0)
                spawned_pids.remove(pid)

            # If it got a signal, return the signal that was sent.
            if (retval & 0xff):
                return ((retval & 0xff) << 8)

            # Otherwise, return its exit code.
            return (retval >> 8)

    # Everything succeeded
    return 0
Ejemplo n.º 5
0
def spawn(mycommand, env={}, opt_name=None, fd_pipes=None, returnpid=False,
          uid=None, gid=None, groups=None, umask=None, logfile=None,
          path_lookup=True, pre_exec=None):
    """
    Spawns a given command.
    
    @param mycommand: the command to execute
    @type mycommand: String or List (Popen style list)
    @param env: A dict of Key=Value pairs for env variables
    @type env: Dictionary
    @param opt_name: an optional name for the spawn'd process (defaults to the binary name)
    @type opt_name: String
    @param fd_pipes: A dict of mapping for pipes, { '0': stdin, '1': stdout } for example
    @type fd_pipes: Dictionary
    @param returnpid: Return the Process IDs for a successful spawn.
    NOTE: This requires the caller clean up all the PIDs, otherwise spawn will clean them.
    @type returnpid: Boolean
    @param uid: User ID to spawn as; useful for dropping privilages
    @type uid: Integer
    @param gid: Group ID to spawn as; useful for dropping privilages
    @type gid: Integer
    @param groups: Group ID's to spawn in: useful for having the process run in multiple group contexts.
    @type groups: List
    @param umask: An integer representing the umask for the process (see man chmod for umask details)
    @type umask: Integer
    @param logfile: name of a file to use for logging purposes
    @type logfile: String
    @param path_lookup: If the binary is not fully specified then look for it in PATH
    @type path_lookup: Boolean
    @param pre_exec: A function to be called with no arguments just prior to the exec call.
    @type pre_exec: callable
    
    logfile requires stdout and stderr to be assigned to this process (ie not pointed
       somewhere else.)
    
    """

    # mycommand is either a str or a list
    if isinstance(mycommand, basestring):
        mycommand = mycommand.split()

    if sys.hexversion < 0x3000000:
        # Avoid a potential UnicodeEncodeError from os.execve().
        env_bytes = {}
        for k, v in env.items():
            env_bytes[_unicode_encode(k, encoding=_encodings['content'])] = \
                _unicode_encode(v, encoding=_encodings['content'])
        env = env_bytes
        del env_bytes

    # If an absolute path to an executable file isn't given
    # search for it unless we've been told not to.
    binary = mycommand[0]
    if binary not in (BASH_BINARY, SANDBOX_BINARY, FAKEROOT_BINARY) and \
        (not os.path.isabs(binary) or not os.path.isfile(binary)
        or not os.access(binary, os.X_OK)):
        binary = path_lookup and find_binary(binary) or None
        if not binary:
            raise CommandNotFound(mycommand[0])

    # If we haven't been told what file descriptors to use
    # default to propagating our stdin, stdout and stderr.
    if fd_pipes is None:
        fd_pipes = {
            0:sys.stdin.fileno(),
            1:sys.stdout.fileno(),
            2:sys.stderr.fileno(),
        }

    # mypids will hold the pids of all processes created.
    mypids = []

    if logfile:
        # Using a log file requires that stdout and stderr
        # are assigned to the process we're running.
        if 1 not in fd_pipes or 2 not in fd_pipes:
            raise ValueError(fd_pipes)

        # Create a pipe
        (pr, pw) = os.pipe()

        # Create a tee process, giving it our stdout and stderr
        # as well as the read end of the pipe.
        mypids.extend(spawn(('tee', '-i', '-a', logfile),
                      returnpid=True, fd_pipes={0:pr,
                      1:fd_pipes[1], 2:fd_pipes[2]}))

        # We don't need the read end of the pipe, so close it.
        os.close(pr)

        # Assign the write end of the pipe to our stdout and stderr.
        fd_pipes[1] = pw
        fd_pipes[2] = pw

    pid = os.fork()

    if pid == 0:
        try:
            _exec(binary, mycommand, opt_name, fd_pipes,
                  env, gid, groups, uid, umask, pre_exec)
        except SystemExit:
            raise
        except Exception as e:
            # We need to catch _any_ exception so that it doesn't
            # propagate out of this function and cause exiting
            # with anything other than os._exit()
            sys.stderr.write("%s:\n   %s\n" % (e, " ".join(mycommand)))
            traceback.print_exc()
            sys.stderr.flush()
            os._exit(1)

    if not isinstance(pid, int):
        raise AssertionError("fork returned non-integer: %s" % (repr(pid),))

    # Add the pid to our local and the global pid lists.
    mypids.append(pid)
    spawned_pids.append(pid)

    # If we started a tee process the write side of the pipe is no
    # longer needed, so close it.
    if logfile:
        os.close(pw)

    # If the caller wants to handle cleaning up the processes, we tell
    # it about all processes that were created.
    if returnpid:
        return mypids

    # Otherwise we clean them up.
    while mypids:

        # Pull the last reader in the pipe chain. If all processes
        # in the pipe are well behaved, it will die when the process
        # it is reading from dies.
        pid = mypids.pop(0)

        # and wait for it.
        retval = os.waitpid(pid, 0)[1]

        # When it's done, we can remove it from the
        # global pid list as well.
        spawned_pids.remove(pid)

        if retval:
            # If it failed, kill off anything else that
            # isn't dead yet.
            for pid in mypids:
                # With waitpid and WNOHANG, only check the
                # first element of the tuple since the second
                # element may vary (bug #337465).
                if os.waitpid(pid, os.WNOHANG)[0] == 0:
                    os.kill(pid, signal.SIGTERM)
                    os.waitpid(pid, 0)
                spawned_pids.remove(pid)

            # If it got a signal, return the signal that was sent.
            if (retval & 0xff):
                return ((retval & 0xff) << 8)

            # Otherwise, return its exit code.
            return (retval >> 8)

    # Everything succeeded
    return 0