diff options
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | Makefile.am | 13 | ||||
-rw-r--r-- | tests/softkiller/Makefile | 9 | ||||
-rw-r--r-- | tests/softkiller/softkiller.c | 443 | ||||
-rw-r--r-- | tests/test-extensions/net/sourceforge/jnlp/browsertesting/browsers/Firefox.java | 2 |
5 files changed, 474 insertions, 3 deletions
@@ -1,3 +1,13 @@ +2012-12-03 Pavel Tisnovsky <[email protected]> + + * Makefile.am: Added new target for compiling softkiller. + * tests/softkiller/softkiller.c: + Added browser softkiller. + * tests/softkiller/Makefile: + Added makefile used to build and clean browser softkiller. + * tests/test-extensions/net/sourceforge/jnlp/browsertesting/browsers/Firefox.java: + Uncommented code used to close windows. + 2012-11-30 Adam Domurad <[email protected]> Breaks up IcedTeaPluginUtilities::javaResultToNPVariant into multiple, diff --git a/Makefile.am b/Makefile.am index c6918cb..d66f278 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,6 +39,7 @@ export EXPORTED_TEST_CERT_SUFFIX=crt export TEST_CERT_ALIAS=icedteaweb export PUBLIC_KEYSTORE=${HOME}/.icedtea/security/trusted.certs export PUBLIC_KEYSTORE_PASS=changeit +export SOFTKILLER=softkiller export JUNIT_RUNNER_JAR=$(abs_top_builddir)/junit-runner.jar export UNIT_CLASS_NAMES = $(abs_top_builddir)/unit_class_names @@ -808,9 +809,14 @@ $(REPRODUCERS_CLASS_NAMES): $(REPRODUCERS_CLASS_WHITELIST) done ; \ echo $$class_names > $(REPRODUCERS_CLASS_NAMES) +$(TESTS_DIR)/$(SOFTKILLER): + cd $(TESTS_SRCDIR)/$(SOFTKILLER); \ + $(MAKE) ; \ + mv $(SOFTKILLER) $(TESTS_DIR)/ + stamps/run-netx-dist-tests.stamp: stamps/netx-dist.stamp extra-lib/about.jar stamps/plugin.stamp launcher.build/$(javaws) \ javaws.desktop stamps/docs.stamp launcher.build/$(itweb_settings) itweb-settings.desktop \ - stamps/netx.stamp stamps/junit-jnlp-dist-dirs stamps/netx-dist-tests-import-cert-to-public \ + stamps/netx.stamp stamps/junit-jnlp-dist-dirs stamps/netx-dist-tests-import-cert-to-public $(TESTS_DIR)/softkiller \ stamps/test-extensions-compile.stamp stamps/compile-reproducers-testcases.stamp $(JUNIT_RUNNER_JAR) stamps/copy-reproducers-resources.stamp\ $(TESTS_DIR)/$(REPORT_STYLES_DIRNAME) $(REPRODUCERS_CLASS_NAMES) stamps/process-custom-reproducers.stamp cd $(TEST_EXTENSIONS_DIR) ; \ @@ -1191,7 +1197,10 @@ clean_tests_reports: rm -rf $(TESTS_DIR)/$(REPORT_STYLES_DIRNAME)/ rm -f $(TESTS_DIR)/*.html -clean-netx-dist-tests: clean_tests_reports netx-dist-tests-remove-cert-from-public clean-custom-reproducers +clean-$(SOFTKILLER): + rm $(TESTS_DIR)/softkiller + +clean-netx-dist-tests: clean_tests_reports netx-dist-tests-remove-cert-from-public clean-custom-reproducers clean-$(SOFTKILLER) rm -f test-extensions-source-files.txt rm -f test-extensions-tests-source-files.txt rm -f $(TEST_EXTENSIONS_COMPATIBILITY_SYMLINK) diff --git a/tests/softkiller/Makefile b/tests/softkiller/Makefile new file mode 100644 index 0000000..669f67b --- /dev/null +++ b/tests/softkiller/Makefile @@ -0,0 +1,9 @@ +# we need c99 because of snprintf function! +# (this function does not exist in C89/ANSI C) + +softkiller: softkiller.c + $(CC) -Wall -pedantic -std=c99 -o $@ -lX11 $< + +clean: + rm softkiller + diff --git a/tests/softkiller/softkiller.c b/tests/softkiller/softkiller.c new file mode 100644 index 0000000..b8539d1 --- /dev/null +++ b/tests/softkiller/softkiller.c @@ -0,0 +1,443 @@ +/* X Window app killer + * + * Author: Pavel Tisnovsky <[email protected]> + * + * Compile: + * gcc -Wall -pedantic -std=c99 -o softkiller softkiller.c -lX11 + * (please note that -std=c99 is needed because we use snprintf + * function which does not exist in C89/ANSI C) + * + * Run: + * ./softkiller PID + */ + + + +#include <stdio.h> +#include <stdlib.h> +#include <glob.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +/* + * Number of long decimal digits + 1 (for ASCIIZ storage) + */ +#define MAX_LONG_DECIMAL_DIGITS 21 + +/* + * Max line length in /proc/stat files + */ +#define MAX_LINE 8192 + +/* + * Max filename length for /proc/... files + */ +#define MAX_FILENAME 32 + +/* + * Return values + */ +#define EXIT_CODE_OK 0 +#define EXIT_CODE_ERROR 1 + +/* + * Different softkilling strategies + */ +#define TRY_TO_CLOSE_WINDOW 1 +#define TRY_TO_KILL_WINDOW 1 + +/* + * Delay between application of different softkilling strategies. + */ +#define SLEEP_AMOUNT 2 + +/* + * Not in c89/c99... + */ +#define file_no(FP) ((FP)->_fileno) + +/* + * Basic information about given process + */ +typedef struct ProcStruct +{ + long uid, pid, ppid; + char cmd[MAX_LINE]; +} ProcStruct; + +ProcStruct *P = NULL; + +int N = 0; + +Display *display; +Window root_window; +Atom atom_pid; + + + +/* + * Read basic process info from the file /proc/${PID}/stat + * where ${PID} is process ID. + */ +int read_process_info(char *file_name_part, ProcStruct *P) +{ + FILE *fin; + char filename[MAX_FILENAME]; + struct stat stat; + + /* try to open file /proc/${PID}/stat for reading */ + snprintf(filename, sizeof(filename), "%s%s", file_name_part, "/stat"); + fin = fopen(filename, "r"); + + if (fin == NULL) + { + return 0; /* process vanished since glob() */ + } + + /* read basic process info */ + if (3 != fscanf(fin, "%ld %s %*c %ld", &(P->pid), P->cmd, &(P->ppid))) + { + fclose(fin); + return 0; /* Problem with file format, AFAIK should not happen */ + } + if (fstat(file_no(fin), &stat)) + { + fclose(fin); + return 0; + } + P->uid = stat.st_uid; + + /* fin can't be NULL here */ + fclose(fin); + return 1; +} + + + +/* + * Read command line parameters for given ${PID} + */ +int read_cmd_line(char *file_name_part, char *cmd) +{ + FILE *fin; + char filename[MAX_FILENAME]; + int c; + int k = 0; + + /* try to open file /proc/${PID}/cmdline for reading */ + snprintf(filename, sizeof(filename), "%s%s", file_name_part, "/cmdline"); + fin = fopen(filename, "r"); + + if (fin == NULL) + { + return 0; /* process vanished since glob() */ + } + + /* replace \0 by spaces */ + while (k < MAX_LINE - 1 && EOF != (c = fgetc(fin))) + { + cmd[k++] = c == '\0' ? ' ' : c; + } + if (k > 0) + { + cmd[k] = '\0'; + } + + /* fin can't be NULL here */ + fclose(fin); + return 1; +} + + + +/* + * Fill in an array pointed by P. + */ +int get_processes(void) +{ + glob_t globbuf; + unsigned int i, j; + + glob("/proc/[0-9]*", GLOB_NOSORT, NULL, &globbuf); + + P = calloc(globbuf.gl_pathc, sizeof(struct ProcStruct)); + if (P == NULL) + { + fprintf(stderr, "Problems with malloc, it should not happen...\n"); + exit(1); + } + + for (i = j = 0; i < globbuf.gl_pathc; i++) + { + char * name_part = globbuf.gl_pathv[globbuf.gl_pathc - i - 1]; + if (read_process_info(name_part, &(P[j])) == 0) + { + continue; + } + if (read_cmd_line(name_part, P[j].cmd) == 0) + { + continue; + } + /* Debug output */ + /* printf("uid=%5ld, pid=%5ld, ppid=%5ld, cmd='%s'\n", P[j].uid, P[j].pid, P[j].ppid, P[j].cmd); */ + j++; + } + globfree(&globbuf); + return j; +} + + + +/* + * Try to open X Display + */ +Display * open_display(void) +{ + Display *display = XOpenDisplay(0); + if (display == NULL) + { + puts("Cannot open display"); + exit(EXIT_CODE_ERROR); + } + return display; +} + + + +/* + * Return the atom identifier for the atom name "_NET_WM_PID" + */ +Atom get_atom_pid(Display *display) +{ + Atom atom_pid = XInternAtom(display, "_NET_WM_PID", True); + if (atom_pid == None) + { + printf("No such atom _NET_WM_PID"); + exit(EXIT_CODE_ERROR); + } + return atom_pid; +} + + + +/* + * Try to focus the window and send Ctrl+W to it. + */ +void close_window(Window window, long processId) +{ + char windowIDstr[MAX_LONG_DECIMAL_DIGITS]; + pid_t pid; + + snprintf(windowIDstr, MAX_LONG_DECIMAL_DIGITS, "%ld", window); + char *args[] = + { + "/usr/bin/xdotool", + "windowfocus", + windowIDstr, + "windowactivate", + windowIDstr, + "key", + "--window", + windowIDstr, + "--clearmodifiers", + "Ctrl+W", + (char *) NULL + }; + if ((pid = fork()) == -1) + { + perror("some fork error"); + } + else if (pid == 0) + { + /* child process */ + printf("Trying to close window ID %ld for process ID %ld\n", (long)window, processId); + execv("/usr/bin/xdotool", args); + } + else + { + /* parent process */ + sleep(SLEEP_AMOUNT); + } +} + + + +/* + * Run xkill to kill window with specified window ID + */ +void kill_window(Window window, long processId) +{ + char windowIDstr[MAX_LONG_DECIMAL_DIGITS]; + pid_t pid; + + /* we need to convert window id (long) into a string to call xkill */ + snprintf(windowIDstr, MAX_LONG_DECIMAL_DIGITS, "%ld", window); + char *args[] = + { + "/usr/bin/xkill", + "-id", + windowIDstr, + (char *) NULL + }; + if ((pid = fork()) == -1) + { + perror("some fork error"); + } + else if (pid == 0) + { + printf("Trying to kill window ID %ld for process ID %ld\n", (long)window, processId); + execv("/usr/bin/xkill", args); + } + else + { + // parent + sleep(SLEEP_AMOUNT); + } +} + + + +/* + * Recursivelly search for a window(s) associated with given process ID + */ +void search_and_destroy(Display *display, Window window, Atom atomPID, long processId) +{ + Atom type; + int format; + unsigned long nItems; + unsigned long bytesAfter; + unsigned char *propertyPID = NULL; + + /* read _NET_WM_PID property, if exists */ + if (Success == XGetWindowProperty(display, window, atomPID, 0, 1, False, XA_CARDINAL, + &type, &format, &nItems, &bytesAfter, &propertyPID)) + { + if (propertyPID != NULL) + { + if (processId == *((unsigned long *)propertyPID)) + { + printf("Found window ID %ld for process ID %ld\n", (long)window, processId); + XFree(propertyPID); +#if TRY_TO_CLOSE_WINDOW == 1 + close_window(window, processId); +#endif +#if TRY_TO_KILL_WINDOW == 1 + kill_window(window, processId); +#endif + } + } + } + + /* recurse into child windows */ + Window rootWindow; + Window parentWindow; + Window *childWindows; + unsigned nChildren; + if (0 != XQueryTree(display, window, &rootWindow, &parentWindow, &childWindows, &nChildren)) + { + unsigned int i; + for(i = 0; i < nChildren; i++) + { + search_and_destroy(display, childWindows[i], atomPID, processId); + } + } +} + + + +/* + * Kill process with given ancestor PID. + */ +void kill_process(int pid) +{ + Atom atom_pid = get_atom_pid(display); + printf("Searching for windows associated with PID %d\n", pid); + search_and_destroy(display, root_window, atom_pid, pid); +} + + + +/* + * Kill all processes with given ppid (ancestor PID) + */ +void kill_processes_with_ppid(int ppid) +{ + int i; + int done = 1; + for (i = 0; i < N; i++) + { + if (ppid == P[i].ppid) + { + int pid = P[i].pid; + printf("uid=%5ld, pid=%5ld, ppid=%5ld, cmd='%s'\n", P[i].uid, P[i].pid, P[i].ppid, P[i].cmd); + kill_processes_with_ppid(pid); + /* at least one child process exists */ + done = 0; + kill_process(pid); + } + } + kill_process(ppid); + /* if none child processes have been found we are at bottom of the process tree */ + if (done) + { + return; + } +} + + + +/* TODO: better check for user input */ +int read_pid(int argc, char **argv) +{ + int pid; + + if (argc != 2) + { + puts("Usage softkiller PID"); + exit(EXIT_CODE_ERROR); + } + + pid = atoi(argv[1]); + + if (sscanf(argv[1], "%d", &pid) != 1) + { + printf("Wrong PID entered: %s\n", argv[1]); + exit(EXIT_CODE_ERROR); + } + + /* basic check (nobody should try to kill PID 0 :) */ + if (pid <= 0) + { + printf("Wrong process ID %d\n", pid); + exit(EXIT_CODE_ERROR); + } + + return pid; +} + + + +/* + * Entry point to this tool. + */ +int main(int argc, char **argv) +{ + int pid = read_pid(argc, argv); + + printf("ancestor PID to kill: %d\n", pid); + + N = get_processes(); + printf("N = %d\n", N); + display = open_display(); + root_window = XDefaultRootWindow(display); + atom_pid = get_atom_pid(display); + + kill_processes_with_ppid(pid); + + puts("*** Done! ***"); + return 0; +} + diff --git a/tests/test-extensions/net/sourceforge/jnlp/browsertesting/browsers/Firefox.java b/tests/test-extensions/net/sourceforge/jnlp/browsertesting/browsers/Firefox.java index c80d8ea..f69a5cd 100644 --- a/tests/test-extensions/net/sourceforge/jnlp/browsertesting/browsers/Firefox.java +++ b/tests/test-extensions/net/sourceforge/jnlp/browsertesting/browsers/Firefox.java @@ -78,7 +78,7 @@ public class Firefox extends MozillaFamilyLinuxBrowser { @Override public void beforeKill(String s) { try { - //ProcessAssasin.closeWindows(s); + ProcessAssasin.closeWindows(s); } catch (Exception ex) { throw new RuntimeException(ex); } |