/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2019, Google Inc. * * process.cpp - Process object */ #include "libcamera/internal/process.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "libcamera/internal/event_notifier.h" #include "libcamera/internal/log.h" #include "libcamera/internal/utils.h" /** * \file process.h * \brief Process object */ namespace libcamera { LOG_DEFINE_CATEGORY(Process) /** * \class ProcessManager * \brief Manager of processes * * The ProcessManager singleton keeps track of all created Process instances, * and manages the signal handling involved in terminating processes. */ namespace { void sigact(int signal, siginfo_t *info, void *ucontext) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-result" /* * We're in a signal handler so we can't log any message, and we need * to continue anyway. */ char data = 0; write(ProcessManager::instance()->writePipe(), &data, sizeof(data)); #pragma GCC diagnostic pop const struct sigaction &oldsa = ProcessManager::instance()->oldsa(); if (oldsa.sa_flags & SA_SIGINFO) { oldsa.sa_sigaction(signal, info, ucontext); } else { if (oldsa.sa_handler != SIG_IGN && oldsa.sa_handler != SIG_DFL) oldsa.sa_handler(signal); } } } /* namespace */ void ProcessManager::sighandler([[maybe_unused]] EventNotifier *notifier) { char data; ssize_t ret = read(pipe_[0], &data, sizeof(data)); if (ret < 0) { LOG(Process, Error) << "Failed to read byte from signal handler pipe"; return; } for (auto it = processes_.begin(); it != processes_.end(); ) { Process *process = *it; int wstatus; pid_t pid = waitpid(process->pid_, &wstatus, WNOHANG); if (process->pid_ != pid) { ++it; continue; } it = processes_.erase(it); process->died(wstatus); } } /** * \brief Register process with process manager * \param[in] proc Process to register * * This method registers the \a proc with the process manager. It * shall be called by the parent process after successfully forking, in * order to let the parent signal process termination. */ void ProcessManager::registerProcess(Process *proc) { processes_.push_back(proc); } ProcessManager *ProcessManager::self_ = nullptr; /** * \brief Construct a ProcessManager instance * * The ProcessManager class is meant to only be instantiated once, by the * CameraManager. */ ProcessManager::ProcessManager() { if (self_) LOG(Process, Fatal) << "Multiple ProcessManager objects are not allowed"; sigaction(SIGCHLD, NULL, &oldsa_); struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = &sigact; memcpy(&sa.sa_mask, &oldsa_.sa_mask, sizeof(sa.sa_mask)); sigaddset(&sa.sa_mask, SIGCHLD); sa.sa_flags = oldsa_.sa_flags | SA_SIGINFO; sigaction(SIGCHLD, &sa, NULL); if (pipe2(pipe_, O_CLOEXEC | O_DIRECT | O_NONBLOCK)) LOG(Process, Fatal) << "Failed to initialize pipe for signal handling"; sigEvent_ = new EventNotifier(pipe_[0], EventNotifier::Read); sigEvent_->activated.connect(this, &ProcessManager::sighandler); self_ = this; } ProcessManager::~ProcessManager() { sigaction(SIGCHLD, &oldsa_, NULL); delete sigEvent_; close(pipe_[0]); close(pipe_[1]); self_ = nullptr; } /** * \brief Retrieve the Process manager instance * * The ProcessManager is constructed by the CameraManager. This function shall * be used to retrieve the single instance of the manager. * * \return The Process manager instance */ ProcessManager *ProcessManager::instance() { return self_; } /** * \brief Retrieve the Process manager's write pipe * * This method is meant only to be used by the static signal handler. * * \return Pipe for writing */ int ProcessManager::writePipe() const { return pipe_[1]; } /** * \brief Retrive the old signal action data * * This method is meant only to be used by the static signal handler. * * \return The old signal action data */ const struct sigaction &ProcessManager::oldsa() const { return oldsa_; } /** * \class Process * \brief Process object * * The Process class models a process, and simplifies spawning new processes * and monitoring the exiting of a process. */ /** * \enum Process::ExitStatus * \brief Exit status of process * \var Process::NotExited * The process hasn't exited yet * \var Process::NormalExit * The process exited normally, either via exit() or returning from main * \var Process::SignalExit * The process was terminated by a signal (this includes crashing) */ Process::Process() : pid_(-1), running_(false), exitStatus_(NotExited), exitCode_(0) { } Process::~Process() { kill(); /* \todo wait for child process to exit */ } /** * \brief Fork and exec a process, and close fds * \param[in] path Path to executable * \param[in] args Arguments to pass to executable (optional) * \param[in] fds Vector of file descriptors to keep open (optional) * * Fork a process, and exec the executable specified by path. Prior to * exec'ing, but after forking, all file descriptors except for those * specified in fds will be closed. * * All indexes of args will be incremented by 1 before being fed to exec(), * so args[0] should not need to be equal to path. * * \return Zero on successful fork, exec, and closing the file descriptors, * or a negative error code otherwise */ int Process::start(const std::string &path, const std::vector &args, const std::vector &fds) { int ret; if (running_) return 0; int childPid = fork(); if (childPid == -1) { ret = -errno; LOG(Process, Error) << "Failed to fork: " << strerror(-ret); return ret; } else if (childPid) { pid_ = childPid; ProcessManager::instance()->registerProcess(this); running_ = true; return 0; } else { if (isolate()) _exit(EXIT_FAILURE); closeAllFdsExcept(fds); unsetenv("LIBCAMERA_LOG_FILE"); const char **argv = new const char *[args.size() + 2]; unsigned int len = args.size(); argv[0] = path.c_str(); for (unsigned int i = 0; i < len; i++) argv[i+1] = args[i].c_str(); argv[len+1] = nullptr; execv(path.c_str(), (char **)argv); exit(EXIT_FAILURE); } } void Process::closeAllFdsExcept(const std::vector &fds) { std::vector v(fds); sort(v.begin(), v.end()); DIR *dir = opendir("/proc/self/fd"); if (!dir) return; int dfd = dirfd(dir); struct dirent *ent; while ((ent = readdir(dir)) != nullptr) { char *endp; int fd = strtoul(ent->d_name, &endp, 10); if (*endp) continue; if (fd >= 0 && fd != dfd && !std::binary_search(v.begin(), v.end(), fd)) close(fd); } closedir(dir); } int Process::isolate() { int ret = unshare(CLONE_NEWUSER | CLONE_NEWNET); if (ret) { ret = -errno; LOG(Process, Error) << "Failed to unshare execution context: " << strerror(-ret); return ret; } return 0; } /** * \brief SIGCHLD handler * \param[in] wstatus The status as output by waitpid() * * This method is called when the process associated with Process terminates. * It emits the Process::finished signal. */ void Process::died(int wstatus) { running_ = false; exitStatus_ = WIFEXITED(wstatus) ? NormalExit : SignalExit; exitCode_ = exitStatus_ == NormalExit ? WEXITSTATUS(wstatus) : -1; finished.emit(this, exitStatus_, exitCode_); } /** * \fn Process::exitStatus() * \brief Retrieve the exit status of the process * * Return the exit status of the process, that is, whether the process * has exited via exit() or returning from main, or if the process was * terminated by a signal. * * \sa ExitStatus * * \return The process exit status */ /** * \fn Process::exitCode() * \brief Retrieve the exit code of the process * * This method is only valid if exitStatus() returned NormalExit. * * \return Exit code */ /** * \var Process::finished * * Signal that is emitted when the process is confirmed to have terminated. */ /** * \brief Kill the process * * Sends SIGKILL to the process. */ void Process::kill() { if (pid_ > 0) ::kill(pid_, SIGKILL); } } /* namespace libcamera */ 154 155 156 157 158 159 160 161 162 163 164 165 166 167
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * control_serialization.cpp - Serialize and deserialize controls
 */

