$NetBSD$ * Part of patchset to build electron on NetBSD * Based on OpenBSD's chromium patches, and FreeBSD's electron patches --- chrome/browser/process_singleton_posix.cc.orig 2025-02-24 19:59:26.000000000 +0000 +++ chrome/browser/process_singleton_posix.cc @@ -59,6 +59,7 @@ #include #include #include +#include #include #include "base/base_paths.h" @@ -86,6 +87,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/task/sequenced_task_runner_helpers.h" #include "base/task/single_thread_task_runner.h" +#include "base/threading/thread_restrictions.h" #include "base/threading/platform_thread.h" #include "base/time/time.h" #include "base/timer/timer.h" @@ -102,7 +104,7 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/scoped_startup_resource_bundle.h" -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#if 0 #include "chrome/browser/ui/process_singleton_dialog_linux.h" #endif @@ -348,6 +350,8 @@ bool SymlinkPath(const base::FilePath& t bool DisplayProfileInUseError(const base::FilePath& lock_path, const std::string& hostname, int pid) { + return true; +#if 0 // Ensure there is an instance of ResourceBundle that is initialized for // localized string resource accesses. ui::ScopedStartupResourceBundle ensure_startup_resource_bundle; @@ -360,7 +364,7 @@ bool DisplayProfileInUseError(const base if (g_disable_prompt) return g_user_opted_unlock_in_use_profile; -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_BSD) std::u16string relaunch_button_text = l10n_util::GetStringUTF16(IDS_PROFILE_IN_USE_LINUX_RELAUNCH); return ShowProcessSingletonDialog(error, relaunch_button_text); @@ -370,6 +374,8 @@ bool DisplayProfileInUseError(const base #endif NOTREACHED(); + return false; +#endif } bool IsChromeProcess(pid_t pid) { @@ -382,6 +388,21 @@ bool IsChromeProcess(pid_t pid) { base::FilePath(chrome::kBrowserProcessExecutableName)); } +bool IsAppSandboxed() { +#if BUILDFLAG(IS_MAC) + // NB: There is no sane API for this, we have to just guess by + // reading tea leaves + base::FilePath home_dir; + if (!base::PathService::Get(base::DIR_HOME, &home_dir)) { + return false; + } + + return home_dir.value().find("Library/Containers") != std::string::npos; +#else + return false; +#endif // BUILDFLAG(IS_MAC) +} + // A helper class to hold onto a socket. class ScopedSocket { public: @@ -594,6 +615,7 @@ class ProcessSingleton::LinuxWatcher // |reader| is for sending back ACK message. void HandleMessage(const std::string& current_dir, const std::vector& argv, + const std::vector additional_data, SocketReader* reader); // Called when the ProcessSingleton that owns this class is about to be @@ -653,13 +675,17 @@ void ProcessSingleton::LinuxWatcher::Sta } void ProcessSingleton::LinuxWatcher::HandleMessage( - const std::string& current_dir, const std::vector& argv, + const std::string& current_dir, + const std::vector& argv, + const std::vector additional_data, SocketReader* reader) { DCHECK(ui_task_runner_->BelongsToCurrentThread()); DCHECK(reader); if (parent_ && parent_->notification_callback_.Run( - base::CommandLine(argv), base::FilePath(current_dir))) { + base::CommandLine(argv), + base::FilePath(current_dir), + std::move(additional_data))) { // Send back "ACK" message to prevent the client process from starting up. reader->FinishWithACK(kACKToken, std::size(kACKToken) - 1); } else { @@ -707,7 +733,8 @@ void ProcessSingleton::LinuxWatcher::Soc } } - // Validate the message. The shortest message is kStartToken\0x\0x + // Validate the message. The shortest message kStartToken\0\00 + // The shortest message with additional data is kStartToken\0\00\00\0. const size_t kMinMessageLength = std::size(kStartToken) + 4; if (bytes_read_ < kMinMessageLength) { buf_[bytes_read_] = 0; @@ -737,10 +764,28 @@ void ProcessSingleton::LinuxWatcher::Soc tokens.erase(tokens.begin()); tokens.erase(tokens.begin()); + size_t num_args; + base::StringToSizeT(tokens[0], &num_args); + std::vector command_line(tokens.begin() + 1, tokens.begin() + 1 + num_args); + + std::vector additional_data; + if (tokens.size() >= 3 + num_args) { + size_t additional_data_size; + base::StringToSizeT(tokens[1 + num_args], &additional_data_size); + std::string remaining_args = base::JoinString( + base::make_span(tokens.begin() + 2 + num_args, tokens.end()), + std::string(1, kTokenDelimiter)); + const uint8_t* additional_data_bits = + reinterpret_cast(remaining_args.c_str()); + additional_data = std::vector( + additional_data_bits, additional_data_bits + additional_data_size); + } + // Return to the UI thread to handle opening a new browser tab. ui_task_runner_->PostTask( FROM_HERE, base::BindOnce(&ProcessSingleton::LinuxWatcher::HandleMessage, - parent_, current_dir, tokens, this)); + parent_, current_dir, command_line, + std::move(additional_data), this)); fd_watch_controller_.reset(); // LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader @@ -769,8 +814,10 @@ void ProcessSingleton::LinuxWatcher::Soc // ProcessSingleton::ProcessSingleton( const base::FilePath& user_data_dir, + const base::raw_span additional_data, const NotificationCallback& notification_callback) : notification_callback_(notification_callback), + additional_data_(additional_data), current_pid_(base::GetCurrentProcId()) { socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename); lock_path_ = user_data_dir.Append(chrome::kSingletonLockFilename); @@ -785,6 +832,10 @@ ProcessSingleton::~ProcessSingleton() { if (watcher_) { watcher_->OnEminentProcessSingletonDestruction(); } + // Manually free resources with IO explicitly allowed. + base::ScopedAllowBlocking allow_blocking; + watcher_ = nullptr; + std::ignore = socket_dir_.Delete(); } ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() { @@ -887,7 +938,8 @@ ProcessSingleton::NotifyResult ProcessSi sizeof(socket_timeout)); // Found another process, prepare our command line - // format is "START\0\0\0...\0". + // format is "START\0\0\0\0...\0 + // \0\0". std::string to_send(kStartToken); to_send.push_back(kTokenDelimiter); @@ -897,11 +949,21 @@ ProcessSingleton::NotifyResult ProcessSi to_send.append(current_dir.value()); const std::vector& argv = cmd_line.argv(); + to_send.push_back(kTokenDelimiter); + to_send.append(base::NumberToString(argv.size())); for (auto it = argv.begin(); it != argv.end(); ++it) { to_send.push_back(kTokenDelimiter); to_send.append(*it); } + size_t data_to_send_size = additional_data_.size_bytes(); + if (data_to_send_size) { + to_send.push_back(kTokenDelimiter); + to_send.append(base::NumberToString(data_to_send_size)); + to_send.push_back(kTokenDelimiter); + to_send.append(reinterpret_cast(additional_data_.data()), data_to_send_size); + } + // Send the message if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) { // Try to kill the other process, because it might have been dead. @@ -1055,11 +1117,32 @@ bool ProcessSingleton::Create() { // Create the socket file somewhere in /tmp which is usually mounted as a // normal filesystem. Some network filesystems (notably AFS) are screwy and // do not support Unix domain sockets. - if (!socket_dir_.CreateUniqueTempDir()) { - LOG(ERROR) << "Failed to create socket directory."; + base::FilePath tmp_dir; + if (!base::GetTempDir(&tmp_dir)) { + LOG(ERROR) << "Failed to get temporary directory."; return false; } + if (IsAppSandboxed()) { + // For sandboxed applications, the tmp dir could be too long to fit + // addr->sun_path, so we need to make it as short as possible. + if (!socket_dir_.Set(tmp_dir.Append("S"))) { + LOG(ERROR) << "Failed to set socket directory."; + return false; + } + } else { + // Create the socket file somewhere in /tmp which is usually mounted as a + // normal filesystem. Some network filesystems (notably AFS) are screwy and + // do not support Unix domain sockets. + // Prefer CreateUniqueTempDirUnderPath rather than CreateUniqueTempDir as + // the latter will calculate unique paths based on bundle ids which can + // increase the socket path length than what is allowed. + if (!socket_dir_.CreateUniqueTempDirUnderPath(tmp_dir)) { + LOG(ERROR) << "Failed to create socket directory."; + return false; + } + } + // Check that the directory was created with the correct permissions. int dir_mode = 0; CHECK(base::GetPosixFilePermissions(socket_dir_.GetPath(), &dir_mode) &&