Skip to content

Commit 94593b0

Browse files
committed
Implement service event log
Store global and per-service event changes in the runtime dir under events. Stores runlevel changes in the global log, service state changes in per-service log
1 parent 109a0bf commit 94593b0

5 files changed

Lines changed: 199 additions & 0 deletions

File tree

src/librc/librc.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,10 @@ rc_service_mark(const char *service, const RC_SERVICE state)
10611061
closedir(dp);
10621062
}
10631063
}
1064+
1065+
/* Log the service state change to the eventlog */
1066+
rc_eventlog_service(service, state);
1067+
10641068
free(init);
10651069
return true;
10661070
}

src/librc/librc.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,30 @@ static const char *const dirnames[RC_DIR_SYS_MAX] =
7777
RC_STRINGLIST *config_list(int dirfd, const char *pathname);
7878
void clear_dirfds(void);
7979

80+
/*
81+
* Log an event for a specific service.
82+
* Events are logged to <RC_DIR_SVCDIR>/events/services/<service>
83+
*
84+
* @param service name of the service
85+
* @param state new state of the service
86+
*/
87+
void rc_eventlog_service(const char *service, RC_SERVICE state);
88+
89+
/*
90+
* Log a global system event.
91+
* Events are logged to <RC_DIR_SVCDIR>/events/global
92+
*
93+
* @param event_type type of event (e.g., "runlevel", "boot", "shutdown")
94+
* @param message event message with details
95+
*/
96+
void rc_eventlog_global(const char *event_type, const char *message);
97+
98+
/*
99+
* Initialize the eventlog system.
100+
* Creates necessary directories under <RC_DIR_SVCDIR>/events/
101+
*
102+
* @return 0 on success, -1 on failure
103+
*/
104+
int rc_eventlog_init(void);
105+
80106
#endif

src/librc/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ librc_sources = [
1111
'librc-depend.c',
1212
'librc-misc.c',
1313
'librc-stringlist.c',
14+
'rc-eventlog.c',
1415
]
1516

