From 26b0c6e4cf595dbf90f7e3c3bda54bbe0cfc3fa1 Mon Sep 17 00:00:00 2001 From: Roman Rashchupkin Date: Wed, 7 Feb 2018 12:50:25 +0300 Subject: [PATCH 1/3] Fix storage_lookup_patches when process already exited If process terminated before storage_lookup_patches, then no buildids can be found and function must return -1. --- src/kpatch_storage.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/kpatch_storage.c b/src/kpatch_storage.c index a466460..6dbadac 100644 --- a/src/kpatch_storage.c +++ b/src/kpatch_storage.c @@ -398,7 +398,7 @@ int storage_lookup_patches(kpatch_storage_t *storage, kpatch_process_t *proc) struct kp_file *pkpfile; struct object_file *o; const char *bid; - int found = 0, ret; + int found = -1, ret; list_for_each_entry(o, &proc->objs, list) { if (!o->is_elf || is_kernel_object_name(o->name)) @@ -410,6 +410,8 @@ int storage_lookup_patches(kpatch_storage_t *storage, kpatch_process_t *proc) o->name); continue; } + if (found < 0) + found = 0; ret = storage_load_patch(storage, bid, &pkpfile); if (ret == PATCH_OPEN_ERROR) { From 363eacbeffb0c259dd261e98f84fc49012466bea Mon Sep 17 00:00:00 2001 From: Roman Rashchupkin Date: Wed, 7 Feb 2018 18:37:19 +0300 Subject: [PATCH 2/3] Add libcare-stresstest logging to files -change some printfs to logging --- src/kpatch_log.c | 29 +++++++++++++++++ src/kpatch_log.h | 3 ++ src/kpatch_patch.c | 14 ++++---- src/kpatch_process.c | 14 ++++---- src/kpatch_user.c | 76 ++++++++++++++++++++++++++++++++++++-------- 5 files changed, 108 insertions(+), 28 deletions(-) diff --git a/src/kpatch_log.c b/src/kpatch_log.c index de80f7b..125843e 100644 --- a/src/kpatch_log.c +++ b/src/kpatch_log.c @@ -5,9 +5,13 @@ #include #include "kpatch_log.h" +#ifdef STRESS_TEST +#include "kpatch_user.h" +#endif int log_level = LOG_INFO; int log_indent; +static FILE *log_file; static void __valog(int level, const char *prefix, const char *fmt, va_list va) { @@ -15,6 +19,14 @@ static void __valog(int level, const char *prefix, const char *fmt, va_list va) if (prefix) fprintf(f, "%s", prefix); + if (log_file) { + va_list vaf; + va_copy(vaf, va); + if (prefix) + fprintf(log_file, "%s", prefix); + vfprintf(log_file, fmt, vaf); + fflush(log_file); + } vfprintf(f, fmt, va); } @@ -100,3 +112,20 @@ void _kpfatalerror(const char *file, int line, const char *fmt, ...) exit(EXIT_FAILURE); } + +int log_file_init(char *fname) +{ + if (!fname) + return -1; + log_file = fopen(fname, "a"); + if (!log_file) + return -1; + return 0; +} + +void log_file_free() +{ + if (log_file) + fclose(log_file); + log_file = NULL; +} diff --git a/src/kpatch_log.h b/src/kpatch_log.h index dfb8370..ff9d268 100644 --- a/src/kpatch_log.h +++ b/src/kpatch_log.h @@ -33,4 +33,7 @@ void _kplogerror(const char *filename, int line, const char *fmt, ...) #define LOG_DEBUG 3 #define LOG_TRACE 5 +int log_file_init(char *fname); +void log_file_free(); + #endif diff --git a/src/kpatch_patch.c b/src/kpatch_patch.c index e32c702..f2cc672 100644 --- a/src/kpatch_patch.c +++ b/src/kpatch_patch.c @@ -437,7 +437,7 @@ object_unapply_old_patch(struct object_file *o) return 1; } - printf("%s: replacing patch level %d with level %d\n", + kpinfo("%s: replacing patch level %d with level %d\n", o->name, kpatch_applied->user_level, kpatch_storage->user_level); @@ -565,12 +565,12 @@ int process_patch(int pid, void *_data) out: if (ret < 0) { - printf("Failed to apply patch '%s'\n", storage->path); + kpinfo("Failed to apply patch '%s'\n", storage->path); kperr("Failed to apply patch '%s'\n", storage->path); } else if (ret == 0) - printf("No patch(es) applicable to PID '%d' have been found\n", pid); + kpinfo("No patch(es) applicable to PID '%d' have been found\n", pid); else { - printf("%d patch hunk(s) have been successfully applied to PID '%d'\n", ret, pid); + kpinfo("%d patch hunk(s) have been successfully applied to PID '%d'\n", ret, pid); ret = 0; } @@ -750,11 +750,11 @@ int process_unpatch(int pid, void *_data) kpatch_process_free(proc); if (ret < 0) - printf("Failed to cancel patches for %d\n", pid); + kpinfo("Failed to cancel patches for %d\n", pid); else if (ret == 0) - printf("No patch(es) cancellable from PID '%d' were found\n", pid); + kpinfo("No patch(es) cancellable from PID '%d' were found\n", pid); else - printf("%d patch hunk(s) were successfully cancelled from PID '%d'\n", ret, pid); + kpinfo("%d patch hunk(s) were successfully cancelled from PID '%d'\n", ret, pid); return ret; } diff --git a/src/kpatch_process.c b/src/kpatch_process.c index 2f85373..a0ea5f5 100644 --- a/src/kpatch_process.c +++ b/src/kpatch_process.c @@ -746,9 +746,9 @@ process_print_cmdline(kpatch_process_t *proc) for (i = 0; i < rv; i++) { if (buf[i] != '\n' && isprint(buf[i])) - putchar(buf[i]); + kpinfo("%c", buf[i]); else - printf("\\x%02x", (unsigned char)buf[i]); + kpinfo("\\x%02x", (unsigned char)buf[i]); } } @@ -860,9 +860,9 @@ kpatch_process_kickstart_execve_wrapper(kpatch_process_t *proc) if (ret < 0) return -1; - printf("kpatch_ctl real cmdline=\""); + kpinfo("kpatch_ctl real cmdline=\""); process_print_cmdline(proc); - printf("\"\n"); + kpinfo("\"\n"); return 0; } @@ -1137,11 +1137,11 @@ kpatch_process_init(kpatch_process_t *proc, void kpatch_process_print_short(kpatch_process_t *proc) { - printf("kpatch_ctl targeting pid %d\n", proc->pid); + kpinfo("kpatch_ctl targeting pid %d\n", proc->pid); if (proc->send_fd == -1) { - printf("kpatch_ctl cmdline=\""); + kpinfo("kpatch_ctl cmdline=\""); process_print_cmdline(proc); - printf("\"\n"); + kpinfo("\"\n"); } } diff --git a/src/kpatch_user.c b/src/kpatch_user.c index e6649b0..13ea2fa 100644 --- a/src/kpatch_user.c +++ b/src/kpatch_user.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -566,7 +567,11 @@ cmd_update(int argc, char *argv[]) #ifdef STRESS_TEST -struct test_data { +static int process_pid = -1; +static char test_log_base[PATH_MAX-sizeof("-4194304")]; +static char test_log_name[PATH_MAX]; + +static struct test_data { int option_period; int stat_cycle_num; } test_info = { .option_period = 0, .stat_cycle_num = 0 }; @@ -581,7 +586,7 @@ server_wait(int pid, int period) for (i=0; i= PATH_MAX) { + kplogerror("Can't initialize log \'%s\'-\n", test_log_base); + return -1; + } + } else + if (snprintf(test_log_name, PATH_MAX, "%s-%d", test_log_base, getpid()) >= PATH_MAX) { + kplogerror("Can't initialize log \'%s\'-\n", test_log_base); + return -1; + } + if (log_file_init(test_log_name)) { + kplogerror("Can't open log file \'%s\'\n", test_log_name); + return -1; + } + return 0; +} + static int cmd_stress_test(int fd, int argc, char *argv[]) { + if (sscanf(argv[1], "%d", &process_pid) != 1) { + kplogerror("Can't parse pid from %s\n", argv[1]); + return -1; + } + kpinfo("Spawning child to patch pid %d\n", process_pid); + int child = fork(); if (child == 0) { - int rv = server_stress_test(fd, argc, argv); + log_file_free(); + if (stress_test_log_init(0)) + kpfatal("Can't initialize log.\n"); + + int rv = server_stress_test(fd, process_pid); + + log_file_free(); exit(rv); } close(fd); @@ -640,7 +672,11 @@ static int cmd_stress_test(int fd, int argc, char *argv[]) static int usage_stresstest() { - fprintf(stderr, "usage: libcare-stresstest PERIOD(ms, 0 - only patch) [STORAGE ROOT]\n"); + fprintf(stderr, "usage: libcare-stresstest [args] PERIOD(ms, 0 - only patch) [STORAGE ROOT]\n"); + fprintf(stderr, "\nOptions:\n"); + fprintf(stderr, " -v - verbose mode\n"); + fprintf(stderr, " -l - log file name -PID\n"); + fprintf(stderr, " -h - this message\n"); return -1; } @@ -828,9 +864,11 @@ cmd_server(int argc, char *argv[]) } #ifdef STRESS_TEST - if (sscanf(argv[0], "%d", &test_info.option_period) != 1) { + if (stress_test_log_init(1)) + kpfatal("Can't initialize log.\n"); + if (sscanf(argv[0], "%d", &test_info.option_period) != 1) kplogerror("Can't parse period from %s\n", argv[0]); - } + kpinfo("Starting libcare-stresstest: period=%d\n", test_info.option_period); #endif sfd = server_bind_socket(argv[1]); @@ -954,13 +992,23 @@ int main(int argc, char *argv[]) { int opt; - while ((opt = getopt(argc, argv, "+vh")) != EOF) { + while ((opt = getopt(argc, argv, "+vl:h")) != EOF) { switch (opt) { case 'v': log_level += 1; break; case 'h': return usage(NULL); + case 'l': +#ifdef STRESS_TEST + strncpy(test_log_base, optarg, sizeof(test_log_base)); + if (test_log_base[sizeof(test_log_base)-1]) { + usage_stresstest(); + kpfatal("Can't initialize log\n"); + break; + } + break; +#endif default: return usage("unknown option"); } From a5dca6187fecd664bfbeed410c1188cea9dd778c Mon Sep 17 00:00:00 2001 From: Roman Rashchupkin Date: Fri, 9 Feb 2018 13:45:36 +0300 Subject: [PATCH 3/3] Notify libcare-stresstest about child events --- src/Makefile | 6 +++--- src/kpatch_log.c | 6 ++++++ src/kpatch_user.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- src/kpatch_user.h | 1 + 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index 58e942a..631c9ed 100644 --- a/src/Makefile +++ b/src/Makefile @@ -36,12 +36,12 @@ kpatch_make: kpatch_make.o LIBUNWIND_LIBS := $(shell pkg-config --libs libunwind libunwind-ptrace) -libcare-ctl: kpatch_user.o kpatch_storage.o kpatch_patch.c kpatch_elf.o kpatch_ptrace.o kpatch_coro.o +libcare-ctl: kpatch_user.o kpatch_storage.o kpatch_patch.o kpatch_elf.o kpatch_ptrace.o kpatch_coro.o libcare-ctl: kpatch_process.o kpatch_common.o rbtree.o kpatch_log.o libcare-ctl: LDLIBS += -lelf -lrt $(LIBUNWIND_LIBS) -libcare-stresstest: kpatch_user-stresstest.o kpatch_storage.o kpatch_patch.c kpatch_elf.o kpatch_ptrace.o kpatch_coro.o -libcare-stresstest: kpatch_process.o kpatch_common.o rbtree.o kpatch_log.o +libcare-stresstest: kpatch_user-stresstest.o kpatch_log-stresstest.o kpatch_storage.o kpatch_patch.o kpatch_elf.o kpatch_ptrace.o kpatch_coro.o +libcare-stresstest: kpatch_process.o kpatch_common.o rbtree.o libcare-stresstest: LDLIBS += -lelf -lrt $(LIBUNWIND_LIBS) libcare-client: libcare-client.o diff --git a/src/kpatch_log.c b/src/kpatch_log.c index 125843e..f1a593a 100644 --- a/src/kpatch_log.c +++ b/src/kpatch_log.c @@ -5,8 +5,10 @@ #include #include "kpatch_log.h" + #ifdef STRESS_TEST #include "kpatch_user.h" +extern int parent_pid; #endif int log_level = LOG_INFO; @@ -64,6 +66,10 @@ void kpfatal(const char *fmt, ...) __valog(LOG_ERR, "FATAL! ", fmt, va); va_end(va); +#ifdef STRESS_TEST + if (parent_pid >= 0) + stress_test_notify_parent(); +#endif exit(1); } diff --git a/src/kpatch_user.c b/src/kpatch_user.c index 13ea2fa..55a36e8 100644 --- a/src/kpatch_user.c +++ b/src/kpatch_user.c @@ -567,6 +567,7 @@ cmd_update(int argc, char *argv[]) #ifdef STRESS_TEST +int parent_pid = -1; static int process_pid = -1; static char test_log_base[PATH_MAX-sizeof("-4194304")]; static char test_log_name[PATH_MAX]; @@ -647,16 +648,53 @@ static int stress_test_log_init(int is_base_process) return 0; } +void stress_test_notify_parent() +{ + if (parent_pid < 0) + return; + union sigval code; + code.sival_int = process_pid; + sigqueue(parent_pid, SIGCHLD, code); +} + +void stress_test_signal_handler(int sig, siginfo_t *info, void *ucontext) +{ + switch (sig) { + case SIGCHLD: { + if (info->si_code == SI_QUEUE) { + kpinfo("Child process pid %d fatal error.\n", info->si_pid); + return; + } else { + int pid, status; + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + kpinfo("Process pid %d exited.\n", pid); + return; + } + break; + } + } +} + +void stress_test_install_sigaction() +{ + struct sigaction sigact; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_SIGINFO | SA_RESTART; + sigact.sa_sigaction = &stress_test_signal_handler; + sigaction(SIGCHLD, &sigact, NULL); +} + static int cmd_stress_test(int fd, int argc, char *argv[]) { if (sscanf(argv[1], "%d", &process_pid) != 1) { kplogerror("Can't parse pid from %s\n", argv[1]); return -1; } - kpinfo("Spawning child to patch pid %d\n", process_pid); + parent_pid = getpid(); int child = fork(); if (child == 0) { + signal(SIGCHLD, SIG_DFL); log_file_free(); if (stress_test_log_init(0)) kpfatal("Can't initialize log.\n"); @@ -666,6 +704,7 @@ static int cmd_stress_test(int fd, int argc, char *argv[]) log_file_free(); exit(rv); } + kpinfo("Spawned child %d to patch pid %d\n", child, process_pid); close(fd); return 0; } @@ -1020,7 +1059,8 @@ int main(int argc, char *argv[]) #ifdef STRESS_TEST if (argc < 3) return usage("not enough arguments."); - signal(SIGCHLD, SIG_IGN); + + stress_test_install_sigaction(); return cmd_server(argc, argv); #else if (argc < 1) diff --git a/src/kpatch_user.h b/src/kpatch_user.h index c0b52ff..719f649 100644 --- a/src/kpatch_user.h +++ b/src/kpatch_user.h @@ -7,5 +7,6 @@ int cmd_patch_user(int argc, char *argv[]); int cmd_unpatch_user(int argc, char *argv[]); +void stress_test_notify_parent(); #endif