Fix missing #include (needed for compiling with GCC 10)
[oweals/dinit.git] / src / includes / dinit-utmp.h
1 // Wrappers for utmp/wtmp & equivalent database access.
2
3 #ifndef DINIT_UTMP_H_INCLUDED
4 #define DINIT_UTMP_H_INCLUDED
5
6 #include "mconfig.h"  // pull in any explicit configuration
7
8 // 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
12 //                      boot entry.
13
14 #ifndef USE_UTMPX
15 #if __linux__ || __FreeBSD__ || __DragonFly__
16 #define USE_UTMPX 1
17 #if __linux__
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
21 #endif
22 #else
23 #define USE_UTMPX 0
24 #endif
25 #endif
26
27 #ifndef USE_UPDWTMPX
28 #ifdef __linux__
29 #define USE_UPDWTMPX 1
30 #else
31 #define USE_UPDWTMPX 0
32 #endif
33 #endif
34
35 #ifndef CLEAR_UTMP_ON_BOOT
36 #if __linux__
37 #define CLEAR_UTMP_ON_BOOT 1
38 #else
39 #define CLEAR UTMP_ON_BOOT 0
40 #endif
41 #endif
42
43 #if USE_UTMPX
44 #include <utmpx.h>
45 // Musl has a utmpx.h header but only stub implementations of the functions, and does not define _PATH_UTMPX
46 // nor _PATH_WTMPX.
47 #if CHECK_UTMP_PATH
48 #undef CHECK_UTMP_PATH
49 #if !defined(_PATH_UTMPX) || !defined(_PATH_WTMPX)
50 #undef USE_UTMPX
51 #define USE_UTMPX 0
52 #endif
53 #endif
54 #endif
55
56 #if USE_UTMPX
57
58 #include <cstring>
59
60 #include <sys/time.h>
61
62 // Set the time for a utmpx record to the current time.
63 inline void set_current_time(struct utmpx *record)
64 {
65 #ifdef __linux__
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
68     // utmp format.
69     timeval curtime;
70     gettimeofday(&curtime, nullptr);
71     record->ut_tv.tv_sec = curtime.tv_sec;
72     record->ut_tv.tv_usec = curtime.tv_usec;
73 #else
74     gettimeofday(&record->ut_tv, nullptr);
75 #endif
76 }
77
78 // Log the boot time to the wtmp database (or equivalent).
79 inline bool log_boot()
80 {
81     struct utmpx record;
82     memset(&record, 0, sizeof(record));
83     record.ut_type = BOOT_TIME;
84
85     set_current_time(&record);
86
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:
89 #if USE_UPDWTMPX
90     updwtmpx(_PATH_WTMPX, &record);
91 #endif
92
93 #if CLEAR_UTMP_ON_BOOT
94     truncate(_PATH_UTMPX, 0);
95 #endif
96
97     setutxent();
98     bool success = (pututxline(&record) != NULL);
99     endutxent();
100
101     return success;
102 }
103
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)
106 {
107     struct utmpx record;
108     memset(&record, 0, sizeof(record));
109
110     record.ut_type = INIT_PROCESS;
111     record.ut_pid = pid;
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));
115
116     setutxent();
117     bool success = (pututxline(&record) != NULL);
118     endutxent();
119
120     return success;
121 }
122
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)
125 {
126     struct utmpx record;
127     memset(&record, 0, sizeof(record));
128
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));
133
134     struct utmpx *result;
135
136     setutxent();
137
138     // Try to find an existing entry by id/line and copy the process ID:
139     if (*utmp_id) {
140         result = getutxid(&record);
141     }
142     else {
143         result = getutxline(&record);
144     }
145
146     if (result) {
147         record.ut_pid = result->ut_pid;
148     }
149
150     pututxline(&record);
151     endutxent();
152 }
153
154 #else // Don't update databases:
155
156 static inline bool log_boot()
157 {
158     return true;
159 }
160
161 static inline bool create_utmp_entry(const char *utmp_id, const char *utmp_line)
162 {
163     return true;
164 }
165
166 inline void clear_utmp_entry(const char *utmp_id, const char *utmp_line)
167 {
168     return;
169 }
170
171 #endif
172
173 #endif