Skip to content

Commit ae1c80f

Browse files
authored
fix: potential fix for malloc deadlock in some corner cases (#30)
1 parent fbc26e8 commit ae1c80f

File tree

3 files changed

+68
-40
lines changed

3 files changed

+68
-40
lines changed

src/Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ else
3232
DEBUG_CFLAGS=-O2 -Os
3333
endif
3434
ifeq ($(shell expr $(GCC_VERSION) \< "8.0.0" ), 1)
35-
_CFLAGS=$(CFLAGS) -I. $(shell pkg-config --cflags $(_STATIC_EXTRA_OPT) glib-2.0) -fPIC -Wall -std=c99 -Wextra -pedantic -Wshadow -Wstrict-overflow -Wno-deprecated-declarations -fno-strict-aliasing -DG_LOG_DOMAIN=\"log_proxy\" $(DEBUG_CFLAGS) $(COVERAGE_CFLAGS)
35+
_CFLAGS=$(CFLAGS) -I. $(shell pkg-config --cflags $(_STATIC_EXTRA_OPT) glib-2.0 gthread-2.0) -fPIC -Wall -std=c99 -Wextra -pedantic -Wshadow -Wstrict-overflow -Wno-deprecated-declarations -fno-strict-aliasing -DG_LOG_DOMAIN=\"log_proxy\" $(DEBUG_CFLAGS) $(COVERAGE_CFLAGS)
3636
else
37-
_CFLAGS=$(CFLAGS) -I. $(shell pkg-config --cflags $(_STATIC_EXTRA_OPT) glib-2.0) -fPIC -Wall -std=c99 -Wextra -pedantic -Wshadow -Wstrict-overflow -Wno-deprecated-declarations -Wno-cast-function-type -fno-strict-aliasing -DG_LOG_DOMAIN=\"log_proxy\" $(DEBUG_CFLAGS) $(COVERAGE_CFLAGS)
37+
_CFLAGS=$(CFLAGS) -I. $(shell pkg-config --cflags $(_STATIC_EXTRA_OPT) glib-2.0 gthread-2.0) -fPIC -Wall -std=c99 -Wextra -pedantic -Wshadow -Wstrict-overflow -Wno-deprecated-declarations -Wno-cast-function-type -fno-strict-aliasing -DG_LOG_DOMAIN=\"log_proxy\" $(DEBUG_CFLAGS) $(COVERAGE_CFLAGS)
3838
endif
39-
_LDFLAGS=$(LDFLAGS) -L. $(shell pkg-config --libs $(_STATIC_EXTRA_OPT) glib-2.0) $(shell echo '$(FORCE_RPATH_STR)' |sed 's/@/$$/g' |sed s/~/"'"/g) -lrt
39+
_LDFLAGS=$(LDFLAGS) -L. $(shell pkg-config --libs $(_STATIC_EXTRA_OPT) glib-2.0 gthread-2.0) $(shell echo '$(FORCE_RPATH_STR)' |sed 's/@/$$/g' |sed s/~/"'"/g) -lrt
4040

4141
OBJECTS=util.o control.o out.o
4242
BINARIES=log_proxy log_proxy_wrapper

src/log_proxy.c

Lines changed: 65 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,19 @@
1717

1818
struct sigaction sigact;
1919
static gboolean first_iteration = TRUE;
20+
static GMutex *mutex = NULL;
21+
static GAsyncQueue *queue = NULL;
22+
static volatile gboolean stop_signal = FALSE;
23+
static GIOChannel *in = NULL;
2024

2125
gint _list_compare(gconstpointer a, gconstpointer b) {
2226
gchar *ca = (gchar *) a;
2327
gchar *cb = (gchar *) b;
2428
return g_strcmp0(cb, ca);
2529
}
2630

31+
#define UNUSED(x) (void)(x)
32+
2733
void clean_too_old_files() {
2834
gchar *dirpath = g_path_get_dirname(log_file);
2935
GDir *dir = g_dir_open(dirpath, 0, NULL);
@@ -92,12 +98,11 @@ gboolean rotate() {
9298

9399
void signal_handler(int signum) {
94100
if ((signum == SIGTERM) || (signum == SIGTERM)) {
95-
// nice exit to execute exit_handler
96-
exit(0);
101+
stop_signal = TRUE;
97102
}
98103
}
99104

100-
static void every_second(int sig) {
105+
static void every_second() {
101106
int fd = lock_control_file(log_file);
102107
if (fd >= 0) {
103108
if (first_iteration) {
@@ -111,19 +116,11 @@ static void every_second(int sig) {
111116
destroy_output_channel();
112117
init_output_channel(log_file, use_locks, TRUE, chmod_str, chown_str, chgrp_str);
113118
unlock_control_file(fd);
114-
if (sig > 0) {
115-
// if sig<0, this is the final call before program end
116-
alarm(1);
117-
}
118119
return;
119120
}
120121
glong size = get_file_size(log_file);
121122
if (size < 0) {
122123
unlock_control_file(fd);
123-
if (sig > 0) {
124-
// if sig<0, this is the final call before program end
125-
alarm(1);
126-
}
127124
return;
128125
}
129126
gboolean must_rotate = FALSE;
@@ -149,18 +146,53 @@ static void every_second(int sig) {
149146
}
150147
unlock_control_file(fd);
151148
}
152-
if (sig > 0) {
153-
// if sig<0, this is the final call before program end
154-
alarm(1);
149+
}
150+
151+
gpointer stop_thread(gpointer data) {
152+
// we do this here and not in signal_handler to avoid malloc in signal
153+
// handlers
154+
UNUSED(data);
155+
while (stop_signal == FALSE) {
156+
sleep(1);
155157
}
158+
g_async_queue_push(queue, GINT_TO_POINTER(2));
159+
return NULL;
156160
}
157161

158-
void init_every_second_signal() {
159-
sigact.sa_handler = every_second;
160-
sigemptyset(&sigact.sa_mask);
161-
sigact.sa_flags = 0;
162-
sigaction(SIGALRM, &sigact, (struct sigaction *)NULL);
163-
alarm(1);
162+
gpointer management_thread(gpointer data) {
163+
gpointer qdata;
164+
GTimeVal tval;
165+
gint stop_flag = 0;
166+
UNUSED(data);
167+
while (TRUE) {
168+
g_get_current_time(&tval);
169+
g_time_val_add(&tval, 1000000);
170+
qdata = g_async_queue_timed_pop(queue, &tval);
171+
if (qdata != NULL) {
172+
stop_flag = GPOINTER_TO_INT(qdata);
173+
}
174+
g_mutex_lock(mutex);
175+
every_second();
176+
g_mutex_unlock(mutex);
177+
if (qdata != NULL) {
178+
break;
179+
}
180+
}
181+
if (stop_flag == 2) {
182+
// in this case (sigterm), we prefer to keep the mutex
183+
g_mutex_lock(mutex);
184+
}
185+
destroy_output_channel();
186+
if (rm_fifo_at_exit == TRUE) {
187+
if (fifo != NULL) {
188+
g_unlink(fifo);
189+
}
190+
}
191+
if (stop_flag == 2) {
192+
// we exit here for sigterm as main thread is blocked in reading
193+
exit(0);
194+
}
195+
return NULL;
164196
}
165197

166198
void init_or_reinit_output_channel(const gchar *lg_file, gboolean us_locks) {
@@ -174,14 +206,6 @@ void init_or_reinit_output_channel(const gchar *lg_file, gboolean us_locks) {
174206
unlock_control_file(lock_fd);
175207
}
176208

177-
void exit_handler() {
178-
if (rm_fifo_at_exit == TRUE) {
179-
if (fifo != NULL) {
180-
g_unlink(fifo);
181-
}
182-
}
183-
}
184-
185209
int main(int argc, char *argv[])
186210
{
187211
GOptionContext *context;
@@ -198,7 +222,9 @@ int main(int argc, char *argv[])
198222
g_print("%s", g_option_context_get_help(context, TRUE, NULL));
199223
exit(1);
200224
}
201-
atexit(exit_handler);
225+
g_thread_init(NULL);
226+
mutex = g_mutex_new();
227+
queue = g_async_queue_new();
202228
signal(SIGTERM, signal_handler);
203229
signal(SIGINT, signal_handler);
204230
set_default_values_from_env();
@@ -212,7 +238,6 @@ int main(int argc, char *argv[])
212238
}
213239
}
214240
g_free(log_dir);
215-
GIOChannel *in = NULL;
216241
if (fifo == NULL) {
217242
// We read from stdin
218243
in = g_io_channel_unix_new(fileno(stdin));
@@ -228,30 +253,34 @@ int main(int argc, char *argv[])
228253
GIOStatus in_status = G_IO_STATUS_NORMAL;
229254
GString *in_buffer = g_string_new(NULL);
230255
init_or_reinit_output_channel(log_file, use_locks);
231-
init_every_second_signal();
256+
g_thread_create(stop_thread, NULL, FALSE, NULL);
257+
GThread* management = g_thread_create(management_thread, NULL, TRUE, NULL);
232258
while ((in_status != G_IO_STATUS_EOF) && (in_status != G_IO_STATUS_ERROR)) {
233259
in_status = g_io_channel_read_line_string(in, in_buffer, NULL, NULL);
234260
if (in_status == G_IO_STATUS_NORMAL) {
261+
g_mutex_lock(mutex);
235262
while (TRUE) {
236263
gboolean write_status = write_output_channel(in_buffer);
237264
if (write_status == FALSE) {
238265
g_warning("error during write on: %s", log_file);
239-
alarm(0); // to avoid a potential deadlock with SIGALARM every_second() calls
240266
init_or_reinit_output_channel(log_file, use_locks);
241-
alarm(1);
242267
continue;
243268
}
244269
break;
245270
}
271+
g_mutex_unlock(mutex);
246272
}
247273
}
248-
alarm(0); // to avoid a potential deadlock with SIGALARM every_second() calls
249-
every_second(-1);
250-
destroy_output_channel();
274+
// if we are here, the "in" input channel is closed
275+
signal(SIGINT, SIG_DFL);
276+
signal(SIGTERM, SIG_DFL);
277+
g_async_queue_push(queue, GINT_TO_POINTER(1));
278+
g_thread_join(management);
251279
g_io_channel_shutdown(in, FALSE, NULL);
252280
g_io_channel_unref(in);
253281
g_string_free(in_buffer, TRUE);
254282
g_option_context_free(context);
255283
g_free(log_file);
284+
g_mutex_free(mutex);
256285
return 0;
257286
}

src/out.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include <stdlib.h>
77
#include <unistd.h>
88
#include <string.h>
9-
#include <signal.h>
109
#include <errno.h>
1110
#include "util.h"
1211
#include "control.h"

0 commit comments

Comments
 (0)