blob: 4cda4dae94025127312c4d05ea490f9c18add86e [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/host_wtmpdb_logger.h"
#include <libgen.h>
#include <pty.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <string>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/strings/string_number_conversions.h"
#include "remoting/base/logging.h"
#include "remoting/host/host_status_monitor.h"
#include "sql/database.h"
#include "sql/statement.h"
namespace remoting {
namespace {
// Name to pass to wtmpdb as host.
constexpr char kApplicationName[] = "chromoting";
constexpr base::FilePath::CharType kDbPath[] =
FILE_PATH_LITERAL("/var/lib/wtmpdb/wtmp.db");
constexpr int kUserProcess = 3;
uint64_t GetCurrentTimeMicros() {
timeval now;
gettimeofday(&now, nullptr);
return now.tv_sec * ((uint64_t)1000000ULL) + now.tv_usec;
}
} // namespace
HostWtmpdbLogger::HostWtmpdbLogger(scoped_refptr<HostStatusMonitor> monitor)
: monitor_(monitor) {
monitor_->AddStatusObserver(this);
}
HostWtmpdbLogger::~HostWtmpdbLogger() {
monitor_->RemoveStatusObserver(this);
}
void HostWtmpdbLogger::OnClientConnected(const std::string& signaling_id) {
int pty, replica_pty;
if (openpty(&pty, &replica_pty, nullptr, nullptr, nullptr)) {
PLOG(ERROR) << "Failed to open pty for wtmpdb logging";
return;
}
close(replica_pty);
sql::Database db(sql::DatabaseOptions{}, sql::Database::Tag("Chromoting"));
base::FilePath file_path(kDbPath);
base::CreateDirectory(file_path.DirName());
if (!db.Open(file_path)) {
PLOG(ERROR) << "Failed to open wtmpdb";
close(pty);
return;
}
constexpr base::cstring_view sql_table(
"CREATE TABLE IF NOT EXISTS wtmp(ID INTEGER PRIMARY KEY, "
"Type INTEGER, User TEXT NOT NULL, Login INTEGER, "
"Logout INTEGER, TTY TEXT, RemoteHost TEXT, Service TEXT) STRICT;");
if (!db.Execute(sql_table)) {
PLOG(ERROR) << "Failed to create wtmp table";
close(pty);
return;
}
constexpr base::cstring_view sql_insert(
"INSERT INTO wtmp (Type,User,Login,TTY,RemoteHost,Service) "
"VALUES(?,?,?,?,?,?);");
sql::Statement statement(db.GetCachedStatement(SQL_FROM_HERE, sql_insert));
if (!statement.is_valid()) {
PLOG(ERROR) << "Failed to prepare wtmpdb login query";
close(pty);
return;
}
statement.BindInt(/*param_index=*/0, /*Type=*/kUserProcess);
statement.BindString(/*param_index=*/1, /*User=*/kApplicationName);
statement.BindInt64(/*param_index=*/2, /*Login=*/GetCurrentTimeMicros());
statement.BindString(/*param_index=*/3, /*TTY=*/base::NumberToString(pty));
statement.BindString(/*param_index=*/4, /*RemoteHost=*/kApplicationName);
statement.BindString(/*param_index=*/5, /*Service=*/"chrome-remote-desktop");
if (!statement.Run()) {
PLOG(ERROR) << "Failed to insert wtmpdb entry";
close(pty);
return;
}
session_.emplace(signaling_id, ConnectionInfo{pty, db.GetLastInsertRowId()});
}
void HostWtmpdbLogger::OnClientDisconnected(const std::string& signaling_id) {
auto sess_iter = session_.find(signaling_id);
if (sess_iter == session_.end()) {
return;
}
sql::Database db(sql::DatabaseOptions{}, sql::Database::Tag("Chromoting"));
base::FilePath file_path(kDbPath);
if (!db.Open(file_path)) {
PLOG(ERROR) << "Failed to open wtmp.db";
close(sess_iter->second.pty_id);
session_.erase(signaling_id);
return;
}
constexpr base::cstring_view sql("UPDATE wtmp SET Logout = ? WHERE ID = ?");
sql::Statement statement(db.GetCachedStatement(SQL_FROM_HERE, sql));
if (!statement.is_valid()) {
PLOG(ERROR) << "Failed to prepare wtmpdb logout query";
close(sess_iter->second.pty_id);
session_.erase(signaling_id);
return;
}
statement.BindInt64(/*param_index=*/0, /*Logout=*/GetCurrentTimeMicros());
statement.BindInt64(/*param_index=*/1, /*ID=*/sess_iter->second.wtmpdb_id);
if (!statement.Run()) {
PLOG(ERROR) << "Failed to update wtmpdb entry";
}
close(sess_iter->second.pty_id);
session_.erase(signaling_id);
}
} // namespace remoting