BSL v0.0.0
AMMOS Bundle Protocol Security Library (BSL)
Loading...
Searching...
No Matches
LoggingStderr.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2025 The Johns Hopkins University Applied Physics
3 * Laboratory LLC.
4 *
5 * This file is part of the Bundle Protocol Security Library (BSL).
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * This work was performed for the Jet Propulsion Laboratory, California
18 * Institute of Technology, sponsored by the United States Government under
19 * the prime contract 80NM0018D0004 between the Caltech and NASA under
20 * subcontract 1700763.
21 */
26#include <pthread.h>
27#include <stdarg.h>
28#include <stdio.h>
29#include <strings.h>
30#include <sys/time.h>
31#include <time.h>
32
33#include <BPSecLib_Private.h>
34#include <BSLConfig.h>
35
36#include <m-buffer.h>
37#include <m-string.h>
38
40#define BSL_LOG_QUEUE_SIZE 100
41
42// NOLINTBEGIN
43static const char *sev_names[] = {
44 NULL, // LOG_EMERG
45 NULL, // LOG_ALERT
46 "CRIT", // LOG_CRIT
47 "ERROR", // LOG_ERR
48 "WARNING", // LOG_WARNING
49 NULL, // LOG_NOTICE
50 "INFO", // LOG_INFO
51 "DEBUG", // LOG_DEBUG
52};
53// NOLINTEND
54
56typedef struct
57{
59 pthread_t thread;
61 struct timeval timestamp;
65 string_t context;
67 string_t message;
69
70static void BSL_LogEvent_event_init(BSL_LogEvent_event_t *obj)
71{
72 obj->thread = pthread_self();
73 gettimeofday(&(obj->timestamp), NULL);
74 obj->severity = LOG_DEBUG;
75 string_init(obj->context);
76 string_init(obj->message);
77}
78
79static void BSL_LogEvent_event_deinit(BSL_LogEvent_event_t *obj)
80{
81 string_clear(obj->message);
82 string_clear(obj->context);
83}
84
86#define M_OPL_BSL_LogEvent_event_t() (INIT(API_2(BSL_LogEvent_event_init)), CLEAR(API_2(BSL_LogEvent_event_deinit)))
87
90M_BUFFER_DEF(BSL_LogEvent_queue, BSL_LogEvent_event_t, BSL_LOG_QUEUE_SIZE, M_BUFFER_THREAD_SAFE | M_BUFFER_BLOCKING)
92
94static BSL_LogEvent_queue_t event_queue;
96static pthread_t thr_sink;
98static atomic_bool thr_valid = ATOMIC_VAR_INIT(false);
100
101uint8_t *BSL_Log_DumpAsHexString(uint8_t *dstbuf, size_t dstlen, const uint8_t *srcbuf, size_t srclen)
102{
103 ASSERT_ARG_NONNULL(dstbuf);
104 ASSERT_ARG_NONNULL(srcbuf);
105 ASSERT_ARG_EXPR(dstlen > 0);
106 ASSERT_ARG_EXPR(srclen > 0);
107
108 memset(dstbuf, 0, dstlen);
109 const char hex_digits[] = "0123456789ABCDEF";
110 for (size_t i = 0; i < srclen && (((i * 2) + 1) < dstlen-1); i++)
111 {
112 dstbuf[(i * 2)] = (uint8_t)hex_digits[(srcbuf[i] >> 4) & 0x0F];
113 dstbuf[(i * 2) + 1] = (uint8_t)hex_digits[srcbuf[i] & 0x0F];
114 }
115 return dstbuf;
116}
117
119static void write_log(const BSL_LogEvent_event_t *event)
120{
121 ASSERT_ARG_NONNULL(event);
122
123 // already domain validated
124 const char *prioname = sev_names[event->severity];
125
126 char tmbuf[32]; // NOLINT
127 {
128 time_t nowtime = event->timestamp.tv_sec;
129 struct tm nowtm;
130 gmtime_r(&nowtime, &nowtm);
131
132 char *curs = tmbuf;
133 size_t remain = sizeof(tmbuf) - 1;
134 size_t len = strftime(curs, remain, "%Y-%m-%dT%H:%M:%S", &nowtm);
135 curs += len;
136 remain -= len;
137 snprintf(curs, remain, ".%06ld", event->timestamp.tv_usec);
138 }
139 char thrbuf[2 * sizeof(pthread_t) + 1];
140 {
141 const uint8_t *data = (const void *)&(event->thread);
142 char *out = thrbuf;
143 for (size_t ix = 0; ix < sizeof(pthread_t); ++ix)
144 {
145 sprintf(out, "%02X", *data);
146 data++;
147 out += 2;
148 }
149 *out = '\0';
150 }
151 fprintf(stderr, "%s T:%s <%s> [%s] %s\n", tmbuf, thrbuf, prioname, string_get_cstr(event->context),
152 string_get_cstr(event->message));
153 fflush(stderr);
154}
156
157static void *work_sink(void *arg _U_)
158{
159 while (true)
160 {
162 BSL_LogEvent_queue_pop(&event, event_queue);
163 if (string_empty_p(event.message))
164 {
165 BSL_LogEvent_event_deinit(&event);
166 break;
167 }
168
169 write_log(&event);
170 BSL_LogEvent_event_deinit(&event);
171 }
172 return NULL;
173}
174
175void BSL_openlog(void)
176{
177 BSL_LogEvent_queue_init(event_queue, BSL_LOG_QUEUE_SIZE);
178
179 if (pthread_create(&thr_sink, NULL, work_sink, NULL))
180 {
181 // unsynchronized write
183 BSL_LogEvent_event_init(&manual);
184 manual.severity = LOG_CRIT;
185 string_set_str(manual.message, "BSL_openlog() failed");
186 write_log(&manual);
187 BSL_LogEvent_event_deinit(&manual);
188 }
189 else
190 {
191 atomic_store(&thr_valid, true);
192 }
193}
194
195void BSL_closelog(void)
196{
197 // sentinel empty message
199 BSL_LogEvent_event_init(&event);
200 BSL_LogEvent_queue_push(event_queue, event);
201
202 int res = pthread_join(thr_sink, NULL);
203 if (res)
204 {
205 // unsynchronized write
207 BSL_LogEvent_event_init(&manual);
208 manual.severity = LOG_CRIT;
209 string_set_str(manual.message, "BSL_closelog() failed");
210 write_log(&manual);
211 BSL_LogEvent_event_deinit(&manual);
212 }
213 else
214 {
215 atomic_store(&thr_valid, false);
216 }
217}
218
219// NOLINTBEGIN
220void BSL_LogEvent(int severity, const char *filename, int lineno, const char *funcname, const char *format, ...)
221{
222 if ((severity < 0) || (severity > LOG_DEBUG))
223 {
224 return;
225 }
226
228 BSL_LogEvent_event_init(&event);
229 event.severity = severity;
230
231 if (filename)
232 {
233 static const char dirsep = '/';
234
235 const char *pos = strrchr(filename, dirsep);
236 if (pos)
237 {
238 pos += 1;
239 }
240 else
241 {
242 pos = filename;
243 }
244 string_printf(event.context, "%s:%d:%s", pos, lineno, funcname);
245 }
246
247 {
248 va_list val;
249 va_start(val, format);
250 string_vprintf(event.message, format, val);
251 va_end(val);
252 }
253
254 if (string_empty_p(event.message))
255 {
256 // ignore empty messages
257 BSL_LogEvent_event_deinit(&event);
258 return;
259 }
260
261 if (atomic_load(&thr_valid))
262 {
263 BSL_LogEvent_queue_push(event_queue, event);
264 }
265 else
266 {
268 BSL_LogEvent_event_init(&manual);
269 manual.severity = LOG_CRIT;
270 string_set_str(manual.message, "BSL_LogEvent() called before BSL_openlog()");
271 write_log(&manual);
272 BSL_LogEvent_event_deinit(&manual);
273
274 write_log(&event);
275 BSL_LogEvent_event_deinit(&event);
276 }
277}
278// NOLINTEND
Single entry-point include file for all of the BPSec Lib (BSL) frontend API.
#define _U_
Mark an unused parameter Within a function definition.
static atomic_bool thr_valid
True if thr_sink is valid.
static void * work_sink(void *arg)
NOLINTEND.
#define BSL_LOG_QUEUE_SIZE
Number of events to buffer to I/O thread.
uint8_t * BSL_Log_DumpAsHexString(uint8_t *dstbuf, size_t dstlen, const uint8_t *srcbuf, size_t srclen)
NOLINTEND.
void BSL_LogEvent(int severity, const char *filename, int lineno, const char *funcname, const char *format,...)
Log an event.
void BSL_openlog(void)
Opens the event log.
static BSL_LogEvent_queue_t event_queue
NOLINTBEGIN.
static void write_log(const BSL_LogEvent_event_t *event)
NOLINTBEGIN.
void BSL_closelog(void)
Closes the event log.
static pthread_t thr_sink
Sink thread ID.
A single event for the log.
string_t message
Fully formatted message.
string_t context
File and function context.
pthread_t thread
Source thread ID.
struct timeval timestamp
Source event timestamp.
int severity
Event severity enumeration.