1 /*@ Implementation of code.h: (unavoidable) basics.
2 *@ TODO Log: domain should be configurable
3 *@ TODO Assert: the C++ lib has per-thread assertion states, s_nolog to
4 *@ TODO suppress log, test_state(), test_and_clear_state(): for unit tests!
5 *@ TODO su_program: if set, the PID should be logged, too!
6 *
7 * Copyright (c) 2019 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
8 * SPDX-License-Identifier: ISC
9 *
10 * Permission to use, copy, modify, and/or distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22 #undef su_FILE
23 #define su_FILE su_core_code
24 #define su_SOURCE
25 #define su_SOURCE_CORE_CODE
26 #define su_MASTER
27
28 #include "su/code.h"
29
30 #include <errno.h> /* XXX Grrrr */
31 #include <stdarg.h>
32 #include <stdio.h> /* TODO Get rid */
33 #include <stdlib.h>
34 #include <unistd.h> /* TODO POSIX module! */
35
36 #include "su/icodec.h"
37
38 /*#include "su/code.h"*/
39 #include "su/code-in.h"
40
41 #if defined su_HAVE_DEBUG || defined su_HAVE_DEVEL
42 struct a_core_nyd_info{
43 char const *cni_file;
44 char const *cni_fun;
45 u32 cni_chirp_line;
46 u32 cni_level;
47 };
48 #endif
49
50 static char const a_core_lvlnames[][8] = {
51 FIELD_INITI(su_LOG_EMERG) "emerg",
52 FIELD_INITI(su_LOG_ALERT) "alert",
53 FIELD_INITI(su_LOG_CRIT) "crit",
54 FIELD_INITI(su_LOG_ERR) "error",
55 FIELD_INITI(su_LOG_WARN) "warning",
56 FIELD_INITI(su_LOG_NOTICE) "notice",
57 FIELD_INITI(su_LOG_INFO) "info",
58 FIELD_INITI(su_LOG_DEBUG) "debug"
59 };
60 /* You can deduce the value from the offset */
61 CTAV(su_LOG_EMERG == 0);
62 CTAV(su_LOG_DEBUG == 7);
63
64 union su__bom_union const su__bom_little = {{'\xFF', '\xFE'}};
65 union su__bom_union const su__bom_big = {{'\xFE', '\xFF'}};
66
67 #if DVLOR(1, 0)
68 MT( static uz a_core_glock_recno[su__GLOCK_MAX +1]; )
69
70 static u32 a_core_nyd_curr, a_core_nyd_level;
71 static boole a_core_nyd_skip;
72 static struct a_core_nyd_info a_core_nyd_infos[su_NYD_ENTRIES];
73 #endif
74
75 uz su__state;
76
77 char const su_empty[] = "";
78 char const su_reproducible_build[] = "reproducible_build";
79 u16 const su_bom = su_BOM;
80
81 char const *su_program;
82
83 /* TODO Eventually all the I/O is SU based, then this will vanish!
84 * TODO We need some _USECASE_ hook to store readily prepared lines then.
85 * TODO Also, our log does not yet prepend "su_progam: " to each output line,
86 * TODO because of all that (port FmtEncCtx, use rounds!!) */
87 su_SINLINE void a_evlog(BITENUM_IS(u32,su_log_level) lvl, char const *fmt,
88 va_list ap);
89
90 /* */
91 #if DVLOR(1, 0)
92 static void a_core_nyd_printone(void (*ptf)(up cookie, char const *buf,
93 uz blen), up cookie, struct a_core_nyd_info const *cnip);
94 #endif
95
96 su_SINLINE void
a_evlog(BITENUM_IS (u32,su_log_level)lvl,char const * fmt,va_list ap)97 a_evlog(BITENUM_IS(u32,su_log_level) lvl, char const *fmt, va_list ap){
98 #ifdef su_USECASE_MX
99 # ifndef mx_HAVE_AMALGAMATION
100 /*extern*/ void n_err(char const *, ...);
101 /*extern*/ void n_verr(char const *, va_list);
102 # endif
103 #endif
104 char buf[su_IENC_BUFFER_SIZE];
105 char const *cp, *xfmt;
106 u32 f;
107
108 f = lvl & ~su__LOG_MASK;
109 lvl &= su__LOG_MASK;
110
111 if(!(f & su_LOG_F_CORE)){
112 #ifdef su_USECASE_MX
113 if(lvl != su_LOG_EMERG)
114 goto jnostd;
115 #endif
116 }
117
118 /* TODO ensure each line has the prefix; use FormatEncodeCtx */
119 if(su_program != NIL){
120 if(su_state_has(su_STATE_LOG_SHOW_PID)){
121 cp = su_ienc_u32(buf, getpid(), 10);
122 xfmt = "%s[%s]: ";
123 }else{
124 cp = su_empty;
125 xfmt = "%s: ";
126 }
127 fprintf(stderr, xfmt, su_program, cp);
128 }
129
130 if(su_state_has(su_STATE_LOG_SHOW_LEVEL))
131 fprintf(stderr, "[%s] ",
132 a_core_lvlnames[lvl]);
133
134 vfprintf(stderr, fmt, ap);
135
136 #ifdef su_USECASE_MX
137 goto jnomx;
138 jnostd:
139 n_verr(fmt, ap);
140 jnomx:
141 #endif
142
143 if(lvl == su_LOG_EMERG)
144 abort(); /* TODO configurable */
145 }
146
147 #if DVLOR(1, 0)
148 static void
a_core_nyd_printone(void (* ptf)(up cookie,char const * buf,uz blen),up cookie,struct a_core_nyd_info const * cnip)149 a_core_nyd_printone(void (*ptf)(up cookie, char const *buf, uz blen),
150 up cookie, struct a_core_nyd_info const *cnip){
151 char buf[80 +1], c;
152 union {int i; uz z;} u;
153 char const *sep, *cp;
154
155 /* Ensure actual file name can be seen, unless multibyte comes into play */
156 sep = su_empty;
157 cp = cnip->cni_file;
158 for(u.z = 0; (c = cp[u.z]) != '\0'; ++u.z)
159 if(S(uc,c) & 0x80){
160 u.z = 0;
161 break;
162 }
163 if(u.z > 40){
164 cp += -(38 - u.z);
165 sep = "..";
166 }
167
168 u.i = snprintf(buf, sizeof(buf) - 1,
169 "%c[%2" PRIu32 "] %.25s (%s%.40s:%" PRIu32 ")\n",
170 "=><"[(cnip->cni_chirp_line >> 29) & 0x3], cnip->cni_level,
171 cnip->cni_fun, sep, cp, (cnip->cni_chirp_line & 0x1FFFFFFFu));
172 if(u.i > 0){
173 u.z = u.i;
174 if(u.z >= sizeof(buf) -1){
175 buf[sizeof(buf) - 2] = '\n';
176 buf[sizeof(buf) - 1] = '\0';
177 u.z = sizeof(buf) -1; /* (Skip \0) */
178 }
179 (*ptf)(cookie, buf, u.z);
180 }
181 }
182 #endif /* DVLOR(1, 0) */
183
184 #ifdef su_HAVE_MT
185 void
su__glock(enum su__glock_type gt)186 su__glock(enum su__glock_type gt){
187 NYD2_IN;
188
189 switch(gt){
190 case su__GLOCK_STATE: /* XXX spinlock */
191 break;
192 case su__GLOCK_LOG: /* XXX mutex */
193 break;
194 }
195
196 # if DVLOR(1, 0)
197 ASSERT(a_core_glock_recno[gt] != UZ_MAX);
198 ++a_core_glock_recno[gt];
199 # endif
200 NYD2_OU;
201 }
202
203 void
su__gunlock(enum su__glock_type gt)204 su__gunlock(enum su__glock_type gt){
205 NYD2_IN;
206
207 switch(gt){
208 case su__GLOCK_STATE: /* XXX spinlock */
209 break;
210 case su__GLOCK_LOG: /* XXX mutex */
211 break;
212 }
213
214 # if DVLOR(1, 0)
215 ASSERT(a_core_glock_recno[gt] > 0);
216 --a_core_glock_recno[gt];
217 # endif
218 NYD2_OU;
219 }
220 #endif /* su_HAVE_MT */
221
222 s32
su_state_err(enum su_state_err_type err,uz state,char const * msg_or_nil)223 su_state_err(enum su_state_err_type err, uz state, char const *msg_or_nil){
224 static char const intro_nomem[] = N_("Out of memory: %s\n"),
225 intro_overflow[] = N_("Datatype overflow: %s\n");
226
227 enum su_log_level lvl;
228 char const *introp;
229 s32 eno;
230 u32 xerr;
231 NYD2_IN;
232
233 if(msg_or_nil == NIL)
234 msg_or_nil = N_("(no error information)");
235 state &= su_STATE_ERR_MASK;
236
237 xerr = err;
238 switch(xerr &= su_STATE_ERR_TYPE_MASK){
239 default:
240 ASSERT(0);
241 /* FALLTHRU */
242 case su_STATE_ERR_NOMEM:
243 eno = su_ERR_NOMEM;
244 introp = intro_nomem;
245 break;
246 case su_STATE_ERR_OVERFLOW:
247 eno = su_ERR_OVERFLOW;
248 introp = intro_overflow;
249 break;
250 }
251
252 lvl = su_LOG_EMERG;
253 if(state & su_STATE_ERR_NOPASS)
254 goto jdolog;
255 if(state & su_STATE_ERR_PASS)
256 lvl = su_LOG_DEBUG;
257 else if((state & xerr) || su_state_has(xerr))
258 lvl = su_LOG_ALERT;
259
260 if(su_log_would_write(lvl))
261 jdolog:
262 su_log_write(lvl, V_(introp), V_(msg_or_nil));
263
264 if(!(state & su_STATE_ERR_NOERRNO))
265 su_err_set_no(eno);
266 NYD2_OU;
267 return eno;
268 }
269
270 s32
su_err_no(void)271 su_err_no(void){
272 s32 rv;
273 rv = errno; /* TODO a_core_eno */
274 return rv;
275 }
276
277 s32
su_err_set_no(s32 eno)278 su_err_set_no(s32 eno){
279 errno = eno; /* TODO a_core_eno; */
280 return eno;
281 }
282
283 s32
su_err_no_via_errno(void)284 su_err_no_via_errno(void){
285 s32 rv;
286 rv = /*TODO a_core_eno =*/errno;
287 return rv;
288 }
289
290 void
su_log_write(BITENUM_IS (u32,su_log_level)lvl,char const * fmt,...)291 su_log_write(BITENUM_IS(u32,su_log_level) lvl, char const *fmt, ...){
292 va_list va;
293 NYD_IN;
294
295 if(su_log_would_write(lvl)){
296 va_start(va, fmt);
297 a_evlog(lvl, fmt, va);
298 va_end(va);
299 }
300 NYD_OU;
301 }
302
303 void
su_log_vwrite(BITENUM_IS (u32,su_log_level)lvl,char const * fmt,void * vp)304 su_log_vwrite(BITENUM_IS(u32,su_log_level) lvl, char const *fmt, void *vp){
305 NYD_IN;
306
307 if(su_log_would_write(lvl))
308 a_evlog(lvl, fmt, *S(va_list*,vp));
309 NYD_OU;
310 }
311
312 void
su_assert(char const * expr,char const * file,u32 line,char const * fun,boole crash)313 su_assert(char const *expr, char const *file, u32 line, char const *fun,
314 boole crash){
315 su_log_write((crash ? su_LOG_EMERG : su_LOG_ALERT),
316 "SU assert failed: %.60s\n"
317 " File %.60s, line %" PRIu32 "\n"
318 " Function %.142s\n",
319 expr, file, line, fun);
320 }
321
322 #if DVLOR(1, 0)
323 void
su_nyd_set_disabled(boole disabled)324 su_nyd_set_disabled(boole disabled){
325 a_core_nyd_skip = (disabled != FAL0);
326 }
327
328 void
su_nyd_reset_level(u32 nlvl)329 su_nyd_reset_level(u32 nlvl){
330 if(nlvl < a_core_nyd_level)
331 a_core_nyd_level = nlvl;
332 }
333
334 void
su_nyd_chirp(u8 act,char const * file,u32 line,char const * fun)335 su_nyd_chirp(u8 act, char const *file, u32 line, char const *fun){
336 if(!a_core_nyd_skip){
337 struct a_core_nyd_info *cnip;
338
339 cnip = &a_core_nyd_infos[0];
340
341 if(a_core_nyd_curr != su_NELEM(a_core_nyd_infos))
342 cnip += a_core_nyd_curr++;
343 else
344 a_core_nyd_curr = 1;
345 cnip->cni_file = file;
346 cnip->cni_fun = fun;
347 cnip->cni_chirp_line = (S(u32,act & 0x3) << 29) | (line & 0x1FFFFFFFu);
348 cnip->cni_level = ((act == 0) ? a_core_nyd_level /* TODO spinlock */
349 : (act == 1) ? ++a_core_nyd_level : a_core_nyd_level--);
350 }
351 }
352
353 void
su_nyd_dump(void (* ptf)(up cookie,char const * buf,uz blen),up cookie)354 su_nyd_dump(void (*ptf)(up cookie, char const *buf, uz blen), up cookie){
355 uz i;
356 struct a_core_nyd_info const *cnip;
357
358 a_core_nyd_skip = TRU1;
359 if(a_core_nyd_infos[su_NELEM(a_core_nyd_infos) - 1].cni_file != NULL)
360 for(i = a_core_nyd_curr, cnip = &a_core_nyd_infos[i];
361 i < su_NELEM(a_core_nyd_infos); ++i)
362 a_core_nyd_printone(ptf, cookie, cnip++);
363 for(i = 0, cnip = a_core_nyd_infos; i < a_core_nyd_curr; ++i)
364 a_core_nyd_printone(ptf, cookie, cnip++);
365 }
366 #endif /* DVLOR(1, 0) */
367
368 #include "su/code-ou.h"
369 /* s-it-mode */
370