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 de80f7b..f1a593a 100644 --- a/src/kpatch_log.c +++ b/src/kpatch_log.c @@ -6,8 +6,14 @@ #include "kpatch_log.h" +#ifdef STRESS_TEST +#include "kpatch_user.h" +extern int parent_pid; +#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 +21,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); } @@ -52,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); } @@ -100,3 +118,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_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) { diff --git a/src/kpatch_user.c b/src/kpatch_user.c index e6649b0..55a36e8 100644 --- a/src/kpatch_user.c +++ b/src/kpatch_user.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -566,7 +567,12 @@ cmd_update(int argc, char *argv[]) #ifdef STRESS_TEST -struct test_data { +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]; + +static struct test_data { int option_period; int stat_cycle_num; } test_info = { .option_period = 0, .stat_cycle_num = 0 }; @@ -581,7 +587,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; +} + +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; + } + + parent_pid = getpid(); int child = fork(); if (child == 0) { - int rv = server_stress_test(fd, argc, argv); + signal(SIGCHLD, SIG_DFL); + 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); } + kpinfo("Spawned child %d to patch pid %d\n", child, process_pid); close(fd); return 0; } 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 +903,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 +1031,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"); } @@ -972,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