1617
rc_h = configure_file(input : 'rc.h.in', output : 'rc.h',

src/librc/rc-eventlog.c

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* rc-eventlog.c
3+
* Event logging for OpenRC services and system events
4+
*/
5+
6+
/*
7+
* Copyright (c) 2024 The OpenRC Authors.
8+
* See the Authors file at the top-level directory of this distribution and
9+
* https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
10+
*
11+
* This file is part of OpenRC. It is subject to the license terms in
12+
* the LICENSE file found in the top-level directory of this
13+
* distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
14+
* This file may not be copied, modified, propagated, or distributed
15+
* except according to the terms contained in the LICENSE file.
16+
*/
17+
18+
#include <errno.h>
19+
#include <fcntl.h>
20+
#include <stdio.h>
21+
#include <stdlib.h>
22+
#include <string.h>
23+
#include <sys/stat.h>
24+
#include <sys/types.h>
25+
#include <time.h>
26+
#include <unistd.h>
27+
28+
#include "librc.h"
29+
30+
#define RC_EVENTLOG_DIR "events"
31+
#define RC_EVENTLOG_SERVICES "events/"
32+
#define RC_EVENTLOG_GLOBAL "events/rc"
33+
34+
/*
35+
* Get the name of a service state.
36+
*/
37+
static const char *rc_service_state_name(RC_SERVICE state)
38+
{
39+
int i;
40+
41+
for (i = 0; rc_service_state_names[i].name; i++) {
42+
if (rc_service_state_names[i].state == state)
43+
return rc_service_state_names[i].name;
44+
}
45+
46+
return "unknown";
47+
}
48+
49+
/*
50+
* Format the current time as an ISO 8601 timestamp with milliseconds.
51+
* Returns a dynamically allocated string that must be freed by the caller.
52+
*/
53+
static char *format_timestamp(void)
54+
{
55+
time_t now;
56+
struct tm *tm;
57+
char timebuf[32];
58+
char *result;
59+
int ms;
60+
61+
clock_gettime(CLOCK_REALTIME, &ts);
62+
tm = localtime(&ts.tv_sec);
63+
ms = (int)(ts.tv_nsec / 1000000);
64+
65+
strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S", tm);
66+
xasprintf(&result, "%s.%03d%+03ld%02ld", timebuf, ms,
67+
tm->tm_gmtoff / 3600, (labs(tm->tm_gmtoff) % 3600) / 60);
68+
69+
return result;
70+
}
71+
72+
/*
73+
* Initialize the eventlog system.
74+
* Creates necessary directories under <RC_DIR_SVCDIR>/events/
75+
*/
76+
int rc_eventlog_init(void)
77+
{
78+
int svcfd = rc_dirfd(RC_DIR_SVCDIR);
79+
80+
if (svcfd < 0)
81+
return -1;
82+
83+
if (mkdirat(svcfd, RC_EVENTLOG_DIR, 0755) == -1 && errno != EEXIST)
84+
return -1;
85+
86+
if (mkdirat(svcfd, RC_EVENTLOG_SERVICES, 0755) == -1 && errno != EEXIST)
87+
return -1;
88+
89+
return 0;
90+
}
91+
92+
/*
93+
* Log an event for a specific service.
94+
*/
95+
void rc_eventlog_service(const char *service, RC_SERVICE state)
96+
{
97+
int svcfd;
98+
int eventsfd;
99+
char *timestamp;
100+
FILE *fp;
101+
const char *state_name;
102+
103+
if (!service)
104+
return;
105+
106+
svcfd = rc_dirfd(RC_DIR_SVCDIR);
107+
if (svcfd < 0)
108+
return;
109+
110+
if (rc_eventlog_init() != 0)
111+
return;
112+
113+
eventsfd = openat(svcfd, RC_EVENTLOG_SERVICES, O_RDONLY | O_DIRECTORY);
114+
if (eventsfd < 0)
115+
return;
116+
117+
service = basename_c(service);
118+
119+
timestamp = format_timestamp();
120+
121+
state_name = rc_service_state_name(state);
122+
123+
fp = do_fopenat(eventsfd, service, O_WRONLY | O_CREAT | O_APPEND);
124+
close(eventsfd);
125+
126+
if (!fp) {
127+
free(timestamp);
128+
return;
129+
}
130+
131+
fprintf(fp, "%s %s\n", timestamp, state_name);
132+
fclose(fp);
133+
free(timestamp);
134+
}
135+
136+
/*
137+
* Log a global system event.
138+
*/
139+
void rc_eventlog_global(const char *event_type, const char *message)
140+
{
141+
int svcfd;
142+
char *timestamp;
143+
FILE *fp;
144+
145+
if (!event_type || !message)
146+
return;
147+
148+
svcfd = rc_dirfd(RC_DIR_SVCDIR);
149+
if (svcfd < 0)
150+
return;
151+
152+
if (rc_eventlog_init() != 0)
153+
return;
154+
155+
timestamp = format_timestamp();
156+
157+
fp = do_fopenat(svcfd, RC_EVENTLOG_GLOBAL, O_WRONLY | O_CREAT | O_APPEND);
158+
if (!fp) {
159+
free(timestamp);
160+
return;
161+
}
162+
163+
fprintf(fp, "%s %s %s\n", timestamp, event_type, message);
164+
fclose(fp);
165+
free(timestamp);
166+
}

src/openrc/rc.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,7 @@ int main(int argc, char **argv)
906906
if (access(RC_KRUNLEVEL, F_OK) != 0)
907907
set_krunlevel(runlevel);
908908
rc_runlevel_set(newlevel);
909+
rc_eventlog_global("runlevel", newlevel);
909910
setenv("RC_RUNLEVEL", newlevel, 1);
910911
setenv("RC_GOINGDOWN", "YES", 1);
911912
} else {
@@ -1059,6 +1060,7 @@ int main(int argc, char **argv)
10591060
/* Store the new runlevel */
10601061
if (newlevel) {
10611062
rc_runlevel_set(newlevel);
1063+
rc_eventlog_global("runlevel", newlevel);
10621064
free(runlevel);
10631065
runlevel = xstrdup(newlevel);
10641066
setenv("RC_RUNLEVEL", runlevel, 1);

0 commit comments

Comments
 (0)