blob: 0e68e7b8c473220f4610cc476354e820a828dd2b [file] [log] [blame]
#include "aos/logging/log_namer.h"
#include <dirent.h>
#include <mntent.h>
#include <unistd.h>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ostream>
#include <string>
#include "absl/flags/flag.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "aos/configuration.h"
#include "aos/time/time.h"
#if defined(__clang)
#pragma clang diagnostic ignored "-Wformat-nonliteral"
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
ABSL_FLAG(std::string, logging_folder,
#ifdef AOS_ARCHITECTURE_arm_frc
"",
#else
"./logs",
#endif
"The folder to log to. If empty, search for the /media/sd*1/ "
"folder and place logs there.");
namespace aos::logging {
namespace {
void AllocateLogName(char **filename, const char *directory,
const char *basename) {
int fileindex = 0;
DIR *const d = opendir(directory);
if (d == nullptr) {
PLOG(FATAL) << "could not open directory" << directory;
}
int index = 0;
while (true) {
errno = 0;
struct dirent *const dir = readdir(d);
if (dir == nullptr) {
if (errno == 0) {
break;
} else {
PLOG(FATAL) << "readdir(" << d << ") failed";
}
} else {
char previous_date[512];
// Look for previous index and date
const std::string format_string = std::string(basename) + "-%d_%s";
if (sscanf(dir->d_name, format_string.c_str(), &index, &previous_date) ==
2) {
if (index >= fileindex) {
fileindex = index + 1;
}
}
}
}
closedir(d);
char previous[512];
::std::string path = ::std::string(directory) + "/" + basename + "-current";
ssize_t len = ::readlink(path.c_str(), previous, sizeof(previous));
if (len != -1) {
previous[len] = '\0';
} else {
previous[0] = '\0';
LOG(INFO) << "Could not find " << path;
}
// Remove subsecond accuracy (after the "."). We don't need it, and it makes
// the string very long
std::string time_short = aos::ToString(aos::realtime_clock::now());
time_short = time_short.substr(0, time_short.find("."));
if (asprintf(filename, "%s/%s-%03d_%s", directory, basename, fileindex,
time_short.c_str()) == -1) {
PLOG(FATAL) << "couldn't create final name";
}
// Fix basename formatting.
LOG(INFO) << "Created log file (" << *filename << "). Previous file was ("
<< directory << "/" << previous << ").";
}
bool FoundThumbDrive(const char *path) {
FILE *mnt_fp = setmntent("/etc/mtab", "r");
if (mnt_fp == nullptr) {
LOG(FATAL) << "Could not open /etc/mtab";
}
bool found = false;
struct mntent mntbuf;
char buf[256];
while (!found) {
struct mntent *mount_list = getmntent_r(mnt_fp, &mntbuf, buf, sizeof(buf));
if (mount_list == nullptr) {
break;
}
if (strcmp(mount_list->mnt_dir, path) == 0) {
found = true;
}
}
endmntent(mnt_fp);
return found;
}
bool FindDevice(char *device, size_t device_size) {
char test_device[10];
for (char i = 'a'; i < 'z'; ++i) {
snprintf(test_device, sizeof(test_device), "/dev/sd%c", i);
VLOG(1) << "Trying to access" << test_device;
if (access(test_device, F_OK) != -1) {
snprintf(device, device_size, "sd%c", i);
return true;
}
}
return false;
}
} // namespace
std::optional<std::string> MaybeGetLogName(const char *basename) {
if (absl::GetFlag(FLAGS_logging_folder).empty()) {
char folder[128];
{
char dev_name[8];
if (!FindDevice(dev_name, sizeof(dev_name))) {
LOG(INFO) << "Waiting for a device";
return std::nullopt;
}
snprintf(folder, sizeof(folder), "/media/%s1", dev_name);
if (!FoundThumbDrive(folder)) {
LOG(INFO) << "Waiting for" << folder;
return std::nullopt;
}
snprintf(folder, sizeof(folder), "/media/%s1/", dev_name);
}
if (access(folder, F_OK) == -1) {
LOG(FATAL) << "folder '" << folder
<< "' does not exist. please create it.";
}
absl::SetFlag(&FLAGS_logging_folder, folder);
}
const std::string folder = absl::GetFlag(FLAGS_logging_folder);
if (access(folder.c_str(), R_OK | W_OK) == -1) {
LOG(FATAL) << "folder '" << folder << "' does not exist. please create it.";
}
LOG(INFO) << "logging to folder '" << folder << "'";
char *tmp;
AllocateLogName(&tmp, folder.c_str(), basename);
std::string log_base_name = tmp;
std::string log_roborio_name = log_base_name + "/";
free(tmp);
char *tmp2;
if (asprintf(&tmp2, "%s/%s-current", folder.c_str(), basename) == -1) {
PLOG(WARNING) << "couldn't create current symlink name";
} else {
if (unlink(tmp2) == -1 && (errno != EROFS && errno != ENOENT)) {
LOG(WARNING) << "unlink('" << tmp2 << "') failed";
}
if (symlink(log_roborio_name.c_str(), tmp2) == -1) {
PLOG(WARNING) << "symlink('" << log_roborio_name.c_str() << "', '" << tmp2
<< "') failed";
}
free(tmp2);
}
return log_base_name;
}
std::string GetLogName(const char *basename) {
std::optional<std::string> log_base_name;
while (true) {
log_base_name = MaybeGetLogName(basename);
if (log_base_name.has_value()) {
break;
}
sleep(5);
}
return log_base_name.value();
}
} // namespace aos::logging