source: trunk/icedtea-web/tests/softkiller/softkiller.c@ 433

Last change on this file since 433 was 427, checked in by dmik, 11 years ago

icedtea-web: Import version 1.5.1 from vendor.

File size: 9.0 KB
Line 
1/* X Window app killer
2 *
3 * Author: Pavel Tisnovsky <ptisnovs@redhat.com>
4 *
5 * Compile:
6 * gcc -Wall -pedantic -std=c99 -o softkiller softkiller.c -lX11
7 * (please note that -std=c99 is needed because we use snprintf
8 * function which does not exist in C89/ANSI C)
9 *
10 * Run:
11 * ./softkiller PID
12 */
13
14
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <glob.h>
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <unistd.h>
22#include <X11/Xlib.h>
23#include <X11/Xatom.h>
24
25/*
26 * Number of long decimal digits + 1 (for ASCIIZ storage)
27 */
28#define MAX_LONG_DECIMAL_DIGITS 21
29
30/*
31 * Max line length in /proc/stat files
32 */
33#define MAX_LINE 8192
34
35/*
36 * Max filename length for /proc/... files
37 */
38#define MAX_FILENAME 32
39
40/*
41 * Return values
42 */
43#define EXIT_CODE_OK 0
44#define EXIT_CODE_ERROR 1
45
46/*
47 * Different softkilling strategies
48 */
49#define TRY_TO_CLOSE_WINDOW 1
50#define TRY_TO_KILL_WINDOW 1
51
52/*
53 * Delay between application of different softkilling strategies.
54 */
55#define SLEEP_AMOUNT 2
56
57/*
58 * Not in c89/c99...
59 */
60#define file_no(FP) ((FP)->_fileno)
61
62/*
63 * Basic information about given process
64 */
65typedef struct ProcStruct
66{
67 long uid, pid, ppid;
68 char cmd[MAX_LINE];
69} ProcStruct;
70
71ProcStruct *P = NULL;
72
73int N = 0;
74
75Display *display;
76Window root_window;
77Atom atom_pid;
78
79
80
81/*
82 * Read basic process info from the file /proc/${PID}/stat
83 * where ${PID} is process ID.
84 */
85int read_process_info(char *file_name_part, ProcStruct *P)
86{
87 FILE *fin;
88 char filename[MAX_FILENAME];
89 struct stat stat;
90
91 /* try to open file /proc/${PID}/stat for reading */
92 snprintf(filename, sizeof(filename), "%s%s", file_name_part, "/stat");
93 fin = fopen(filename, "r");
94
95 if (fin == NULL)
96 {
97 return 0; /* process vanished since glob() */
98 }
99
100 /* read basic process info */
101 if (3 != fscanf(fin, "%ld %s %*c %ld", &(P->pid), P->cmd, &(P->ppid)))
102 {
103 fclose(fin);
104 return 0; /* Problem with file format, AFAIK should not happen */
105 }
106 if (fstat(file_no(fin), &stat))
107 {
108 fclose(fin);
109 return 0;
110 }
111 P->uid = stat.st_uid;
112
113 /* fin can't be NULL here */
114 fclose(fin);
115 return 1;
116}
117
118
119
120/*
121 * Read command line parameters for given ${PID}
122 */
123int read_cmd_line(char *file_name_part, char *cmd)
124{
125 FILE *fin;
126 char filename[MAX_FILENAME];
127 int c;
128 int k = 0;
129
130 /* try to open file /proc/${PID}/cmdline for reading */
131 snprintf(filename, sizeof(filename), "%s%s", file_name_part, "/cmdline");
132 fin = fopen(filename, "r");
133
134 if (fin == NULL)
135 {
136 return 0; /* process vanished since glob() */
137 }
138
139 /* replace \0 by spaces */
140 while (k < MAX_LINE - 1 && EOF != (c = fgetc(fin)))
141 {
142 cmd[k++] = c == '\0' ? ' ' : c;
143 }
144 if (k > 0)
145 {
146 cmd[k] = '\0';
147 }
148
149 /* fin can't be NULL here */
150 fclose(fin);
151 return 1;
152}
153
154
155
156/*
157 * Fill in an array pointed by P.
158 */
159int get_processes(void)
160{
161 glob_t globbuf;
162 unsigned int i, j;
163
164 glob("/proc/[0-9]*", GLOB_NOSORT, NULL, &globbuf);
165
166 P = calloc(globbuf.gl_pathc, sizeof(struct ProcStruct));
167 if (P == NULL)
168 {
169 fprintf(stderr, "Problems with malloc, it should not happen...\n");
170 exit(1);
171 }
172
173 for (i = j = 0; i < globbuf.gl_pathc; i++)
174 {
175 char * name_part = globbuf.gl_pathv[globbuf.gl_pathc - i - 1];
176 if (read_process_info(name_part, &(P[j])) == 0)
177 {
178 continue;
179 }
180 if (read_cmd_line(name_part, P[j].cmd) == 0)
181 {
182 continue;
183 }
184 /* Debug output */
185 /* printf("uid=%5ld, pid=%5ld, ppid=%5ld, cmd='%s'\n", P[j].uid, P[j].pid, P[j].ppid, P[j].cmd); */
186 j++;
187 }
188 globfree(&globbuf);
189 return j;
190}
191
192
193
194/*
195 * Try to open X Display
196 */
197Display * open_display(void)
198{
199 Display *display = XOpenDisplay(0);
200 if (display == NULL)
201 {
202 puts("Cannot open display");
203 exit(EXIT_CODE_ERROR);
204 }
205 return display;
206}
207
208
209
210/*
211 * Return the atom identifier for the atom name "_NET_WM_PID"
212 */
213Atom get_atom_pid(Display *display)
214{
215 Atom atom_pid = XInternAtom(display, "_NET_WM_PID", True);
216 if (atom_pid == None)
217 {
218 printf("No such atom _NET_WM_PID");
219 exit(EXIT_CODE_ERROR);
220 }
221 return atom_pid;
222}
223
224
225
226/*
227 * Try to focus the window and send Ctrl+W to it.
228 */
229void close_window(Window window, long processId)
230{
231 char windowIDstr[MAX_LONG_DECIMAL_DIGITS];
232 pid_t pid;
233
234 snprintf(windowIDstr, MAX_LONG_DECIMAL_DIGITS, "%ld", window);
235 char *args[] =
236 {
237 "/usr/bin/xdotool",
238 "windowfocus",
239 windowIDstr,
240 "windowactivate",
241 windowIDstr,
242 "key",
243 "--window",
244 windowIDstr,
245 "--clearmodifiers",
246 "Ctrl+W",
247 (char *) NULL
248 };
249 if ((pid = fork()) == -1)
250 {
251 perror("some fork error");
252 }
253 else if (pid == 0)
254 {
255 /* child process */
256 printf("Trying to close window ID %ld for process ID %ld\n", (long)window, processId);
257 execv("/usr/bin/xdotool", args);
258 }
259 else
260 {
261 /* parent process */
262 sleep(SLEEP_AMOUNT);
263 }
264}
265
266
267
268/*
269 * Run xkill to kill window with specified window ID
270 */
271void kill_window(Window window, long processId)
272{
273 char windowIDstr[MAX_LONG_DECIMAL_DIGITS];
274 pid_t pid;
275
276 /* we need to convert window id (long) into a string to call xkill */
277 snprintf(windowIDstr, MAX_LONG_DECIMAL_DIGITS, "%ld", window);
278 char *args[] =
279 {
280 "/usr/bin/xkill",
281 "-id",
282 windowIDstr,
283 (char *) NULL
284 };
285 if ((pid = fork()) == -1)
286 {
287 perror("some fork error");
288 }
289 else if (pid == 0)
290 {
291 printf("Trying to kill window ID %ld for process ID %ld\n", (long)window, processId);
292 execv("/usr/bin/xkill", args);
293 }
294 else
295 {
296 // parent
297 sleep(SLEEP_AMOUNT);
298 }
299}
300
301
302
303/*
304 * Recursivelly search for a window(s) associated with given process ID
305 */
306void search_and_destroy(Display *display, Window window, Atom atomPID, long processId)
307{
308 Atom type;
309 int format;
310 unsigned long nItems;
311 unsigned long bytesAfter;
312 unsigned char *propertyPID = NULL;
313
314 /* read _NET_WM_PID property, if exists */
315 if (Success == XGetWindowProperty(display, window, atomPID, 0, 1, False, XA_CARDINAL,
316 &type, &format, &nItems, &bytesAfter, &propertyPID))
317 {
318 if (propertyPID != NULL)
319 {
320 if (processId == *((unsigned long *)propertyPID))
321 {
322 printf("Found window ID %ld for process ID %ld\n", (long)window, processId);
323 XFree(propertyPID);
324#if TRY_TO_CLOSE_WINDOW == 1
325 close_window(window, processId);
326#endif
327#if TRY_TO_KILL_WINDOW == 1
328 kill_window(window, processId);
329#endif
330 }
331 }
332 }
333
334 /* recurse into child windows */
335 Window rootWindow;
336 Window parentWindow;
337 Window *childWindows;
338 unsigned nChildren;
339 if (0 != XQueryTree(display, window, &rootWindow, &parentWindow, &childWindows, &nChildren))
340 {
341 unsigned int i;
342 for(i = 0; i < nChildren; i++)
343 {
344 search_and_destroy(display, childWindows[i], atomPID, processId);
345 }
346 }
347}
348
349
350
351/*
352 * Kill process with given ancestor PID.
353 */
354void kill_process(int pid)
355{
356 Atom atom_pid = get_atom_pid(display);
357 printf("Searching for windows associated with PID %d\n", pid);
358 search_and_destroy(display, root_window, atom_pid, pid);
359}
360
361
362
363/*
364 * Kill all processes with given ppid (ancestor PID)
365 */
366void kill_processes_with_ppid(int ppid)
367{
368 int i;
369 int done = 1;
370 for (i = 0; i < N; i++)
371 {
372 if (ppid == P[i].ppid)
373 {
374 int pid = P[i].pid;
375 printf("uid=%5ld, pid=%5ld, ppid=%5ld, cmd='%s'\n", P[i].uid, P[i].pid, P[i].ppid, P[i].cmd);
376 kill_processes_with_ppid(pid);
377 /* at least one child process exists */
378 done = 0;
379 kill_process(pid);
380 }
381 }
382 kill_process(ppid);
383 /* if none child processes have been found we are at bottom of the process tree */
384 if (done)
385 {
386 return;
387 }
388}
389
390
391
392/* TODO: better check for user input */
393int read_pid(int argc, char **argv)
394{
395 int pid;
396
397 if (argc != 2)
398 {
399 puts("Usage softkiller PID");
400 exit(EXIT_CODE_ERROR);
401 }
402
403 pid = atoi(argv[1]);
404
405 if (sscanf(argv[1], "%d", &pid) != 1)
406 {
407 printf("Wrong PID entered: %s\n", argv[1]);
408 exit(EXIT_CODE_ERROR);
409 }
410
411 /* basic check (nobody should try to kill PID 0 :) */
412 if (pid <= 0)
413 {
414 printf("Wrong process ID %d\n", pid);
415 exit(EXIT_CODE_ERROR);
416 }
417
418 return pid;
419}
420
421
422
423/*
424 * Entry point to this tool.
425 */
426int main(int argc, char **argv)
427{
428 int pid = read_pid(argc, argv);
429
430 printf("ancestor PID to kill: %d\n", pid);
431
432 N = get_processes();
433 printf("N = %d\n", N);
434 display = open_display();
435 root_window = XDefaultRootWindow(display);
436 atom_pid = get_atom_pid(display);
437
438 kill_processes_with_ppid(pid);
439
440 puts("*** Done! ***");
441 return 0;
442}
443
Note: See TracBrowser for help on using the repository browser.