添加信号处理
This commit is contained in:
2
Makefile
2
Makefile
@@ -35,7 +35,7 @@ prepare:
|
|||||||
@cp $(BUILD_DIR)/compile_commands.json $(MAKEFILE_DIR)
|
@cp $(BUILD_DIR)/compile_commands.json $(MAKEFILE_DIR)
|
||||||
|
|
||||||
build: prepare
|
build: prepare
|
||||||
${RM} -f $(TARGET)
|
${RM} $(TARGET)
|
||||||
@cmake --build $(BUILD_DIR)
|
@cmake --build $(BUILD_DIR)
|
||||||
@cp $(BUILD_DIR)/$(TARGET_NAME) $(TARGET)
|
@cp $(BUILD_DIR)/$(TARGET_NAME) $(TARGET)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.24)
|
|||||||
|
|
||||||
set(APP_NAME "zdwm")
|
set(APP_NAME "zdwm")
|
||||||
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
set(BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
|
||||||
add_compile_definitions(LOG_FILE="$ENV{HOME}/zdwm-log.txt")
|
add_compile_definitions(LOG_FILE="$ENV{HOME}/zdwm-log.txt")
|
||||||
|
|
||||||
@@ -18,7 +19,49 @@ project(${APP_NAME}
|
|||||||
LANGUAGES C
|
LANGUAGES C
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(${APP_NAME} main.c)
|
add_executable(${APP_NAME} main.c utils.c buffer.c backtrace.c)
|
||||||
|
|
||||||
# use C23
|
# use C23
|
||||||
set_property(TARGET ${APP_NAME} PROPERTY C_STANDARD 23)
|
set_property(TARGET ${APP_NAME} PROPERTY C_STANDARD 23)
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
|
pkg_check_modules(deps REQUIRED
|
||||||
|
glib-2.0
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(${APP_NAME} SYSTEM
|
||||||
|
PRIVATE ${deps_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
target_include_directories(${APP_NAME}
|
||||||
|
PRIVATE ${SOURCE_DIR}
|
||||||
|
PRIVATE ${BUILD_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${APP_NAME}
|
||||||
|
PRIVATE ${deps_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
## 配置项
|
||||||
|
# Check for backtrace_symbols()
|
||||||
|
include(CheckSymbolExists)
|
||||||
|
check_symbol_exists(backtrace_symbols execinfo.h HAS_EXECINFO)
|
||||||
|
if(NOT HAS_EXECINFO)
|
||||||
|
find_library(LIB_EXECINFO execinfo)
|
||||||
|
if(LIB_EXECINFO)
|
||||||
|
set(HAS_EXECINFO TRUE)
|
||||||
|
target_link_libraries(${APP_NAME} ${LIB_EXECINFO})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(HAS_EXECINFO)
|
||||||
|
message(STATUS "checking for execinfo -- found")
|
||||||
|
else()
|
||||||
|
message(STATUS "checking for execinfo -- not found")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
configure_file("${SOURCE_DIR}/config.h.in"
|
||||||
|
"${BUILD_DIR}/config.h"
|
||||||
|
ESCAPE_QUOTES
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
|||||||
38
src/backtrace.c
Normal file
38
src/backtrace.c
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#include "backtrace.h"
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifdef HAS_EXECINFO
|
||||||
|
#include <execinfo.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_STACK_SIZE 32
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a backtrace.
|
||||||
|
* @param buffer The buffer to fill with backtrace.
|
||||||
|
*/
|
||||||
|
void backtrace_get(buffer_t *buffer) {
|
||||||
|
buffer_init(buffer);
|
||||||
|
|
||||||
|
#ifdef HAS_EXECINFO
|
||||||
|
void *stack[MAX_STACK_SIZE];
|
||||||
|
char **bt;
|
||||||
|
int stack_size;
|
||||||
|
|
||||||
|
stack_size = backtrace(stack, countof(stack));
|
||||||
|
bt = backtrace_symbols(stack, stack_size);
|
||||||
|
|
||||||
|
if (bt) {
|
||||||
|
for (int i = 0; i < stack_size; i++) {
|
||||||
|
if (i > 0) buffer_addsl(buffer, "\n");
|
||||||
|
buffer_adds(buffer, bt[i]);
|
||||||
|
}
|
||||||
|
p_delete(&bt);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
|
||||||
|
buffer_addsl(buffer, "Cannot get backtrace symbols.");
|
||||||
|
}
|
||||||
5
src/backtrace.h
Normal file
5
src/backtrace.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
void backtrace_get(buffer_t *buffer);
|
||||||
37
src/buffer.c
Normal file
37
src/buffer.c
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sysexits.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
char buffer_slop[1];
|
||||||
|
|
||||||
|
void buffer_ensure(buffer_t *buf, int newlen) {
|
||||||
|
if (newlen < 0) exit(EX_SOFTWARE);
|
||||||
|
|
||||||
|
if (newlen < buf->size) return;
|
||||||
|
|
||||||
|
if (newlen < buf->offs + buf->size && buf->offs > buf->size / 4) {
|
||||||
|
/* Data fits in the current area, shift it left */
|
||||||
|
memmove(buf->s - buf->offs, buf->s, buf->len + 1);
|
||||||
|
buf->s -= buf->offs;
|
||||||
|
buf->size += buf->offs;
|
||||||
|
buf->offs = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf->size = p_alloc_nr(buf->size + buf->offs);
|
||||||
|
if (buf->size < newlen + 1) buf->size = newlen + 1;
|
||||||
|
if (buf->alloced && !buf->offs)
|
||||||
|
p_realloc(&buf->s, buf->size);
|
||||||
|
else {
|
||||||
|
char *new_area = xmalloc(buf->size);
|
||||||
|
memcpy(new_area, buf->s, buf->len + 1);
|
||||||
|
if (buf->alloced) free(buf->s - buf->offs);
|
||||||
|
buf->alloced = true;
|
||||||
|
buf->s = new_area;
|
||||||
|
buf->offs = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/buffer.h
Normal file
78
src/buffer.h
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
typedef struct buffer_t {
|
||||||
|
char *s;
|
||||||
|
int len, size;
|
||||||
|
unsigned alloced : 1;
|
||||||
|
unsigned offs : 31;
|
||||||
|
} buffer_t;
|
||||||
|
|
||||||
|
extern char buffer_slop[1];
|
||||||
|
|
||||||
|
#define BUFFER_INIT (buffer_t){.s = buffer_slop, .size = 1}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a buffer.
|
||||||
|
* @param buf A buffer pointer.
|
||||||
|
* @return The same buffer pointer.
|
||||||
|
*/
|
||||||
|
static inline buffer_t *buffer_init(buffer_t *buf) {
|
||||||
|
*buf = BUFFER_INIT;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void buffer_ensure(buffer_t *buf, int len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add data in the buffer.
|
||||||
|
* @param buf Buffer where to add.
|
||||||
|
* @param pos Position where to add.
|
||||||
|
* @param len Length.
|
||||||
|
* @param data Data to add.
|
||||||
|
* @param dlen Data length.
|
||||||
|
*/
|
||||||
|
static inline void buffer_splice(buffer_t *buf, int pos, int len,
|
||||||
|
const void *data, int dlen) {
|
||||||
|
assert(pos >= 0 && len >= 0 && dlen >= 0);
|
||||||
|
|
||||||
|
if (unlikely(pos > buf->len)) pos = buf->len;
|
||||||
|
if (unlikely(len > buf->len - pos)) len = buf->len - pos;
|
||||||
|
if (pos == 0 && len + buf->offs >= dlen) {
|
||||||
|
buf->offs += len - dlen;
|
||||||
|
buf->s += len - dlen;
|
||||||
|
buf->size -= len - dlen;
|
||||||
|
buf->len -= len - dlen;
|
||||||
|
} else if (len != dlen) {
|
||||||
|
buffer_ensure(buf, buf->len + dlen - len);
|
||||||
|
memmove(buf->s + pos + dlen, buf->s + pos + len, buf->len - pos - len);
|
||||||
|
buf->len += dlen - len;
|
||||||
|
buf->s[buf->len] = '\0';
|
||||||
|
}
|
||||||
|
memcpy(buf->s + pos, data, dlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add data at the end of buffer.
|
||||||
|
* @param buf Buffer where to add.
|
||||||
|
* @param data Data to add.
|
||||||
|
* @param len Data length.
|
||||||
|
*/
|
||||||
|
static inline void buffer_add(buffer_t *buf, const void *data, int len) {
|
||||||
|
buffer_splice(buf, buf->len, 0, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define buffer_addsl(buf, data) buffer_add(buf, data, sizeof(data) - 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a string to the and of a buffer.
|
||||||
|
* @param buf The buffer where to add.
|
||||||
|
* @param s The string to add.
|
||||||
|
*/
|
||||||
|
static inline void buffer_adds(buffer_t *buf, const char *s) {
|
||||||
|
buffer_splice(buf, buf->len, 0, s, a_strlen(s));
|
||||||
|
}
|
||||||
5
src/config.h.in
Normal file
5
src/config.h.in
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define APP_NAME "@APP_NAME@"
|
||||||
|
|
||||||
|
#cmakedefine HAS_EXECINFO
|
||||||
87
src/main.c
87
src/main.c
@@ -1 +1,86 @@
|
|||||||
int main(int argc, char *argv[]) { return 0; }
|
#include <glib-unix.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include <glibconfig.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "backtrace.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
zdwm_t global_state;
|
||||||
|
|
||||||
|
/** argv used to run zdwm */
|
||||||
|
static char **zdwm_argv;
|
||||||
|
|
||||||
|
/** A pipe that is used to asynchronously handle SIGCHLD */
|
||||||
|
static int sigchld_pipe[2];
|
||||||
|
|
||||||
|
static void zdwm_atexit(bool restart) { global_state.need_restart = restart; }
|
||||||
|
|
||||||
|
static void zdwm_restart(void) {
|
||||||
|
zdwm_atexit(true);
|
||||||
|
execvp(zdwm_argv[0], zdwm_argv);
|
||||||
|
fatal("execv() failed: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean restart_on_signal(gpointer data) {
|
||||||
|
zdwm_restart();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean exit_on_signal(gpointer data) {
|
||||||
|
g_main_loop_quit(global_state.loop);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void signal_fatal(int signal_number) {
|
||||||
|
buffer_t buffer;
|
||||||
|
backtrace_get(&buffer);
|
||||||
|
fatal("signal %d, dumping backtrace\n%s", signal_number, buffer.s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Signal handler for SIGCHLD. Causes reap_children() to be called. */
|
||||||
|
static void signal_child(int signum) {
|
||||||
|
assert(signum == SIGCHLD);
|
||||||
|
int res = write(sigchld_pipe[1], " ", 1);
|
||||||
|
(void)res;
|
||||||
|
assert(res == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
p_clear(&global_state, 1);
|
||||||
|
|
||||||
|
zdwm_argv = argv;
|
||||||
|
|
||||||
|
g_unix_signal_add(SIGINT, exit_on_signal, NULL);
|
||||||
|
g_unix_signal_add(SIGTERM, exit_on_signal, NULL);
|
||||||
|
g_unix_signal_add(SIGHUP, restart_on_signal, NULL);
|
||||||
|
|
||||||
|
struct sigaction sa = {.sa_handler = signal_fatal, .sa_flags = SA_RESETHAND};
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sigaction(SIGABRT, &sa, 0);
|
||||||
|
sigaction(SIGBUS, &sa, 0);
|
||||||
|
sigaction(SIGFPE, &sa, 0);
|
||||||
|
sigaction(SIGILL, &sa, 0);
|
||||||
|
sigaction(SIGSEGV, &sa, 0);
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
|
sa.sa_handler = signal_child;
|
||||||
|
sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
|
||||||
|
sigaction(SIGCHLD, &sa, 0);
|
||||||
|
|
||||||
|
if (global_state.loop == NULL) {
|
||||||
|
global_state.loop = g_main_loop_new(NULL, FALSE);
|
||||||
|
g_main_loop_run(global_state.loop);
|
||||||
|
}
|
||||||
|
g_main_loop_unref(global_state.loop);
|
||||||
|
global_state.loop = NULL;
|
||||||
|
|
||||||
|
zdwm_atexit(false);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|||||||
8
src/types.h
Normal file
8
src/types.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
typedef struct zdwm_t {
|
||||||
|
bool need_restart;
|
||||||
|
GMainLoop *loop;
|
||||||
|
} zdwm_t;
|
||||||
34
src/utils.c
Normal file
34
src/utils.c
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
const char *current_time_str(void) {
|
||||||
|
static char buffer[25];
|
||||||
|
time_t now;
|
||||||
|
struct tm tm;
|
||||||
|
|
||||||
|
time(&now);
|
||||||
|
localtime_r(&now, &tm);
|
||||||
|
if (!strftime(buffer, sizeof(buffer), "%Y-%m-%d %T", &tm)) buffer[0] = '\0';
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Print error and exit with EXIT_FAILURE code.
|
||||||
|
*/
|
||||||
|
void _fatal(int line, const char *fct, const char *file, const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
const char *format = "(EE) [%s] %s\n%s:%d:%s:\n";
|
||||||
|
fprintf(stderr, format, APP_NAME, current_time_str(), file, line, fct);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
66
src/utils.h
Normal file
66
src/utils.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define ssizeof(foo) (ssize_t)sizeof(foo)
|
||||||
|
#define countof(foo) (ssizeof(foo) / ssizeof(foo[0]))
|
||||||
|
|
||||||
|
#define p_alloc_nr(x) (((x) + 16) * 3 / 2)
|
||||||
|
#define p_clear(p, count) ((void)memset((p), 0, sizeof(*(p)) * (count)))
|
||||||
|
#define p_realloc(pp, count) xrealloc((void *)(pp), sizeof(**(pp)) * (count))
|
||||||
|
|
||||||
|
#define p_delete(mem_p) \
|
||||||
|
do { \
|
||||||
|
void **__ptr = (void **)(mem_p); \
|
||||||
|
free(*__ptr); \
|
||||||
|
*(void **)__ptr = NULL; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define likely(expr) __builtin_expect(!!(expr), 1)
|
||||||
|
#define unlikely(expr) __builtin_expect((expr), 0)
|
||||||
|
#else
|
||||||
|
#define likely(expr) expr
|
||||||
|
#define unlikely(expr) expr
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline void *__attribute__((malloc)) xmalloc(ssize_t size) {
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
if (size <= 0) return NULL;
|
||||||
|
|
||||||
|
ptr = calloc(1, size);
|
||||||
|
|
||||||
|
if (!ptr) abort();
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void xrealloc(void **ptr, ssize_t newsize) {
|
||||||
|
if (newsize <= 0)
|
||||||
|
p_delete(ptr);
|
||||||
|
else {
|
||||||
|
*ptr = realloc(*ptr, newsize);
|
||||||
|
if (!*ptr) abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief NULL resistant strlen.
|
||||||
|
*
|
||||||
|
* Unlike it's libc sibling, a_strlen returns a ssize_t, and supports its
|
||||||
|
* argument being NULL.
|
||||||
|
*
|
||||||
|
* @param s the string.
|
||||||
|
* @return the string length (or 0 if s is NULL).
|
||||||
|
*/
|
||||||
|
static inline ssize_t a_strlen(const char *s) { return s ? strlen(s) : 0; }
|
||||||
|
|
||||||
|
#define fatal(format, ...) \
|
||||||
|
_fatal(__LINE__, __FUNCTION__, __FILE__, format, ##__VA_ARGS__)
|
||||||
|
void _fatal(int line, const char *function, const char *file,
|
||||||
|
const char *format, ...) __attribute__((noreturn))
|
||||||
|
__attribute__((format(printf, 4, 5)));
|
||||||
|
|
||||||
|
const char *current_time_str(void);
|
||||||
Reference in New Issue
Block a user