#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "chrome/browser/process_singleton.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stddef.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <cstring>
#include <memory>
#include <set>
#include <string>
#include <type_traits>
#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/files/file_descriptor_watcher_posix.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/path_service.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/safe_strerror.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#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/platform_thread.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/process_singleton_internal.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/process_singleton_lock_posix.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/network_interfaces.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/scoped_startup_resource_bundle.h"
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/ui/process_singleton_dialog_linux.h"
#endif
BrowserThread;
namespace {
#if BUILDFLAG(IS_MAC)
struct SockaddrUn {
decltype(sockaddr_un::sun_len) sun_len;
decltype(sockaddr_un::sun_family) sun_family;
std::remove_extent_t<decltype(sockaddr_un::sun_path)>
sun_path[SOCK_MAXADDRLEN - offsetof(sockaddr_un, sun_path)];
};
#else
SockaddrUn;
#endif
const int kTimeoutInSeconds = …;
const int kRetryAttempts = …;
const char kStartToken[] = …;
const char kACKToken[] = …;
const char kShutdownToken[] = …;
const char kTokenDelimiter = …;
const int kMaxMessageLength = …;
const int kMaxACKMessageLength = …;
bool g_disable_prompt = …;
bool g_skip_is_chrome_process_check = …;
bool g_user_opted_unlock_in_use_profile = …;
int SetCloseOnExec(int fd) { … }
void CloseSocket(int fd) { … }
bool WriteToSocket(int fd, const char *message, size_t length) { … }
struct timeval TimeDeltaToTimeVal(const base::TimeDelta& delta) { … }
int WaitSocketForRead(int fd, const base::TimeDelta& timeout) { … }
ssize_t ReadFromSocket(int fd,
char* buf,
size_t bufsize,
const base::TimeDelta& timeout) { … }
bool SetupSockAddr(const std::string& path,
SockaddrUn* addr,
socklen_t* socklen) { … }
int SetupSocketOnly() { … }
void SetupSocket(const std::string& path,
int* sock,
SockaddrUn* addr,
socklen_t* socklen) { … }
base::FilePath ReadLink(const base::FilePath& path) { … }
bool UnlinkPath(const base::FilePath& path) { … }
bool SymlinkPath(const base::FilePath& target, const base::FilePath& path) { … }
bool DisplayProfileInUseError(const base::FilePath& lock_path,
const std::string& hostname,
int pid) { … }
bool IsChromeProcess(pid_t pid) { … }
class ScopedSocket { … };
std::string GenerateCookie() { … }
bool CheckCookie(const base::FilePath& path, const base::FilePath& cookie) { … }
bool ConnectSocket(ScopedSocket* socket,
const base::FilePath& socket_path,
const base::FilePath& cookie_path) { … }
#if BUILDFLAG(IS_MAC)
bool ReplaceOldSingletonLock(const base::FilePath& symlink_content,
const base::FilePath& lock_path) {
base::ScopedFD lock_fd(HANDLE_EINTR(
open(lock_path.value().c_str(), O_RDWR | O_CREAT | O_SYMLINK, 0644)));
if (!lock_fd.is_valid()) {
PLOG(ERROR) << "Could not open singleton lock";
return false;
}
int rc = HANDLE_EINTR(flock(lock_fd.get(), LOCK_EX | LOCK_NB));
if (rc == -1) {
if (errno == EWOULDBLOCK) {
LOG(ERROR) << "Singleton lock held by old process.";
} else {
PLOG(ERROR) << "Error locking singleton lock";
}
return false;
}
if (!base::DeleteFile(lock_path)) {
PLOG(ERROR) << "Could not delete old singleton lock.";
return false;
}
return SymlinkPath(symlink_content, lock_path);
}
#endif
}
class ProcessSingleton::LinuxWatcher
: public base::RefCountedThreadSafe<ProcessSingleton::LinuxWatcher,
BrowserThread::DeleteOnIOThread> { … };
void ProcessSingleton::LinuxWatcher::OnSocketCanReadWithoutBlocking(
int socket) { … }
void ProcessSingleton::LinuxWatcher::StartListening(int socket) { … }
void ProcessSingleton::LinuxWatcher::HandleMessage(
const std::string& current_dir, const std::vector<std::string>& argv,
SocketReader* reader) { … }
void ProcessSingleton::LinuxWatcher::RemoveSocketReader(SocketReader* reader) { … }
void ProcessSingleton::LinuxWatcher::SocketReader::
OnSocketCanReadWithoutBlocking() { … }
void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
const char *message, size_t length) { … }
ProcessSingleton::ProcessSingleton(
const base::FilePath& user_data_dir,
const NotificationCallback& notification_callback)
: … { … }
ProcessSingleton::~ProcessSingleton() { … }
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() { … }
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
const base::CommandLine& cmd_line,
int retry_attempts,
const base::TimeDelta& timeout,
bool kill_unresponsive) { … }
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() { … }
ProcessSingleton::NotifyResult
ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate(
const base::CommandLine& command_line,
int retry_attempts,
const base::TimeDelta& timeout) { … }
void ProcessSingleton::OverrideCurrentPidForTesting(base::ProcessId pid) { … }
void ProcessSingleton::OverrideKillCallbackForTesting(
const base::RepeatingCallback<void(int)>& callback) { … }
void ProcessSingleton::DisablePromptForTesting() { … }
void ProcessSingleton::SkipIsChromeProcessCheckForTesting(bool skip) { … }
void ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting(
bool set_unlock) { … }
bool ProcessSingleton::Create() { … }
void ProcessSingleton::StartWatching() { … }
void ProcessSingleton::Cleanup() { … }
bool ProcessSingleton::IsSameChromeInstance(pid_t pid) { … }
bool ProcessSingleton::KillProcessByLockPath(bool is_connected_to_socket) { … }
void ProcessSingleton::KillProcess(int pid) { … }