AC_DEFINE_UNQUOTED([DLOG_CRITICAL_LOGFILE_PATH], "$critical_logfile")
AC_SUBST([DLOG_CRITICAL_LOGFILE_PATH], "$critical_logfile")
+critical_logfile_size_default=1048576 # 1 MB
+AC_ARG_ENABLE([critical-logsize],
+ AS_HELP_STRING([--critical-log-size=SIZE], [size of the critical log file in bytes (default: $critical_logfile_size_default)]),
+ [critical_logsize=$enableval],
+ [critical_logsize=$critical_logfile_size_default])
+AC_DEFINE_UNQUOTED([DLOG_CRITICAL_LOGFILE_SIZE], [$critical_logsize])
+AC_SUBST([DLOG_CRITICAL_LOGFILE_SIZE], [$critical_logsize])
+
AC_ARG_ENABLE([android-monotonic],
AS_HELP_STRING([--enable-android-monotonic], [use monotonic send timestamp with log message when using android backend (EXPERIMENTAL)]),
[android_monotonic=true],
/sbin/ldconfig
systemctl daemon-reload
chsmack -a System /var/log/dlog
-touch /var/log/dlog/critical
-chmod 660 /var/log/dlog/critical
-chown log:log /var/log/dlog/critical
+touch /var/log/dlog/critical.a
+touch /var/log/dlog/critical.b
+chmod 660 /var/log/dlog/critical*
+chown log:log /var/log/dlog/critical*
chsmack -e 'System' %{_libexecdir}/dlog-log-critical
%postun -n libdlog -p /sbin/ldconfig
#include <fcntl.h>
#include <unistd.h>
#include <sys/file.h>
+#include <sys/stat.h>
// C++
#include <iostream>
if (wr < 0)
throw throwable_errno ();
}
+
+ void truncate () const
+ {
+ if (ftruncate(fd, 0) < 0)
+ throw throwable_errno ();
+ }
+
+ virtual struct stat get_stat () const
+ {
+ struct stat s;
+ if (fstat (fd, &s) < 0)
+ throw throwable_errno ();
+ return s;
+ }
};
class LockedFD final : public FD {
+private:
+ /* Since we've locked the file, nobody else
+ * can modify it so fstat() will return the
+ * same values every time (well, not really
+ * but to the extent we're interested in, at
+ * any rate). Therefore, we can cache it to
+ * avoid multiple syscalls. */
+ struct stat s;
+ bool s_cached;
+
public:
/* Note: we're using advisory locks instead
* of mandatory locks. This is because:
LockedFD (const char * path, int flags)
: FD (path, flags)
+ , s {}
+ , s_cached {false}
{
struct flock f;
f.l_type = F_WRLCK;
(void) fcntl (fd, F_SETLK, &f);
}
+
+ struct stat get_stat () const final override
+ {
+ if (!s_cached) {
+ /* Could use a mutex for reusability,
+ * but let's not overengineer for now. */
+ const_cast <LockedFD *> (this)->s_cached = true;
+ const_cast <LockedFD *> (this)->s = FD::get_stat ();
+ }
+
+ return s;
+ }
};
+bool operator < (const struct timespec & a, const struct timespec & b)
+{
+ return (a.tv_sec < b.tv_sec)
+ || (a.tv_sec == b.tv_sec && a.tv_nsec < b.tv_nsec);
+}
+
int main (int argc, char **argv) try
{
if (argc != 2)
throw std::invalid_argument ("Usage: ./dlog-log-critical \"formatted-message\"");
- LockedFD logfile (DLOG_CRITICAL_LOGFILE_PATH, O_WRONLY | O_APPEND | O_SYNC);
- logfile.write(argv[1]);
+ LockedFD logfile_a (DLOG_CRITICAL_LOGFILE_PATH ".a", O_WRONLY | O_APPEND | O_SYNC),
+ logfile_b (DLOG_CRITICAL_LOGFILE_PATH ".b", O_WRONLY | O_APPEND | O_SYNC);
+
+ const bool is_a_older = logfile_a.get_stat().st_mtime < logfile_b.get_stat().st_mtime;
+ auto older = is_a_older ? &logfile_a : &logfile_b;
+ auto newer = is_a_older ? &logfile_b : &logfile_a;
+
+ if (newer->get_stat().st_size > DLOG_CRITICAL_LOGFILE_SIZE) {
+ older->truncate();
+ std::swap(older, newer);
+ }
+
+ newer->write(argv[1]);
return EXIT_SUCCESS;
} catch (const std::exception & ex) {