#include <iostream>

#include <libcamera/camera.h>
#include <libcamera/control_ids.h>
#include <libcamera/controls.h>

#include "libcamera/internal/byte_stream_buffer.h"
#include "libcamera/internal/control_serializer.h"

#include "serialization_test.h"
#include "test.h"

using namespace std;
using namespace libcamera;

class ControlSerializationTest : public SerializationTest
{
protected:
	int init() override
	{
		return status_;
	}

	int run() override
	{
		ControlSerializer serializer;
		ControlSerializer deserializer;

		std::vector<uint8_t> infoData;
		std::vector<uint8_t> listData;

		size_t size;
		int ret;

		/* Create a control list with three controls. */
		const ControlInfoMap &infoMap = camera_->controls();
		ControlList list(infoMap);

		list.set(controls::Brightness, 0.5f);
		list.set(controls::Contrast, 1.2f);
		list.set(controls::Saturation, 0.2f);

		/*
		 * Serialize the control list, this should fail as the control
		 * info map hasn't been serialized.
		 */
		size = serializer.binarySize(list);
		listData.resize(size);
		ByteStreamBuffer buffer(listData.data(), listData.size());

		ret = serializer.serialize(list, buffer);
		if (!ret) {
			cerr << "List serialization without info map should have failed"
			     << endl;
			return TestFail;
		}

		if (buffer.overflow() || buffer.offset()) {
			cerr << "Failed list serialization modified the buffer"
			     << endl;
			return TestFail;
		}

		/* Serialize the control info map. */
		size = serializer.binarySize(infoMap);
		infoData.resize(size);
		buffer = ByteStreamBuffer(infoData.data(), infoData.size());

		ret = serializer.serialize(infoMap, buffer);
		if (ret < 0) {
			cerr << "Failed to serialize ControlInfoMap" << endl;
			return TestFail;
		}

		if (buffer.overflow()) {
			cerr << "Overflow when serializing ControlInfoMap" << endl;
			return TestFail;
		}

		/* Serialize the control list, this should now succeed. */
		size = serializer.binarySize(list);
		listData.resize(size);
		buffer = ByteStreamBuffer(listData.data(), listData.size());

		ret = serializer.serialize(list, buffer);
		if (ret) {
			cerr << "Failed to serialize ControlList" << endl;
			return TestFail;
		}

		if (buffer.overflow()) {
			cerr << "Overflow when serializing ControlList" << endl;
			return TestFail;
		}

		/*
		 * Deserialize the control list, this should fail as the control
		 * info map hasn't been deserialized.
		 */
		buffer = ByteStreamBuffer(const_cast<const uint8_t *>(listData.data()),
					  listData.size());

		ControlList newList = deserializer.deserialize<ControlList>(buffer);
		if (!newList.empty()) {
			cerr << "List deserialization without info map should have failed"
			     << endl;
			return TestFail;
		}

		if (buffer.overflow()) {
			cerr << "Failed list deserialization modified the buffer"
			     << endl;
			return TestFail;
		}

		/* Deserialize the control info map and verify the contents. */
		buffer = ByteStreamBuffer(const_cast<const uint8_t *>(infoData.data()),
					  infoData.size());

		ControlInfoMap newInfoMap = deserializer.deserialize<ControlInfoMap>(buffer);
		if (newInfoMap.empty()) {
			cerr << "Failed to deserialize ControlInfoMap" << endl;
			return TestFail;
		}

		if (buffer.overflow()) {
			cerr << "Overflow when deserializing ControlInfoMap" << endl;
			return TestFail;
		}

		if (!equals(infoMap, newInfoMap)) {
			cerr << "Deserialized map doesn't match original" << endl;
			return TestFail;
		}

		/* Deserialize the control list and verify the contents. */
		buffer = ByteStreamBuffer(const_cast<const uint8_t *>(listData.data()),
					  listData.size());

		newList = deserializer.deserialize<ControlList>(buffer);
		if (newList.empty()) {
			cerr << "Failed to deserialize ControlList" << endl;
			return TestFail;
		}

		if (buffer.overflow()) {
			cerr << "Overflow when deserializing ControlList" << endl;
			return TestFail;
		}

		if (!equals(list, newList)) {
			cerr << "Deserialized list doesn't match original" << endl;
			return TestFail;
		}

		return TestPass;
	}
};

TEST_REGISTER(ControlSerializationTest)