Update stuff

This commit is contained in:
2026-05-25 23:13:27 -07:00
parent 5d631b594f
commit 5dfbdc43c0
7 changed files with 133 additions and 168 deletions
+9 -3
View File
@@ -1,9 +1,15 @@
CC=gcc # If set to 1, the autograder
CFLAGS=-Wall -Wextra -Wpedantic -std=c23 -D_POSIX_C_SOURCE=200809L -pthread -O2 BAD_ERROR_REPORTING_FOR_AUTOGRADER=1
CFLAGS+=-Og -g -fsanitize=address,undefined
CC=clang
CFLAGS=-Wall -Wextra -Wpedantic -Werror -std=c23 -D_POSIX_C_SOURCE=200809L -pthread -O2
#CFLAGS+=-Og -g -fsanitize=address,undefined
SRCS=main.c threadpool.c util.c server.c http.c SRCS=main.c threadpool.c util.c server.c http.c
OBJS=$(SRCS:%.c=bin/%.o) OBJS=$(SRCS:%.c=bin/%.o)
ifeq ($(BAD_ERROR_REPORTING_FOR_AUTOGRADER),1)
CFLAGS+=-DBAD_ERROR_REPORTING_FOR_AUTOGRADER=1
endif
all: httpserver all: httpserver
+15 -32
View File
@@ -7,9 +7,7 @@
static const char *EMPTY_STRING = ""; static const char *EMPTY_STRING = "";
HTTPHeaderList* http_header_list_push(HTTPHeaderList* list, const char* key, HTTPHeaderList *http_header_list_push(HTTPHeaderList *list, const char *key, const char *value) {
const char* value)
{
HTTPHeaderList *new = malloc(sizeof(HTTPHeaderList)); HTTPHeaderList *new = malloc(sizeof(HTTPHeaderList));
if (!new) { if (!new) {
return NULL; return NULL;
@@ -33,8 +31,7 @@ HTTPHeaderList* http_header_list_push(HTTPHeaderList* list, const char* key,
return new; return new;
} }
void free_http_header_list(HTTPHeaderList* list) void free_http_header_list(HTTPHeaderList *list) {
{
while (list) { while (list) {
HTTPHeaderList *next = list->next; HTTPHeaderList *next = list->next;
free(list->key); free(list->key);
@@ -44,9 +41,7 @@ void free_http_header_list(HTTPHeaderList* list)
} }
} }
const char* http_header_list_search(HTTPHeaderList* list, const char* key, const char *http_header_list_search(HTTPHeaderList *list, const char *key, const char *def) {
const char* def)
{
while (list) { while (list) {
if (strcmp(list->key, key) == 0) { if (strcmp(list->key, key) == 0) {
return list->value; return list->value;
@@ -69,10 +64,8 @@ const char* http_header_list_search(HTTPHeaderList* list, const char* key,
} }
// if an error occured, req->uri is not allocated // if an error occured, req->uri is not allocated
static HTTPRequestParseResult parse_method_uri_line(FILE* stream, static HTTPRequestParseResult parse_method_uri_line(
size_t* restrict bytes_read, FILE *stream, size_t *restrict bytes_read, HTTPRequest *restrict req) {
HTTPRequest* restrict req)
{
// allow for some leeway in passing incorrect methods // allow for some leeway in passing incorrect methods
char method[MAX_METHOD_LENGTH + 1]; char method[MAX_METHOD_LENGTH + 1];
char uri[MAX_URI_LENGTH + 1]; char uri[MAX_URI_LENGTH + 1];
@@ -81,8 +74,7 @@ static HTTPRequestParseResult parse_method_uri_line(FILE* stream,
ssize_t signed_bytes_read; ssize_t signed_bytes_read;
#define S1(s) #s #define S1(s) #s
#define S(s) S1(s) #define S(s) S1(s)
int nconv = fscanf( int nconv = fscanf(stream,
stream,
// clang-format off // clang-format off
"%" S(MAX_METHOD_LENGTH) "[A-Z]" "%" S(MAX_METHOD_LENGTH) "[A-Z]"
"%c" "%c"
@@ -92,8 +84,8 @@ static HTTPRequestParseResult parse_method_uri_line(FILE* stream,
"%c%c" "%c%c"
"%zn", "%zn",
// clang-format on // clang-format on
method, &whitespace[0], uri, &whitespace[1], version_str, method, &whitespace[0], uri, &whitespace[1], version_str, &whitespace[2], &whitespace[3],
&whitespace[2], &whitespace[3], &signed_bytes_read); &signed_bytes_read);
#undef S #undef S
*bytes_read = signed_bytes_read; *bytes_read = signed_bytes_read;
RETURN_IF_READ_ERROR(stream); RETURN_IF_READ_ERROR(stream);
@@ -118,9 +110,7 @@ static HTTPRequestParseResult parse_method_uri_line(FILE* stream,
// return true if there are more headers and no error occurred, false otherwise // return true if there are more headers and no error occurred, false otherwise
// this will *not* free LIST if an error occurs // this will *not* free LIST if an error occurs
static bool next_header(FILE *stream, size_t *restrict bytes_read, static bool next_header(FILE *stream, size_t *restrict bytes_read,
HTTPRequestParseResult* restrict res, HTTPRequestParseResult *restrict res, HTTPHeaderList *restrict *restrict list) {
HTTPHeaderList* restrict* restrict list)
{
char c = fgetc(stream); char c = fgetc(stream);
RETURN_IF_READ_ERROR(stream); RETURN_IF_READ_ERROR(stream);
if (c == '\r') { if (c == '\r') {
@@ -149,8 +139,7 @@ static bool next_header(FILE* stream, size_t* restrict bytes_read,
"%c%c" "%c%c"
"%zn", "%zn",
// clang-format on // clang-format on
key, &whitespace[0], value, &whitespace[1], &whitespace[2], key, &whitespace[0], value, &whitespace[1], &whitespace[2], &signed_bytes_read);
&signed_bytes_read);
#undef S #undef S
*bytes_read = signed_bytes_read; *bytes_read = signed_bytes_read;
RETURN_IF_READ_ERROR(stream); RETURN_IF_READ_ERROR(stream);
@@ -165,9 +154,7 @@ static bool next_header(FILE* stream, size_t* restrict bytes_read,
return true; return true;
} }
HTTPRequestParseResult parse_http_request(FILE* stream, HTTPRequestParseResult parse_http_request(FILE *stream, HTTPRequest *restrict out) {
HTTPRequest* restrict out)
{
out->uri = EMPTY_STRING; out->uri = EMPTY_STRING;
out->path = EMPTY_STRING; out->path = EMPTY_STRING;
out->method = EMPTY_STRING; out->method = EMPTY_STRING;
@@ -187,8 +174,7 @@ HTTPRequestParseResult parse_http_request(FILE* stream,
return res; return res;
} }
void free_http_request(HTTPRequest* restrict req) void free_http_request(HTTPRequest *restrict req) {
{
if (req->method != EMPTY_STRING) { if (req->method != EMPTY_STRING) {
free((char *) req->method); free((char *) req->method);
} }
@@ -198,8 +184,7 @@ void free_http_request(HTTPRequest* restrict req)
free_http_header_list(req->headers); free_http_header_list(req->headers);
} }
const char* status_code_to_message(int status, size_t* restrict length) const char *status_code_to_message(int status, size_t *restrict length) {
{
static const struct { static const struct {
int code; int code;
const char *msg; const char *msg;
@@ -228,11 +213,9 @@ const char* status_code_to_message(int status, size_t* restrict length)
return NULL; return NULL;
} }
void format_http_response(FILE* stream, HTTPResponse* restrict resp) void format_http_response(FILE *stream, HTTPResponse *restrict resp) {
{
assert(status_code_to_message(resp->status, NULL)); assert(status_code_to_message(resp->status, NULL));
fprintf(stream, "HTTP/1.1 %d %s\r\n", resp->status, fprintf(stream, "HTTP/1.1 %d %s\r\n", resp->status, status_code_to_message(resp->status, NULL));
status_code_to_message(resp->status, NULL));
fprintf(stream, "Content-Length: %zu\r\n", resp->body_length); fprintf(stream, "Content-Length: %zu\r\n", resp->body_length);
for (HTTPHeaderList *h = resp->headers; h; h = h->next) { for (HTTPHeaderList *h = resp->headers; h; h = h->next) {
fwrite(h->key, 1, h->key_length, stream); fwrite(h->key, 1, h->key_length, stream);
+42 -60
View File
@@ -40,32 +40,40 @@ static const GlobalFlags DEFAULT_FLAGS = {
}; };
// Return false on failure, true on success // Return false on failure, true on success
static bool parse_uint(const char* str, uintmax_t min, uintmax_t max, static bool parse_uint(const char *str, uintmax_t min, uintmax_t max, uintmax_t *output) {
uintmax_t* output)
{
if (isspace(*str) || *str == '+' || *str == '-') { if (isspace(*str) || *str == '+' || *str == '-') {
#ifdef BAD_ERROR_REPORTING_FOR_AUTOGRADER
fprintf(stderr, "Invalid Port\n");
#else
log_error("malformed number: \"%s\"", str); log_error("malformed number: \"%s\"", str);
#endif
return false; return false;
} }
errno = 0; errno = 0;
char *endptr; char *endptr;
uintmax_t conv = strtoumax(str, &endptr, 10); uintmax_t conv = strtoumax(str, &endptr, 10);
if (!*str || *endptr) { if (!*str || *endptr) {
#ifdef BAD_ERROR_REPORTING_FOR_AUTOGRADER
fprintf(stderr, "Invalid Port\n");
#else
log_error("malformed number: \"%s\"", str); log_error("malformed number: \"%s\"", str);
#endif
return false; return false;
} else if ((conv == UINTMAX_MAX && errno == ERANGE) || conv < min } else if ((conv == UINTMAX_MAX && errno == ERANGE) || conv < min || conv > max) {
|| conv > max) { #ifdef BAD_ERROR_REPORTING_FOR_AUTOGRADER
fprintf(stderr, "Invalid Port\n");
#else
log_error("out of range: %s", str); log_error("out of range: %s", str);
#endif
return false; return false;
} }
*output = conv; *output = conv;
return true; return true;
} }
static void parse_cli_options(int argc, const char** argv, GlobalFlags* flags) static void parse_cli_options(int argc, const char **argv, GlobalFlags *flags) {
{ #define MAX_PARALLELISM SIZE_MAX
constexpr size_t MAX_PARALLELISM = SIZE_MAX; #define MAX_PORT 65535
constexpr uint32_t MAX_PORT = 65535;
opterr = false; opterr = false;
int c; int c;
char pretty_flag[8]; char pretty_flag[8];
@@ -81,9 +89,7 @@ static void parse_cli_options(int argc, const char** argv, GlobalFlags* flags)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
} }
switch (c) { switch (c) {
case 'h': case 'h': flags->help_flag = true; return;
flags->help_flag = true;
return;
case 'p': { case 'p': {
uintmax_t conv; uintmax_t conv;
if (!parse_uint(optarg, 1, MAX_PARALLELISM, &conv)) { if (!parse_uint(optarg, 1, MAX_PARALLELISM, &conv)) {
@@ -91,9 +97,7 @@ static void parse_cli_options(int argc, const char** argv, GlobalFlags* flags)
} }
flags->parallelism = conv; flags->parallelism = conv;
} break; } break;
case 'a': case 'a': flags->address = optarg; break;
flags->address = optarg;
break;
case ':': case ':':
flags->opt_error = true; flags->opt_error = true;
log_error("flag requires argument: '%s'", pretty_flag); log_error("flag requires argument: '%s'", pretty_flag);
@@ -120,31 +124,25 @@ static void parse_cli_options(int argc, const char** argv, GlobalFlags* flags)
} }
} }
static void print_help(FILE* file) static void print_help(FILE *file) {
{ fprintf(file, "usage: httpserver [-h] [-a ADDRESS] [-p PARALLELISM] <PORT>\n");
fprintf(file,
"usage: httpserver [-h] [-a ADDRESS] [-p PARALLELISM] <PORT>\n");
fprintf(file, " -h print this message, then exit\n"); fprintf(file, " -h print this message, then exit\n");
fprintf( fprintf(file, " -p use PARALLELISM threads for processing requests (default: 1)\n");
file,
" -p use PARALLELISM threads for processing requests (default: 1)\n");
fprintf(file, " -a bind to ADDRESS (default: 127.0.0.1)\n"); fprintf(file, " -a bind to ADDRESS (default: 127.0.0.1)\n");
} }
constexpr int WORKER_BLOCKED_SIGNALS[] = { SIGTERM, SIGINT, SIGHUP }; static const int WORKER_BLOCKED_SIGNALS[] = { SIGTERM, SIGINT, SIGHUP };
constexpr size_t N_WORKER_BLOCKED_SIGNALS = sizeof(WORKER_BLOCKED_SIGNALS) / sizeof(int); static const size_t N_WORKER_BLOCKED_SIGNALS = sizeof(WORKER_BLOCKED_SIGNALS) / sizeof(int);
static bool shutdown_flag = false; static bool shutdown_flag = false;
static void signal_handler(int signal) static void signal_handler(int signal) {
{
if (shutdown_flag) { if (shutdown_flag) {
_exit(signal == SIGTERM ? 0 : EXIT_FAILURE); _exit(signal == SIGTERM ? 0 : EXIT_FAILURE);
} }
shutdown_flag = true; shutdown_flag = true;
} }
static void setup_signals() static void setup_signals() {
{
struct sigaction act = { struct sigaction act = {
.sa_handler = signal_handler, .sa_handler = signal_handler,
.sa_flags = 0, .sa_flags = 0,
@@ -158,25 +156,18 @@ static void setup_signals()
} }
} }
static int parse_result_to_status(HTTPRequestParseResult res) static int parse_result_to_status(HTTPRequestParseResult res) {
{
switch (res) { switch (res) {
case HRPR_OK: case HRPR_OK: return 200;
return 200;
case HRPR_NO_MEM: case HRPR_NO_MEM:
case HRPR_READ_FAILED: case HRPR_READ_FAILED: return 500;
return 500; case HRPR_BAD_VERSION: return 505;
case HRPR_BAD_VERSION: case HRPR_BAD_FORMAT: return 400;
return 505; default: abort();
case HRPR_BAD_FORMAT:
return 400;
default:
abort();
} }
} }
static void send_simple_response(FILE* stream, int status) static void send_simple_response(FILE *stream, int status) {
{
size_t status_msg_len; size_t status_msg_len;
const char *status_msg = status_code_to_message(status, &status_msg_len); const char *status_msg = status_code_to_message(status, &status_msg_len);
HTTPResponse resp = { HTTPResponse resp = {
@@ -189,14 +180,12 @@ static void send_simple_response(FILE* stream, int status)
fputc('\n', stream); fputc('\n', stream);
} }
static void write_audit_log_entry(HTTPRequest* restrict req, int status) static void write_audit_log_entry(HTTPRequest *restrict req, int status) {
{
fprintf(stderr, "%s,%s,%d,%s\n", req->method, req->uri, status, fprintf(stderr, "%s,%s,%d,%s\n", req->method, req->uri, status,
http_header_list_search(req->headers, "Request-ID", "0")); http_header_list_search(req->headers, "Request-ID", "0"));
} }
static void handle_get_request(FILE* conn, HTTPRequest* restrict req) static void handle_get_request(FILE *conn, HTTPRequest *restrict req) {
{
int status = 200; int status = 200;
struct stat statbuf; struct stat statbuf;
flockfile(stderr); flockfile(stderr);
@@ -242,8 +231,7 @@ static void handle_get_request(FILE* conn, HTTPRequest* restrict req)
fclose(file_handle); fclose(file_handle);
} }
static ssize_t get_content_length(HTTPRequest* restrict req) static ssize_t get_content_length(HTTPRequest *restrict req) {
{
const char *text = http_header_list_search(req->headers, "Content-Length", NULL); const char *text = http_header_list_search(req->headers, "Content-Length", NULL);
if (!text) { if (!text) {
return 0; return 0;
@@ -252,15 +240,13 @@ static ssize_t get_content_length(HTTPRequest* restrict req)
} }
char *endptr; char *endptr;
uintmax_t conv = strtoumax(text, &endptr, 10); uintmax_t conv = strtoumax(text, &endptr, 10);
if (*endptr || (conv == UINTMAX_MAX && errno == ERANGE) if (*endptr || (conv == UINTMAX_MAX && errno == ERANGE) || conv > SIZE_MAX) {
|| conv > SIZE_MAX) {
return -1; return -1;
} }
return conv; return conv;
} }
static void handle_put_request(FILE* conn, HTTPRequest* restrict req) static void handle_put_request(FILE *conn, HTTPRequest *restrict req) {
{
ssize_t content_length = get_content_length(req); ssize_t content_length = get_content_length(req);
if (content_length < 0) { if (content_length < 0) {
write_audit_log_entry(req, 400); write_audit_log_entry(req, 400);
@@ -278,8 +264,7 @@ static void handle_put_request(FILE* conn, HTTPRequest* restrict req)
char read_buff[4096]; char read_buff[4096];
while (content_length) { while (content_length) {
ssize_t read_size = fread(read_buff, 1, ssize_t read_size = fread(read_buff, 1,
(size_t)content_length < sizeof(read_buff) (size_t) content_length < sizeof(read_buff) ? (size_t) content_length
? (size_t)content_length
: sizeof(read_buff), : sizeof(read_buff),
conn); conn);
if (ferror(conn) || write(temp_fd, read_buff, read_size) < 0) { if (ferror(conn) || write(temp_fd, read_buff, read_size) < 0) {
@@ -322,8 +307,7 @@ write_status_and_unlock:
} }
} }
static void handle_connection(void* arg) static void handle_connection(void *arg) {
{
FILE *conn = arg; FILE *conn = arg;
HTTPRequest req; HTTPRequest req;
HTTPRequestParseResult res = parse_http_request(conn, &req); HTTPRequestParseResult res = parse_http_request(conn, &req);
@@ -346,13 +330,11 @@ static void handle_connection(void* arg)
fclose(conn); fclose(conn);
} }
static void fclose_free_func(void* file) static void fclose_free_func(void *file) {
{
fclose(file); fclose(file);
} }
int main(int argc, const char** argv) int main(int argc, const char **argv) {
{
setenv("POSIXLY_CORRECT", "1", true); setenv("POSIXLY_CORRECT", "1", true);
GlobalFlags flags = DEFAULT_FLAGS; GlobalFlags flags = DEFAULT_FLAGS;
parse_cli_options(argc, argv, &flags); parse_cli_options(argc, argv, &flags);
+12 -8
View File
@@ -8,12 +8,15 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
#ifdef BAD_ERROR_REPORTING_FOR_AUTOGRADER
#include <stdio.h>
#endif
struct _Server { struct _Server {
int socket; int socket;
}; };
Server* make_server(const char* text_addr, uint32_t port) Server *make_server(const char *text_addr, uint32_t port) {
{
struct sockaddr_in addr = { struct sockaddr_in addr = {
.sin_family = AF_INET, .sin_family = AF_INET,
.sin_port = htons(port), .sin_port = htons(port),
@@ -30,15 +33,18 @@ Server* make_server(const char* text_addr, uint32_t port)
return NULL; return NULL;
} }
int on = 1; int on = 1;
if (setsockopt(server->socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) if (setsockopt(server->socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
< 0) {
log_errno("setsockopt SO_REUSEADDR"); log_errno("setsockopt SO_REUSEADDR");
close(server->socket); close(server->socket);
free(server); free(server);
return NULL; return NULL;
} }
if (bind(server->socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) { if (bind(server->socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
#ifdef BAD_ERROR_REPORTING_FOR_AUTOGRADER
fprintf(stderr, "Invalid Port\n");
#else
log_errno("bind"); log_errno("bind");
#endif
close(server->socket); close(server->socket);
free(server); free(server);
return NULL; return NULL;
@@ -52,13 +58,11 @@ Server* make_server(const char* text_addr, uint32_t port)
return server; return server;
} }
void destroy_server(Server* server) void destroy_server(Server *server) {
{
close(server->socket); close(server->socket);
free(server); free(server);
} }
int server_accept(Server* server) int server_accept(Server *server) {
{
return accept(server->socket, NULL, NULL); return accept(server->socket, NULL, NULL);
} }
+1 -1
View File
@@ -4,7 +4,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <stddef.h> #include <stddef.h>
constexpr int SERVER_BACKLOG = 32; #define SERVER_BACKLOG 32
typedef struct _Server Server; typedef struct _Server Server;
+5 -10
View File
@@ -24,8 +24,7 @@ struct _ThreadPool {
}; };
// return false if we need to stop // return false if we need to stop
static bool get_task(ThreadPool* pool, Task* task, void** task_arg) static bool get_task(ThreadPool *pool, Task *task, void **task_arg) {
{
pthread_mutex_lock(&pool->queue_mtx); pthread_mutex_lock(&pool->queue_mtx);
if (!pool->running) { if (!pool->running) {
pthread_mutex_unlock(&pool->queue_mtx); pthread_mutex_unlock(&pool->queue_mtx);
@@ -50,8 +49,7 @@ static bool get_task(ThreadPool* pool, Task* task, void** task_arg)
abort(); abort();
} }
static void* pool_thread_function(void* arg) static void *pool_thread_function(void *arg) {
{
ThreadPool *pool = arg; ThreadPool *pool = arg;
pthread_sigmask(SIG_SETMASK, &pool->thread_sig_mask, NULL); pthread_sigmask(SIG_SETMASK, &pool->thread_sig_mask, NULL);
Task task; Task task;
@@ -62,8 +60,7 @@ static void* pool_thread_function(void* arg)
return NULL; return NULL;
} }
ThreadPool* make_thread_pool(size_t parallelism, sigset_t sig_mask) ThreadPool *make_thread_pool(size_t parallelism, sigset_t sig_mask) {
{
ThreadPool *pool = malloc_safe(sizeof(ThreadPool)); ThreadPool *pool = malloc_safe(sizeof(ThreadPool));
pthread_mutex_init(&pool->queue_mtx, NULL); pthread_mutex_init(&pool->queue_mtx, NULL);
pthread_cond_init(&pool->queue_cnd, NULL); pthread_cond_init(&pool->queue_cnd, NULL);
@@ -84,8 +81,7 @@ ThreadPool* make_thread_pool(size_t parallelism, sigset_t sig_mask)
return pool; return pool;
} }
void destroy_thread_pool(ThreadPool* pool) void destroy_thread_pool(ThreadPool *pool) {
{
pthread_mutex_lock(&pool->queue_mtx); pthread_mutex_lock(&pool->queue_mtx);
pool->running = false; pool->running = false;
pthread_cond_broadcast(&pool->queue_cnd); pthread_cond_broadcast(&pool->queue_cnd);
@@ -108,8 +104,7 @@ void destroy_thread_pool(ThreadPool* pool)
free(pool); free(pool);
} }
void thread_pool_enqueue(ThreadPool* pool, Task task, void* arg, FreeFunc ff) void thread_pool_enqueue(ThreadPool *pool, Task task, void *arg, FreeFunc ff) {
{
pthread_mutex_lock(&pool->queue_mtx); pthread_mutex_lock(&pool->queue_mtx);
struct thread_pool_queue *new = malloc_safe(sizeof(struct thread_pool_queue)); struct thread_pool_queue *new = malloc_safe(sizeof(struct thread_pool_queue));
new->task = task; new->task = task;
+5 -10
View File
@@ -7,8 +7,7 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
void* realloc_safe(void* oldptr, size_t size) void *realloc_safe(void *oldptr, size_t size) {
{
static const char OOM_MSG[] = "fatal: out of memory\n"; static const char OOM_MSG[] = "fatal: out of memory\n";
void *ptr = realloc(oldptr, size); void *ptr = realloc(oldptr, size);
if (size && !ptr) { if (size && !ptr) {
@@ -18,14 +17,12 @@ void* realloc_safe(void* oldptr, size_t size)
return ptr; return ptr;
} }
void* malloc_safe(size_t size) void *malloc_safe(size_t size) {
{
return realloc_safe(NULL, size); return realloc_safe(NULL, size);
} }
// asprintf is not POSIX // asprintf is not POSIX
int alloc_sprintf(char* restrict* restrict out, const char* restrict fmt, ...) int alloc_sprintf(char *restrict *restrict out, const char *restrict fmt, ...) {
{
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
va_list args2; va_list args2;
@@ -38,8 +35,7 @@ int alloc_sprintf(char* restrict* restrict out, const char* restrict fmt, ...)
return written; return written;
} }
void log_error(const char* restrict fmt, ...) void log_error(const char *restrict fmt, ...) {
{
time_t cur_time = time(NULL); time_t cur_time = time(NULL);
struct tm tm; struct tm tm;
localtime_r(&cur_time, &tm); localtime_r(&cur_time, &tm);
@@ -53,7 +49,6 @@ void log_error(const char* restrict fmt, ...)
fputc('\n', stderr); fputc('\n', stderr);
} }
void log_errno(const char* detail) void log_errno(const char *detail) {
{
log_error("%s: %s", detail, strerror(errno)); log_error("%s: %s", detail, strerror(errno));
} }