1 // Wrappers for utmp/wtmp & equivalent database access.
3 #ifndef DINIT_UTMP_H_INCLUDED
4 #define DINIT_UTMP_H_INCLUDED
6 #include "mconfig.h" // pull in any explicit configuration
9 // USE_UTMPX - whether to update the utmp[x] database. If 0, no-op stubs are defined.
10 // USE_UPDWTMPX - whether to use the updwtmpx function to log boot (contingent on USE_UTMPX).
11 // CLEAR_UTMP_ON_BOOT - whether to explicitly clear the utmp database file before writing the
15 #if __linux__ || __FreeBSD__ || __DragonFly__
18 // Should be safe to #include <utmpx.h>, but it may be stub implementation (Musl). Need to check
19 // that after include:
20 #define CHECK_UTMP_PATH 1
29 #define USE_UPDWTMPX 1
31 #define USE_UPDWTMPX 0
35 #ifndef CLEAR_UTMP_ON_BOOT
37 #define CLEAR_UTMP_ON_BOOT 1
39 #define CLEAR UTMP_ON_BOOT 0
45 // Musl has a utmpx.h header but only stub implementations of the functions, and does not define _PATH_UTMPX
48 #undef CHECK_UTMP_PATH
49 #if !defined(_PATH_UTMPX) || !defined(_PATH_WTMPX)
62 // Set the time for a utmpx record to the current time.
63 inline void set_current_time(struct utmpx *record)
66 // On Linux, ut_tv is not necessarily actually a struct timeval - on x86_64 the tv_sec and tv_usec
67 // fields are actually int32_t (by default) to preserve structural compatibility with 32-bit
70 gettimeofday(&curtime, nullptr);
71 record->ut_tv.tv_sec = curtime.tv_sec;
72 record->ut_tv.tv_usec = curtime.tv_usec;
74 gettimeofday(&record->ut_tv, nullptr);
78 // Log the boot time to the wtmp database (or equivalent).
79 inline bool log_boot()
82 memset(&record, 0, sizeof(record));
83 record.ut_type = BOOT_TIME;
85 set_current_time(&record);
87 // On FreeBSD, putxline will update all appropriate databases. On Linux, it only updates
88 // the utmp database: we need to update the wtmp database explicitly:
90 updwtmpx(_PATH_WTMPX, &record);
93 #if CLEAR_UTMP_ON_BOOT
94 truncate(_PATH_UTMPX, 0);
98 bool success = (pututxline(&record) != NULL);
104 // Create a utmp entry for the specified process, with the given id and tty line.
105 inline bool create_utmp_entry(const char *utmp_id, const char *utmp_line, pid_t pid)
108 memset(&record, 0, sizeof(record));
110 record.ut_type = INIT_PROCESS;
112 set_current_time(&record);
113 strncpy(record.ut_id, utmp_id, sizeof(record.ut_id));
114 strncpy(record.ut_line, utmp_line, sizeof(record.ut_line));
117 bool success = (pututxline(&record) != NULL);
123 // Clear the utmp entry for the given id/line/process.
124 inline void clear_utmp_entry(const char *utmp_id, const char *utmp_line)
127 memset(&record, 0, sizeof(record));
129 record.ut_type = DEAD_PROCESS;
130 set_current_time(&record);
131 strncpy(record.ut_id, utmp_id, sizeof(record.ut_id));
132 strncpy(record.ut_line, utmp_line, sizeof(record.ut_line));
134 struct utmpx *result;
138 // Try to find an existing entry by id/line and copy the process ID:
140 result = getutxid(&record);
143 result = getutxline(&record);
147 record.ut_pid = result->ut_pid;
154 #else // Don't update databases:
156 static inline bool log_boot()
161 static inline bool create_utmp_entry(const char *utmp_id, const char *utmp_line)
166 inline void clear_utmp_entry(const char *utmp_id, const char *utmp_line)