From 577dd36b00f32484158ea4faf543e77eb4091c05 Mon Sep 17 00:00:00 2001 From: aixiao Date: Thu, 9 May 2019 18:25:42 +0800 Subject: [PATCH] Add kill_all function to kill multiple processes Support for custom profiles --- Makefile | 8 +- README.md | 15 +- conf.c | 1 + cproxy.c | 60 ++++- cproxy.h | 5 +- cproxy_help.c | 14 +- kill.c | 617 ++++++++++++++++++++++++++++++++++++++++++++++++++ kill.h | 56 +++++ 8 files changed, 760 insertions(+), 16 deletions(-) create mode 100644 kill.c create mode 100644 kill.h diff --git a/Makefile b/Makefile index 90eed6b..98bd589 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,13 @@ -CC ?= gcc +CROSS_COMPILE ?= +CC := $(CROSS_COMPILE)gcc +STRIP := $(CROSS_COMPILE)strip CFLAGS += -g -Wall -I../iniparser/src -L../iniparser LIBS = -liniparser OBJ := cproxy -all: cproxy.o conf.o cproxy_request.o cproxy_help.o +all: cproxy.o conf.o cproxy_request.o cproxy_help.o kill.o $(CC) $(CFLAGS) -o $(OBJ) $^ $(LIBS) - strip $(OBJ) + $(STRIP) $(OBJ) -chmod a+x $(OBJ) .c.o: $(CC) $(CFLAGS) -c $< $(LIBS) diff --git a/README.md b/README.md index c290a86..661c968 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,17 @@ cd iniparser make cd ../cproxy - make clean; make \ No newline at end of file + make clean; make + +# Help Information + cproxy proxy server + Author: aixiao@aixiao.me + Usage: [-?hdsc] [-s signal] [-c filename] + + Options: + -h : help information + -d : daemon + -s signal : send signal to a master process: stop + -c filename : set configuration file (default: conf/nginx.conf) + + May 7 2019 18:48:10 Compile、link. \ No newline at end of file diff --git a/conf.c b/conf.c index 328e389..cf00967 100644 --- a/conf.c +++ b/conf.c @@ -18,6 +18,7 @@ void read_conf(char *file, conf *p) } memset(p->server_pid_file, 0, p->len_server_pid_file); memcpy(p->server_pid_file, iniparser_getstring(ini, "server:PID_FILE", NULL), p->len_server_pid_file); + //printf("%s\n", p->server_pid_file); // http ip p->len_http_ip = strlen(iniparser_getstring(ini, "http:http_ip", NULL)) + 1; diff --git a/cproxy.c b/cproxy.c index 75cc0e3..7b90b5b 100644 --- a/cproxy.c +++ b/cproxy.c @@ -1,4 +1,5 @@ #include "cproxy.h" +#include "kill.h" char *read_data(int client_sock, char *data, int *data_len) { @@ -183,9 +184,16 @@ int create_server_socket(int port) } // 守护 -int init_daemon(int nochdir, int noclose, conf *configure) +int init_daemon(int nochdir, int noclose, conf *configure, char *path) { - FILE *fp = fopen(configure->server_pid_file, "w"); + char *p = strcat(path, configure->server_pid_file); + FILE *fp = fopen(p, "w"); + if(fp == NULL) { + fclose(fp); + printf("%s Open Failed\n", p); + exit(1); + } + int pid; if ((pid = fork()) < 0) { @@ -258,9 +266,34 @@ void start_server(conf *configure) server_loop(configure); } +int stop(int signal, char *program_name) { + if (signal == 1) { + struct passwd *pwent = NULL; + pwent = getpwnam("root"); + return kill_all(15,1, &program_name, pwent); + } + + return 1; +} + +int get_executable_path(char *processdir, char *processname, int len) +{ + char *filename; + if (readlink("/proc/self/exe", processdir, len) <= 0) { + return -1; + } + filename = strrchr(processdir, '/'); + if (filename == NULL) + return -1; + ++filename; + strcpy(processname, filename); + *filename = '\0'; + return (int)(filename - processdir); +} + int _main(int argc, char *argv[]) { - //初始化全局变量 + // 初始化全局变量 header_buffer = (char *)malloc(BUF_SIZE); len_header_buffer = strlen(header_buffer); @@ -268,22 +301,37 @@ int _main(int argc, char *argv[]) len_complete_data = strlen(complete_data); char *inifile = "conf/cproxy.ini"; + char path[PATH_SIZE] = { 0 }; + char executable_filename[PATH_SIZE] = { 0 }; + (void)get_executable_path(path, executable_filename, sizeof(path)); + inifile=strcat(path, inifile); + conf *configure = (struct CONF *)malloc(sizeof(struct CONF)); read_conf(inifile, configure); local_port = configure->server_port; int opt; - char optstrs[] = ":l:dh"; + char optstrs[] = ":l:ds:c:h?"; while (-1 != (opt = getopt(argc, argv, optstrs))) { switch (opt) { case 'l': local_port = atoi(optarg); break; case 'd': - init_daemon(1, 1, configure); + (void)get_executable_path(path, executable_filename, sizeof(path)); + init_daemon(1, 1, configure, path); break; - case 'h': + case 's': + if (strcasecmp(optarg, "stop") == 0) + stop(1, executable_filename); + exit(0); + break; + case 'c': + inifile=optarg; + read_conf(inifile, configure); + break; + case 'h': case '?': help_information(); exit(0); break; diff --git a/cproxy.h b/cproxy.h index ebe7c47..6868f05 100644 --- a/cproxy.h +++ b/cproxy.h @@ -18,6 +18,9 @@ #include #include +#include + +#define PATH_SIZE 270 #define BUF_SIZE 8192 #define BUF_SIZES 1024 @@ -85,7 +88,7 @@ int receive_data(int socket, char *buffer, int len); void forward_data(int source_sock, int destination_sock); int create_connection(conf *configure, int SIGN); int create_server_socket(int port); -int init_daemon(int nochdir, int noclose, conf *configure); +int init_daemon(int nochdir, int noclose, conf *configure, char *path); void sigchld_handler(int signal); void server_loop(conf *configure); void start_server(conf *configure); diff --git a/cproxy_help.c b/cproxy_help.c index dc22618..ac2291c 100644 --- a/cproxy_help.c +++ b/cproxy_help.c @@ -10,20 +10,24 @@ char help_information(void) } author = { "aixiao@aixiao.me", "aixiao", "Author:", "Email :",}; static const char usage[] = - "usage: [-d] [-h]"; + "Usage: [-?hdsc] [-s signal] [-c filename]"; static const char *s_help[] = { - " -h: help information", - " -d: daemon", + "", + "Options:", + " -h : help information", + " -d : daemon", + " -s signal : send signal to a master process: stop", + " -c filename : set configuration file (default: conf/cproxy.ini)", "", 0 }; //fprintf(stderr, "%s %s\n", author.c, author.b); - fprintf(stderr, "%s %s\n", author.c, author.a); fprintf(stderr, "%s %s\n", name, subject); - fprintf(stderr, "%s %s\n", name, usage); + fprintf(stderr, "%s %s\n", author.c, author.a); + fprintf(stderr, "%s\n", usage); int l; for (l = 0; s_help[l]; l++) { diff --git a/kill.c b/kill.c new file mode 100644 index 0000000..1d29723 --- /dev/null +++ b/kill.c @@ -0,0 +1,617 @@ +#include "kill.h" + +static double +uptime() +{ + char * savelocale; + char buf[2048]; + FILE* file; + if (!(file=fopen( PROC_BASE "/uptime", "r"))) { + fprintf(stderr, "killall: error opening uptime file\n"); + exit(1); + } + savelocale = setlocale(LC_NUMERIC,"C"); + if (fscanf(file, "%2047s", buf) == EOF) perror("uptime"); + fclose(file); + setlocale(LC_NUMERIC,savelocale); + return atof(buf); +} + +/* process age from jiffies to seconds via uptime */ +static double process_age(const unsigned long long jf) +{ + double age; + double sc_clk_tck = sysconf(_SC_CLK_TCK); + assert(sc_clk_tck > 0); + age = uptime() - jf / sc_clk_tck; + if (age < 0L) + return 0L; + return age; +} + +typedef struct NAMEINFO { + const char *name; + int name_length; + struct stat st; +} NAMEINFO; + +static pid_t opt_ns_pid = 0; +static int verbose = 0, exact = 0, interactive = 0, reg = 0, + quiet = 0, wait_until_dead = 0, process_group = 0, + ignore_case = 0; +static long younger_than = 0, older_than = 0; + +enum ns_type { + IPCNS = 0, + MNTNS, + NETNS, + PIDNS, + USERNS, + UTSNS +}; + +static const char *ns_names[] = { + [IPCNS] = "ipc", + [MNTNS] = "mnt", + [NETNS] = "net", + [PIDNS] = "pid", + [USERNS] = "user", + [UTSNS] = "uts", +}; + +static int +load_proc_cmdline(const pid_t pid, const char *comm, char **command, int *got_long) +{ + FILE *file; + char *path, *p, *command_buf; + int cmd_size = 128; + int okay; + + if (asprintf (&path, PROC_BASE "/%d/cmdline", pid) < 0) + return -1; + if (!(file = fopen (path, "r"))) + { + free (path); + return -1; + } + free(path); + + if ( (command_buf = (char *)malloc (cmd_size)) == NULL) + exit(1); + + while (1) + { + /* look for actual command so we skip over initial "sh" if any */ + + /* 'cmdline' has arguments separated by nulls */ + for (p=command_buf; ; p++) + { + int c; + if (p == (command_buf + cmd_size)) + { + char *new_command_buf; + int cur_size = cmd_size; + cmd_size *= 2; + new_command_buf = (char *)realloc(command_buf, cmd_size); + if (!new_command_buf) { + if (command_buf) + free(command_buf); + exit (1); + } + command_buf = new_command_buf; + p = command_buf + cur_size; + } + c = fgetc(file); + if (c == EOF || c == '\0') + { + *p = '\0'; + break; + } else { + *p = c; + } + } + if (strlen(command_buf) == 0) { + okay = 0; + break; + } + p = strrchr(command_buf,'/'); + p = p ? p+1 : command_buf; + if (strncmp(p, comm, COMM_LEN-1) == 0) { + okay = 1; + if (!(*command = strdup(p))) { + free(command_buf); + exit(1); + } + break; + } + } + (void) fclose(file); + free(command_buf); + command_buf = NULL; + + if (exact && !okay) + { + if (verbose) + fprintf (stderr, _("killall: skipping partial match %s(%d)\n"), + comm, pid); + *got_long = okay; + return -1; + } + *got_long = okay; + return 0; +} + +static int +ask (char *name, pid_t pid, const int signal) +{ + int res; + size_t len; + char *line; + + line = NULL; + len = 0; + + do { + if (signal == SIGTERM) + printf (_("Kill %s(%s%d) ? (y/N) "), name, process_group ? "pgid " : "", + pid); + else + printf (_("Signal %s(%s%d) ? (y/N) "), name, process_group ? "pgid " : "", + pid); + + fflush (stdout); + + if (getline (&line, &len, stdin) < 0) + return 0; + /* Check for default */ + if (line[0] == '\n') { + free(line); + return 0; + } + res = rpmatch(line); + if (res >= 0) { + free(line); + return res; + } + } while(1); + /* Never should get here */ +} + +const char *get_ns_name(int id) { + if (id >= NUM_NS) + return NULL; + return ns_names[id]; +} + +static int get_ns(pid_t pid, int id) { + struct stat st; + char buff[50]; + snprintf(buff, sizeof(buff), "/proc/%i/ns/%s", pid, get_ns_name(id)); + if (stat(buff, &st)) + return 0; + else + return st.st_ino; +} + + +static void +free_regexp_list(regex_t *reglist, int names) +{ + int i; + for (i = 0; i < names; i++) + regfree(®list[i]); + free(reglist); +} + +static regex_t * +build_regexp_list(int names, char **namelist) +{ + int i; + regex_t *reglist; + int flag = REG_EXTENDED|REG_NOSUB; + + if (!(reglist = malloc (sizeof (regex_t) * names))) + { + perror ("malloc"); + exit (1); + } + + if (ignore_case) + flag |= REG_ICASE; + + for (i = 0; i < names; i++) + { + if (regcomp(®list[i], namelist[i], flag) != 0) + { + fprintf(stderr, _("killall: Bad regular expression: %s\n"), namelist[i]); + free_regexp_list(reglist, i); + exit (1); + } + } + return reglist; +} + +static NAMEINFO * +build_nameinfo(const int names, char **namelist) +{ + int i; + NAMEINFO *ni = NULL; + if ( (ni = malloc(sizeof(NAMEINFO) * names)) == NULL) + return NULL; + + for (i = 0; i < names; i++) + { + ni[i].name = namelist[i]; + ni[i].st.st_dev = 0; + if (!strchr (namelist[i], '/')) + { + ni[i].name_length = strlen (namelist[i]); + } + else if (stat (namelist[i], &(ni[i].st)) < 0) + { + perror (namelist[i]); + free(ni); + return NULL; + } + } + return ni; +} + +static pid_t * +create_pid_table(int *max_pids, int *pids) +{ + pid_t self, *pid_table; + int pid; + DIR *dir; + struct dirent *de; + + self = getpid (); + if (!(dir = opendir (PROC_BASE))) + { + perror (PROC_BASE); + exit (1); + } + *max_pids = 256; + pid_table = malloc (*max_pids * sizeof (pid_t)); + if (!pid_table) + { + perror ("malloc"); + exit (1); + } + *pids = 0; + while ( (de = readdir (dir)) != NULL) + { + if (!(pid = (pid_t) atoi (de->d_name)) || pid == self) + continue; + if (*pids == *max_pids) + { + if (!(pid_table = realloc (pid_table, 2 * *pids * sizeof (pid_t)))) + { + perror ("realloc"); + exit (1); + } + *max_pids *= 2; + } + pid_table[(*pids)++] = pid; + } + (void) closedir (dir); + return pid_table; +} + +static int +match_process_uid(pid_t pid, uid_t uid) +{ + char buf[128]; + uid_t puid; + FILE *f; + int re = -1; + + snprintf (buf, sizeof buf, PROC_BASE "/%d/status", pid); + if (!(f = fopen (buf, "r"))) + return 0; + + while (fgets(buf, sizeof buf, f)) + { + if (sscanf (buf, "Uid:\t%d", &puid)) + { + re = uid==puid; + break; + } + } + fclose(f); + if (re==-1) + { + fprintf(stderr, _("killall: Cannot get UID from process status\n")); + exit(1); + } + return re; +} + +static int +load_process_name_and_age(char *comm, double *process_age_sec, + const pid_t pid, int load_age) +{ + FILE *file; + char *path; + char buf[1024]; + char *startcomm, *endcomm; + unsigned lencomm; + *process_age_sec = 0; + + if (asprintf (&path, PROC_BASE "/%d/stat", pid) < 0) + return -1; + if (!(file = fopen (path, "r"))) + { + free(path); + return -1; + } + free (path); + if (fgets(buf, 1024, file) == NULL) + { + fclose(file); + return -1; + } + fclose(file); + startcomm = strchr(buf, '(') + 1; + endcomm = strrchr(startcomm, ')'); + lencomm = endcomm - startcomm; + if (lencomm < 0) + lencomm = 0; + if (lencomm > COMM_LEN -1) + lencomm = COMM_LEN -1; + strncpy(comm, startcomm, lencomm); + comm[lencomm] = '\0'; + + endcomm += 2; // skip ") " + if (load_age) + { + unsigned long long proc_stt_jf = 0; + if (sscanf(endcomm, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %Lu", + &proc_stt_jf) != 1) + { + return -1; + } + *process_age_sec = process_age(proc_stt_jf); + } + return lencomm; +} + +#ifdef WITH_SELINUX +int +kill_all(int signal, int name_count, char **namelist, struct passwd *pwent, + regex_t *scontext ) +#else /*WITH_SELINUX*/ +int +kill_all (int signal, int name_count, char **namelist, struct passwd *pwent) +#endif /*WITH_SELINUX*/ +{ + struct stat st; + NAMEINFO *name_info = NULL; + char *path, comm[COMM_LEN]; + char *command = NULL; + pid_t *pid_table, *pid_killed; + pid_t *pgids = NULL; + int i, j, length, got_long, error; + int pids, max_pids, pids_killed; + unsigned long found; + regex_t *reglist = NULL; + long ns_ino = 0; +#ifdef WITH_SELINUX + security_context_t lcontext=NULL; +#endif /*WITH_SELINUX*/ + + if (opt_ns_pid) + ns_ino = get_ns(opt_ns_pid, PIDNS); + + if (name_count && reg) + reglist = build_regexp_list(name_count, namelist); + else + if ( (name_info = build_nameinfo(name_count, namelist)) == NULL) + exit(1); + + pid_table = create_pid_table(&max_pids, &pids); + found = 0; + pids_killed = 0; + pid_killed = malloc (max_pids * sizeof (pid_t)); + if (!pid_killed) + { + perror ("malloc"); + exit (1); + } + if (process_group) + { + pgids = calloc (pids, sizeof (pid_t)); + if (!pgids) + { + perror ("malloc"); + exit (1); + } + } + got_long = 0; + for (i = 0; i < pids; i++) + { + pid_t id; + int found_name = -1; + double process_age_sec = 0; + /* match by UID */ + if (pwent && match_process_uid(pid_table[i], pwent->pw_uid)==0) + continue; + if (opt_ns_pid && ns_ino && ns_ino != get_ns(pid_table[i], PIDNS)) + continue; + +#ifdef WITH_SELINUX + /* match by SELinux context */ + if (scontext) + { + if (getpidcon(pid_table[i], &lcontext) < 0) + continue; + if (regexec(scontext, lcontext, 0, NULL, 0) != 0) { + freecon(lcontext); + continue; + } + freecon(lcontext); + } +#endif /*WITH_SELINUX*/ + length = load_process_name_and_age(comm, &process_age_sec, pid_table[i], (younger_than||older_than)); + if (length < 0) + continue; + + /* test for process age, if required */ + if ( younger_than && (process_age_sec > younger_than ) ) + continue; + if ( older_than && (process_age_sec < older_than ) ) + continue; + + got_long = 0; + if (command) { + free(command); + command = NULL; + } + if (length == COMM_LEN - 1) + if (load_proc_cmdline(pid_table[i], comm, &command, &got_long) < 0) + continue; + + /* match by process name */ + for (j = 0; j < name_count; j++) + { + if (reg) + { + if (regexec (®list[j], got_long ? command : comm, 0, NULL, 0) != 0) + continue; + } + else /* non-regex */ + { + if (!name_info[j].st.st_dev) + { + if (length != COMM_LEN - 1 || name_info[j].name_length < COMM_LEN - 1) + { + if (ignore_case == 1) + { + if (strcasecmp (namelist[j], comm)) + continue; + } else { + if (strcmp(namelist[j], comm)) + continue; + } + } else { + if (ignore_case == 1) + { + if (got_long ? strcasecmp (namelist[j], command) : + strncasecmp (namelist[j], comm, COMM_LEN - 1)) + continue; + } else { + if (got_long ? strcmp (namelist[j], command) : + strncmp (namelist[j], comm, COMM_LEN - 1)) + continue; + } + } + } else { + int ok = 1; + if (asprintf (&path, PROC_BASE "/%d/exe", pid_table[i]) < 0) + continue; + if (stat (path, &st) < 0) + ok = 0; + else if (name_info[j].st.st_dev != st.st_dev || + name_info[j].st.st_ino != st.st_ino) + { + /* maybe the binary has been modified and std[j].st_ino + * is not reliable anymore. We need to compare paths. + */ + size_t len = strlen(namelist[j]); + char *linkbuf = malloc(len + 1); + + if (!linkbuf || + readlink(path, linkbuf, len + 1) != (ssize_t)len || + memcmp(namelist[j], linkbuf, len)) + ok = 0; + free(linkbuf); + } + free(path); + if (!ok) + continue; + } + } /* non-regex */ + found_name = j; + break; + } + if (name_count && found_name==-1) + continue; /* match by process name faild */ + + /* check for process group */ + if (!process_group) + id = pid_table[i]; + else + { + int j; + + id = getpgid (pid_table[i]); + pgids[i] = id; + if (id < 0) + { + fprintf (stderr, "killall: getpgid(%d): %s\n", + pid_table[i], strerror (errno)); + } + for (j = 0; j < i; j++) + if (pgids[j] == id) + break; + if (j < i) + continue; + } + if (interactive && !ask (comm, id, signal)) + continue; + if (kill (process_group ? -id : id, signal) >= 0) + { + if (verbose) + fprintf (stderr, _("Killed %s(%s%d) with signal %d\n"), got_long ? command : + comm, process_group ? "pgid " : "", id, signal); + if (found_name >= 0) + /* mark item of namelist */ + found |= 1UL << found_name; + pid_killed[pids_killed++] = id; + } + else if (errno != ESRCH || interactive) + fprintf (stderr, "%s(%d): %s\n", got_long ? command : + comm, id, strerror (errno)); + } + if (command) + free(command); + if (reglist) + free_regexp_list(reglist, name_count); + free(pgids); + if (!quiet) + for (i = 0; i < name_count; i++) + if (!(found & (1UL << i))) + fprintf (stderr, _("%s: no process found\n"), namelist[i]); + if (name_count) + /* killall returns a zero return code if at least one process has + * been killed for each listed command. */ + error = found == ((1UL << (name_count - 1)) | ((1UL << (name_count - 1)) - 1)) ? 0 : 1; + else + /* in nameless mode killall returns a zero return code if at least + * one process has killed */ + error = pids_killed ? 0 : 1; + /* + * We scan all (supposedly) killed processes every second to detect dead + * processes as soon as possible in order to limit problems of race with + * PID re-use. + */ + while (pids_killed && wait_until_dead) + { + for (i = 0; i < pids_killed;) + { + if (kill (process_group ? -pid_killed[i] : pid_killed[i], 0) < 0 && + errno == ESRCH) + { + pid_killed[i] = pid_killed[--pids_killed]; + continue; + } + i++; + } + sleep (1); /* wait a bit longer */ + } + free(pid_killed); + free(pid_table); + free(name_info); + return error; +} + diff --git a/kill.h b/kill.h new file mode 100644 index 0000000..14200de --- /dev/null +++ b/kill.h @@ -0,0 +1,56 @@ +#ifndef KILL_H +#define KILL_H + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef I18N_H +#define I18N_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef ENABLE_NLS +#include +#include +#define _(String) gettext (String) +#else +#define _(String) (String) +#endif + +#endif + +#ifndef HAVE_RPMATCH +#define rpmatch(line) \ + ( (line == NULL)? -1 : \ + (*line == 'y' || *line == 'Y')? 1 : \ + (*line == 'n' || *line == 'N')? 0 : \ + -1 ) +#endif + +#define COMM_LEN 64 +#define PROC_BASE "/proc" +#define NUM_NS 6 + +int kill_all (int signal, int name_count, char **namelist, struct passwd *pwent); + +#endif