2022-02-27 20:45:41 -08:00
|
|
|
/* ALOG: A Logger
|
|
|
|
|
* Copyright (C) 2022 Aidan Hahn
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
|
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
|
|
|
* Software Foundation, either version 3 of the License, or (at your option)
|
|
|
|
|
* any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
|
* more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License along with
|
|
|
|
|
* this program. If not, see https://www.gnu.org/licenses/.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "alog.h"
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
// ^^^ TODO: build a memory management library
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <unistd.h>
|
2022-02-28 01:09:33 -08:00
|
|
|
#include <string.h>
|
|
|
|
|
#include <time.h>
|
2022-02-27 20:45:41 -08:00
|
|
|
|
|
|
|
|
#define TEST_ALOG_STATE() if (!ALOG_LOGGING_STATE) { _init_alog(); }
|
2022-07-29 12:57:54 -07:00
|
|
|
#define MAX_TIMESTAMP 30
|
2022-02-27 20:45:41 -08:00
|
|
|
|
2022-07-22 09:52:52 -07:00
|
|
|
struct _global_logging_state *ALOG_LOGGING_STATE;
|
|
|
|
|
int _alog_num_out_fds;
|
|
|
|
|
int *_alog_out_fds;
|
|
|
|
|
int _alog_num_err_fds;
|
|
|
|
|
int *_alog_err_fds;
|
|
|
|
|
|
2022-02-27 20:45:41 -08:00
|
|
|
void _init_alog() {
|
|
|
|
|
if (!ALOG_LOGGING_STATE) {
|
|
|
|
|
ALOG_LOGGING_STATE = malloc(sizeof(struct _global_logging_state));
|
|
|
|
|
ALOG_LOGGING_STATE->broadcast_to_all_fds = 0;
|
|
|
|
|
ALOG_LOGGING_STATE->log_debug_messages = 0;
|
|
|
|
|
}
|
|
|
|
|
_alog_num_out_fds = 1;
|
|
|
|
|
_alog_out_fds = malloc(sizeof(int) * 1);
|
|
|
|
|
// stdout
|
|
|
|
|
_alog_out_fds[0] = 1;
|
|
|
|
|
|
|
|
|
|
_alog_num_err_fds = 1;
|
|
|
|
|
_alog_err_fds = malloc(sizeof(int) * 1);
|
|
|
|
|
// stderr
|
2022-02-28 01:09:33 -08:00
|
|
|
_alog_err_fds[0] = 2;
|
2022-02-27 20:45:41 -08:00
|
|
|
}
|
|
|
|
|
|
2022-07-29 12:57:54 -07:00
|
|
|
void init_alog() {
|
|
|
|
|
TEST_ALOG_STATE();
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-27 20:45:41 -08:00
|
|
|
// TODO: should I dont close stdin and stdout?
|
|
|
|
|
void deinit_alog() {
|
|
|
|
|
TEST_ALOG_STATE();
|
|
|
|
|
free(ALOG_LOGGING_STATE);
|
|
|
|
|
for (int i = 0; i < _alog_num_out_fds; i++) {
|
|
|
|
|
close(_alog_out_fds[i]);
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < _alog_num_err_fds; i++) {
|
|
|
|
|
close(_alog_err_fds[i]);
|
|
|
|
|
}
|
|
|
|
|
free(_alog_out_fds);
|
|
|
|
|
free(_alog_err_fds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void alog_add_target(int fd, char is_err) {
|
|
|
|
|
TEST_ALOG_STATE();
|
|
|
|
|
int **fd_list = is_err ? &_alog_err_fds : &_alog_out_fds;
|
|
|
|
|
int *size = is_err ? &_alog_num_err_fds : &_alog_num_out_fds;
|
|
|
|
|
|
|
|
|
|
int *fds = malloc(sizeof(int) * (*size + 1));
|
|
|
|
|
for (int i = 0; i < *size; i++) {
|
|
|
|
|
fds[i] = (*fd_list)[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fds[*size] = fd;
|
|
|
|
|
free(*fd_list);
|
|
|
|
|
(*fd_list) = fds;
|
|
|
|
|
(*size)++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// inefficient time wise, but avoids extra allocs
|
|
|
|
|
// TODO: revisit this and see how to do it less tediously
|
|
|
|
|
void alog_remove_target(int fd) {
|
|
|
|
|
TEST_ALOG_STATE();
|
|
|
|
|
int out_fds = 0;
|
|
|
|
|
for (int i = 0; i < _alog_num_out_fds; i++) {
|
|
|
|
|
if (_alog_out_fds[i] != fd) {
|
|
|
|
|
out_fds++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-28 01:09:33 -08:00
|
|
|
if (!out_fds) {
|
|
|
|
|
_alog_num_out_fds = 0;
|
|
|
|
|
free(_alog_out_fds);
|
|
|
|
|
_alog_out_fds = NULL;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
int *tmp_outs = malloc(sizeof(int) * out_fds);
|
|
|
|
|
int tmp_iter_idx = 0;
|
|
|
|
|
for (int i = 0; i < _alog_num_out_fds; i++) {
|
|
|
|
|
if (_alog_out_fds[i] != fd) {
|
|
|
|
|
tmp_outs[tmp_iter_idx] = _alog_out_fds[i];
|
|
|
|
|
tmp_iter_idx++;
|
|
|
|
|
}
|
2022-02-27 20:45:41 -08:00
|
|
|
}
|
|
|
|
|
|
2022-02-28 01:09:33 -08:00
|
|
|
free(_alog_out_fds);
|
|
|
|
|
_alog_out_fds = tmp_outs;
|
|
|
|
|
_alog_num_out_fds = out_fds;
|
|
|
|
|
}
|
2022-02-27 20:45:41 -08:00
|
|
|
|
|
|
|
|
int err_fds = 0;
|
|
|
|
|
for (int i = 0; i < _alog_num_err_fds; i++) {
|
|
|
|
|
if (_alog_err_fds[i] != fd) {
|
|
|
|
|
err_fds++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-28 01:09:33 -08:00
|
|
|
if (!err_fds) {
|
|
|
|
|
_alog_num_err_fds = 0;
|
|
|
|
|
free(_alog_err_fds);
|
|
|
|
|
_alog_err_fds = NULL;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
int *tmp_errs = malloc(sizeof(int) * err_fds);
|
|
|
|
|
int tmp_iter_idx = 0;
|
|
|
|
|
for (int i = 0; i < _alog_num_err_fds; i++) {
|
|
|
|
|
if (_alog_err_fds[i] != fd) {
|
|
|
|
|
tmp_errs[tmp_iter_idx] = _alog_err_fds[i];
|
|
|
|
|
tmp_iter_idx++;
|
|
|
|
|
}
|
2022-02-27 20:45:41 -08:00
|
|
|
}
|
|
|
|
|
|
2022-02-28 01:09:33 -08:00
|
|
|
free(_alog_err_fds);
|
|
|
|
|
_alog_err_fds = tmp_errs;
|
|
|
|
|
_alog_num_err_fds = err_fds;
|
|
|
|
|
}
|
2022-02-27 20:45:41 -08:00
|
|
|
}
|
|
|
|
|
|
2022-02-28 01:09:33 -08:00
|
|
|
// TODO: use preprocessor directives to gate off the posix timestamp stuff
|
|
|
|
|
// unless the right variable is passed in at compile time
|
2022-02-27 20:45:41 -08:00
|
|
|
void alog (
|
|
|
|
|
alog_sev severity,
|
|
|
|
|
const char * message,
|
|
|
|
|
...
|
|
|
|
|
) {
|
|
|
|
|
TEST_ALOG_STATE();
|
|
|
|
|
if (severity == DEBUG &&
|
|
|
|
|
!ALOG_LOGGING_STATE->log_debug_messages) {
|
|
|
|
|
// nop :)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
va_list fmt_list;
|
|
|
|
|
va_start(fmt_list, message);
|
|
|
|
|
|
2022-02-28 01:09:33 -08:00
|
|
|
char *buffer;
|
|
|
|
|
int size;
|
|
|
|
|
if (severity != PRINT) {
|
|
|
|
|
// GET TIMESTAMP
|
2022-07-22 09:52:52 -07:00
|
|
|
// TODO: try to get this all into 1 call to sprintf
|
2022-02-28 01:09:33 -08:00
|
|
|
time_t t = time(NULL);
|
|
|
|
|
struct tm * p = localtime(&t);
|
2022-07-29 12:57:54 -07:00
|
|
|
int timestamp_len = strftime(NULL, MAX_TIMESTAMP, "[%c]", p) + 1;
|
|
|
|
|
char *timestamp = calloc(timestamp_len , sizeof(char));
|
|
|
|
|
strftime(timestamp, timestamp_len+1, "[%c]", p);
|
|
|
|
|
|
|
|
|
|
size = (timestamp_len + strlen(message) + 1);
|
|
|
|
|
char *msg_and_timestamp = calloc(size, sizeof(char));
|
2022-02-28 01:09:33 -08:00
|
|
|
sprintf(msg_and_timestamp, "%s %s\n", timestamp, message);
|
|
|
|
|
free(timestamp);
|
2022-07-29 12:57:54 -07:00
|
|
|
|
2022-07-22 09:52:52 -07:00
|
|
|
// TODO: Why even use msg_and_timestamp if I am going to write it wholesale into buffer?
|
2022-07-29 12:57:54 -07:00
|
|
|
size = vsnprintf(NULL, 0, msg_and_timestamp, fmt_list);
|
|
|
|
|
va_start(fmt_list, message);
|
|
|
|
|
va_end(fmt_list);
|
2022-02-28 01:09:33 -08:00
|
|
|
buffer = malloc(size + 1);
|
2022-07-29 12:57:54 -07:00
|
|
|
vsprintf(buffer, msg_and_timestamp, fmt_list);
|
|
|
|
|
va_end(fmt_list);
|
2022-02-28 01:09:33 -08:00
|
|
|
free(msg_and_timestamp);
|
2022-07-29 12:57:54 -07:00
|
|
|
|
2022-02-28 01:09:33 -08:00
|
|
|
// if severity is PRINT we avoid timestamp
|
|
|
|
|
} else {
|
2022-07-29 12:57:54 -07:00
|
|
|
size = vsnprintf(NULL, 0, message, fmt_list);
|
|
|
|
|
va_start(fmt_list, message);
|
2022-02-28 01:09:33 -08:00
|
|
|
buffer = malloc(size + 1);
|
2022-07-29 12:57:54 -07:00
|
|
|
vsprintf(buffer, message, fmt_list);
|
|
|
|
|
va_end(fmt_list);
|
2022-02-28 01:09:33 -08:00
|
|
|
}
|
2022-02-27 20:45:41 -08:00
|
|
|
|
|
|
|
|
for (int i = 0; i < _alog_num_out_fds; i++) {
|
|
|
|
|
if (write(_alog_out_fds[i], buffer, size) < size) {
|
|
|
|
|
// TODO: how to handle? probably cant log another message lmao
|
|
|
|
|
// perhaps it should be yanked from the list? maybe not?
|
|
|
|
|
;;
|
|
|
|
|
}
|
|
|
|
|
fsync(_alog_out_fds[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (severity == ERROR) {
|
|
|
|
|
for (int i = 0; i < _alog_num_err_fds; i++) {
|
|
|
|
|
if (write(_alog_err_fds[i], buffer, size) < size) {
|
|
|
|
|
// TODO: see above
|
|
|
|
|
;;
|
|
|
|
|
}
|
|
|
|
|
fsync(_alog_err_fds[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef ALOG_HIJACK_PRINTF
|
|
|
|
|
int printf(const char *format, ...) {
|
|
|
|
|
TEST_ALOG_STATE();
|
|
|
|
|
va_list fmt_list;
|
|
|
|
|
va_start(fmt_list, format);
|
|
|
|
|
/* TODO: this is a duplicate call given the implementation of alog.
|
|
|
|
|
* Lets figure out a better way to handle this */
|
|
|
|
|
int size = snprintf(NULL, 0, format, fmt_list);
|
|
|
|
|
alog(PRINT, NULL, format, fmt_list);
|
|
|
|
|
va_end(fmt_list);
|
|
|
|
|
return size;
|
|
|
|
|
/* potentially accept a mem leak at end of program for alog state.
|
|
|
|
|
* That is, if the program using this is not already using alog correctly
|
|
|
|
|
* or if ALOG has been LD_PRELOAD'ed into a program to override its printf */
|
|
|
|
|
}
|
|
|
|
|
#endif
|