chromium/chrome/updater/test/service/win/rpc_client.py

# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import logging
import os
import socket
import sys
import xmlrpc.client

_UPDATER_XML_RPC_PORT = 9090

# Errors that might be raised when interacting with the RPC server.
RPCErrors = (socket.error, socket.herror, socket.gaierror, socket.timeout)


def GetProxy():
    """Constructs a XML RPC server proxy."""
    return xmlrpc.client.ServerProxy('http://localhost:%s' %
                                     _UPDATER_XML_RPC_PORT)


def TestConnection():
    """Test that connection to the XML RPC server."""
    try:
        proxy = GetProxy()
        return 'hi' == proxy.echo('hi')
    except RPCErrors as err:
        logging.error('Unable connect to XML RPC server')
        logging.exception(err)
        return False


def RunAsSystem(command, env=None, cwd=None, timeout=90):
    """Runs the command as SYSTEM user.

    Args:
        command: The command to run. This argument will be forwarded to
          subprocess.Popen().
        env: Environment variables to pass to command.
        cwd: Working directory for the command.
        timeout: How long the child process should wait for UAC before timeout.

    Returns:
        (pid, exit_code, sdtout, stderr) tuple.
    """
    try:
        proxy = GetProxy()
        env = env or dict(os.environ)
        cwd = cwd or os.getcwd()
        return proxy.RunAsSystem(command, env, cwd, timeout)
    except RPCErrors as err:
        logging.exception(err)
        raise


def RunAsStandardUser(command_line, env=None, cwd=None, timeout=90):
    """Runs the command as the non-elevated logged-on user on default desktop.

    Args:
        command_line: The command line string, includes all arguments.
        env: Environment variables to pass to command.
        cwd: Working directory for the command.
        timeout: How long the child process should wait before timeout.

    Returns:
        (pid, exit_code, sdtout, stderr) tuple.
    """
    try:
        proxy = GetProxy()
        env = env or dict(os.environ)
        cwd = cwd or os.getcwd()
        return proxy.RunAsStandardUser(command_line, env, cwd, timeout)
    except RPCErrors as err:
        logging.exception(err)
        raise


def AnswerUpcomingUACPrompt(actions, timeout=10, wait_child=False, source=''):
    """Answers upcoming UAC prompt that does not require username/password.

    Args:
        actions: Actions to take in string, such as 'AADDA', 'A' to accept,
            'D' to deny.
        timeout: How long the child process should wait for each UAC click.
        wait_child: Whether this thread should wait the completion of
                    the child process.
        source: Optional name of the source that triggers this action
                (for logging and debugging purposes).

    Returns:
        (pid, exit_code) of the created UAC-answering process.
        If the sub-process is not created, or did not finish in wait time,
        returns (None, None).
    """
    try:
        proxy = GetProxy()
        return proxy.AnswerUpcomingUACPrompt(actions, timeout, wait_child,
                                             source)
    except RPCErrors as err:
        logging.exception(err)
        raise