blob: 4b656872ee1b1bf1cf7055802c386c82b93284cc [file] [log] [blame]
James Kuszmaul011b67a2019-12-15 12:52:34 -08001#include "aos/logging/log_namer.h"
2
3#include <dirent.h>
James Kuszmaul011b67a2019-12-15 12:52:34 -08004#include <mntent.h>
James Kuszmaul011b67a2019-12-15 12:52:34 -08005#include <unistd.h>
Tyler Chatowbf0609c2021-07-31 16:13:27 -07006
7#include <cerrno>
8#include <cstdio>
9#include <cstdlib>
10#include <cstring>
Stephan Pleines0960c262024-05-31 20:29:24 -070011#include <ostream>
James Kuszmaul011b67a2019-12-15 12:52:34 -080012#include <string>
13
Stephan Pleines0960c262024-05-31 20:29:24 -070014#include "gflags/gflags.h"
James Kuszmaul011b67a2019-12-15 12:52:34 -080015#include "glog/logging.h"
16
Adam Snaider13d48d92023-08-03 12:20:15 -070017#if defined(__clang)
18#pragma clang diagnostic ignored "-Wformat-nonliteral"
19#elif defined(__GNUC__)
20#pragma GCC diagnostic ignored "-Wformat-nonliteral"
21#endif
22
Austin Schuh03e80a62019-12-28 15:18:54 -080023DEFINE_string(logging_folder,
24#ifdef AOS_ARCHITECTURE_arm_frc
25 "",
26#else
27 "./logs",
28#endif
29 "The folder to log to. If empty, search for the /media/sd*1/ "
30 "folder and place logs there.");
31
Stephan Pleinesf63bde82024-01-13 15:59:33 -080032namespace aos::logging {
James Kuszmaul011b67a2019-12-15 12:52:34 -080033namespace {
34void AllocateLogName(char **filename, const char *directory,
35 const char *basename) {
36 int fileindex = 0;
37 DIR *const d = opendir(directory);
38 if (d == nullptr) {
39 PLOG(FATAL) << "could not open directory" << directory;
40 }
41 int index = 0;
42 while (true) {
43 errno = 0;
44 struct dirent *const dir = readdir(d);
45 if (dir == nullptr) {
46 if (errno == 0) {
47 break;
48 } else {
49 PLOG(FATAL) << "readdir(" << d << ") failed";
50 }
51 } else {
52 const std::string format_string = std::string(basename) + "-%d";
53 if (sscanf(dir->d_name, format_string.c_str(), &index) == 1) {
54 if (index >= fileindex) {
55 fileindex = index + 1;
56 }
57 }
58 }
59 }
60 closedir(d);
61
62 char previous[512];
63 ::std::string path = ::std::string(directory) + "/" + basename + "-current";
64 ssize_t len = ::readlink(path.c_str(), previous, sizeof(previous));
65 if (len != -1) {
66 previous[len] = '\0';
67 } else {
68 previous[0] = '\0';
69 LOG(INFO) << "Could not find " << path;
70 }
71 if (asprintf(filename, "%s/%s-%03d", directory, basename, fileindex) == -1) {
72 PLOG(FATAL) << "couldn't create final name";
73 }
James Kuszmaul131aa042021-08-01 17:28:50 -070074 // Fix basename formatting.
Austin Schuh2b378b42024-02-26 22:01:04 -080075 LOG(INFO) << "Created log file (" << *filename << "). Previous file was ("
James Kuszmaul131aa042021-08-01 17:28:50 -070076 << directory << "/" << previous << ").";
James Kuszmaul011b67a2019-12-15 12:52:34 -080077}
78
James Kuszmaul011b67a2019-12-15 12:52:34 -080079bool FoundThumbDrive(const char *path) {
80 FILE *mnt_fp = setmntent("/etc/mtab", "r");
81 if (mnt_fp == nullptr) {
82 LOG(FATAL) << "Could not open /etc/mtab";
83 }
84
85 bool found = false;
86 struct mntent mntbuf;
87 char buf[256];
88 while (!found) {
89 struct mntent *mount_list = getmntent_r(mnt_fp, &mntbuf, buf, sizeof(buf));
90 if (mount_list == nullptr) {
91 break;
92 }
93 if (strcmp(mount_list->mnt_dir, path) == 0) {
94 found = true;
95 }
96 }
97 endmntent(mnt_fp);
98 return found;
99}
100
101bool FindDevice(char *device, size_t device_size) {
102 char test_device[10];
103 for (char i = 'a'; i < 'z'; ++i) {
104 snprintf(test_device, sizeof(test_device), "/dev/sd%c", i);
Austin Schuh73fcab12020-02-22 14:59:23 -0800105 VLOG(1) << "Trying to access" << test_device;
James Kuszmaul011b67a2019-12-15 12:52:34 -0800106 if (access(test_device, F_OK) != -1) {
107 snprintf(device, device_size, "sd%c", i);
108 return true;
109 }
110 }
111 return false;
112}
Austin Schuh03e80a62019-12-28 15:18:54 -0800113
James Kuszmaul011b67a2019-12-15 12:52:34 -0800114} // namespace
115
Ravago Jones1a4bc762023-04-09 16:21:57 -0700116std::optional<std::string> MaybeGetLogName(const char *basename) {
Austin Schuh03e80a62019-12-28 15:18:54 -0800117 if (FLAGS_logging_folder.empty()) {
118 char folder[128];
119 {
120 char dev_name[8];
Ravago Jones1a4bc762023-04-09 16:21:57 -0700121 if (!FindDevice(dev_name, sizeof(dev_name))) {
Austin Schuh03e80a62019-12-28 15:18:54 -0800122 LOG(INFO) << "Waiting for a device";
Ravago Jones1a4bc762023-04-09 16:21:57 -0700123 return std::nullopt;
Austin Schuh03e80a62019-12-28 15:18:54 -0800124 }
125 snprintf(folder, sizeof(folder), "/media/%s1", dev_name);
Ravago Jones1a4bc762023-04-09 16:21:57 -0700126 if (!FoundThumbDrive(folder)) {
Austin Schuh03e80a62019-12-28 15:18:54 -0800127 LOG(INFO) << "Waiting for" << folder;
Ravago Jones1a4bc762023-04-09 16:21:57 -0700128 return std::nullopt;
Austin Schuh03e80a62019-12-28 15:18:54 -0800129 }
130 snprintf(folder, sizeof(folder), "/media/%s1/", dev_name);
James Kuszmaul011b67a2019-12-15 12:52:34 -0800131 }
James Kuszmaul011b67a2019-12-15 12:52:34 -0800132
Austin Schuh03e80a62019-12-28 15:18:54 -0800133 if (access(folder, F_OK) == -1) {
134 LOG(FATAL) << "folder '" << folder
135 << "' does not exist. please create it.";
136 }
137
138 FLAGS_logging_folder = folder;
139 }
140 const char *folder = FLAGS_logging_folder.c_str();
James Kuszmaul011b67a2019-12-15 12:52:34 -0800141 if (access(folder, R_OK | W_OK) == -1) {
James Kuszmaul011b67a2019-12-15 12:52:34 -0800142 LOG(FATAL) << "folder '" << folder << "' does not exist. please create it.";
143 }
144 LOG(INFO) << "logging to folder '" << folder << "'";
145
146 char *tmp;
147 AllocateLogName(&tmp, folder, basename);
Austin Schuh2d0471d2020-02-29 13:27:07 -0800148
149 std::string log_base_name = tmp;
Austin Schuhe715eae2020-10-10 15:39:30 -0700150 std::string log_roborio_name = log_base_name + "/";
Austin Schuh2d0471d2020-02-29 13:27:07 -0800151 free(tmp);
152
James Kuszmaul011b67a2019-12-15 12:52:34 -0800153 char *tmp2;
Austin Schuhe715eae2020-10-10 15:39:30 -0700154 if (asprintf(&tmp2, "%s/%s-current", folder, basename) == -1) {
James Kuszmaul011b67a2019-12-15 12:52:34 -0800155 PLOG(WARNING) << "couldn't create current symlink name";
156 } else {
157 if (unlink(tmp2) == -1 && (errno != EROFS && errno != ENOENT)) {
158 LOG(WARNING) << "unlink('" << tmp2 << "') failed";
159 }
Austin Schuh2d0471d2020-02-29 13:27:07 -0800160 if (symlink(log_roborio_name.c_str(), tmp2) == -1) {
161 PLOG(WARNING) << "symlink('" << log_roborio_name.c_str() << "', '" << tmp2
162 << "') failed";
James Kuszmaul011b67a2019-12-15 12:52:34 -0800163 }
164 free(tmp2);
165 }
Austin Schuh2d0471d2020-02-29 13:27:07 -0800166 return log_base_name;
James Kuszmaul011b67a2019-12-15 12:52:34 -0800167}
168
Ravago Jones1a4bc762023-04-09 16:21:57 -0700169std::string GetLogName(const char *basename) {
170 std::optional<std::string> log_base_name;
171
172 while (true) {
173 log_base_name = MaybeGetLogName(basename);
174
175 if (log_base_name.has_value()) {
176 break;
177 }
178
179 sleep(5);
180 }
181
182 return log_base_name.value();
183}
184
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800185} // namespace aos::logging