chromium/chrome/test/base/chromeos/crosier/helper/test_sudo_helper_client.h

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

#ifndef CHROME_TEST_BASE_CHROMEOS_CROSIER_HELPER_TEST_SUDO_HELPER_CLIENT_H_
#define CHROME_TEST_BASE_CHROMEOS_CROSIER_HELPER_TEST_SUDO_HELPER_CLIENT_H_

#include <memory>
#include <string>
#include <string_view>

#include "base/files/scoped_file.h"
#include "base/functional/callback.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "base/values.h"

namespace base {
class FilePath;
}

// Client class to send requests to `test_sudo_helper`. Crosier tests uses this
// class to run tasks requiring user `root`, such as start/stop session_manager
// daemon.
class TestSudoHelperClient {
 public:
  struct Result {
    Result() = default;
    Result(int rc, std::string out) : return_code(rc), output(std::move(out)) {}

    int return_code = -1;
    std::string output;  // stdout and stderr combined.
  };

  TestSudoHelperClient();
  TestSudoHelperClient(const TestSudoHelperClient&) = delete;
  TestSudoHelperClient& operator=(const TestSudoHelperClient&) = delete;
  ~TestSudoHelperClient();

  // Waits for server socket to be ready. Returns true if a connection is made
  // successfully within the time out. Otherwise, returns false.
  bool WaitForServer(base::TimeDelta max_wait);

  // Runs the given command line via `test_sudo_helper`. Returns true if the
  // command exit with 0. Otherwise, returns false.
  Result RunCommand(const std::string_view command);

  // Connects using the server path on the default switch, runs one command, and
  // disconnects. Fails if the server path switch is not found.
  static Result ConnectAndRunCommand(const std::string_view command);

  // Starts the session_manager daemon. `stopped_callback` will be invoked
  // when session_manager daemon terminates.
  Result StartSessionManager(base::OnceClosure stopped_callback);

  // Stops the session manager daemon.
  Result StopSessionManager();

  // Ensures that session manager daemon is not left running from this client.
  void EnsureSessionManagerStopped();

 private:
  base::ScopedFD ConnectToServer(const base::FilePath& client_path);

  Result SendDictAndGetResult(const base::Value::Dict& dict,
                              base::ScopedFD* out_sock = nullptr,
                              bool fatal_on_connection_error = true);

  void ReadSessionManagerEventOnWatcherThread(base::ScopedFD sock);

  // Socket path where `test_sudo_helper` server is listening. By default,
  // it is `kTestSudoHelperServerSocketPath`.
  std::string server_path_;

  std::unique_ptr<base::Thread> session_manager_watcher_thread_;
  base::OnceClosure session_manager_stopped_callback_;
};

#endif  // CHROME_TEST_BASE_CHROMEOS_CROSIER_HELPER_TEST_SUDO_HELPER_CLIENT_H_