1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | /* GTK - The GIMP Toolkit |
3 | * Copyright (C) David Zeuthen <davidz@redhat.com> |
4 | * Copyright (C) 2001 Havoc Pennington |
5 | * Copyright (C) 2005-2007 Vincent Untz |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2 of the License, or (at your option) any later version. |
11 | * |
12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | /* |
22 | * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS |
23 | * file for a list of people on the GTK+ Team. See the ChangeLog |
24 | * files for a list of changes. These files are distributed with |
25 | * GTK+ at ftp://ftp.gtk.org/pub/gtk/. |
26 | */ |
27 | |
28 | #include "config.h" |
29 | |
30 | #include <string.h> |
31 | #include <stdlib.h> |
32 | #include <gio/gio.h> |
33 | #include "x11/gdkx.h" |
34 | #include <X11/Xatom.h> |
35 | #include <gtk/gtkicontheme.h> |
36 | #include "gtkintl.h" |
37 | |
38 | /* for the kill(2) system call and errno - POSIX.1-2001 and later */ |
39 | #include <sys/types.h> |
40 | #include <signal.h> |
41 | #include <errno.h> |
42 | |
43 | #if defined(__OpenBSD__) |
44 | #include <sys/sysctl.h> |
45 | #endif |
46 | |
47 | #include "gtkmountoperationprivate.h" |
48 | |
49 | /* ---------------------------------------------------------------------------------------------------- */ |
50 | /* these functions are based on code from libwnck (LGPLv2) */ |
51 | |
52 | static gboolean get_window_list (GdkDisplay *display, |
53 | Display *xdisplay, |
54 | Window xwindow, |
55 | Atom atom, |
56 | Window **windows, |
57 | int *len); |
58 | |
59 | static char* get_utf8_property (GdkDisplay *display, |
60 | Display *xdisplay, |
61 | Window xwindow, |
62 | Atom atom); |
63 | |
64 | static gboolean get_cardinal (GdkDisplay *display, |
65 | Display *xdisplay, |
66 | Window xwindow, |
67 | Atom atom, |
68 | int *val); |
69 | |
70 | static gboolean read_rgb_icon (GdkDisplay *display, |
71 | Display *xdisplay, |
72 | Window xwindow, |
73 | int ideal_width, |
74 | int ideal_height, |
75 | int *width, |
76 | int *height, |
77 | guchar **pixdata); |
78 | |
79 | |
80 | static gboolean |
81 | get_cardinal (GdkDisplay *display, |
82 | Display *xdisplay, |
83 | Window xwindow, |
84 | Atom atom, |
85 | int *val) |
86 | { |
87 | Atom type; |
88 | int format; |
89 | gulong nitems; |
90 | gulong bytes_after; |
91 | gulong *num; |
92 | int err, result; |
93 | |
94 | *val = 0; |
95 | |
96 | gdk_x11_display_error_trap_push (display); |
97 | type = None; |
98 | result = XGetWindowProperty (xdisplay, |
99 | xwindow, |
100 | atom, |
101 | 0, G_MAXLONG, |
102 | False, XA_CARDINAL, &type, &format, &nitems, |
103 | &bytes_after, (void*)&num); |
104 | XSync (xdisplay, False); |
105 | err = gdk_x11_display_error_trap_pop (display); |
106 | |
107 | if (err != Success || |
108 | result != Success) |
109 | return FALSE; |
110 | |
111 | if (type != XA_CARDINAL) |
112 | { |
113 | XFree (num); |
114 | return FALSE; |
115 | } |
116 | |
117 | *val = *num; |
118 | |
119 | XFree (num); |
120 | |
121 | return TRUE; |
122 | } |
123 | |
124 | static char* |
125 | get_utf8_property (GdkDisplay *display, |
126 | Display *xdisplay, |
127 | Window xwindow, |
128 | Atom atom) |
129 | { |
130 | Atom type; |
131 | int format; |
132 | gulong nitems; |
133 | gulong bytes_after; |
134 | char *val; |
135 | int err, result; |
136 | char *retval; |
137 | Atom utf8_string; |
138 | |
139 | utf8_string = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "UTF8_STRING" ); |
140 | |
141 | gdk_x11_display_error_trap_push (display); |
142 | type = None; |
143 | val = NULL; |
144 | result = XGetWindowProperty (xdisplay, |
145 | xwindow, |
146 | atom, |
147 | 0, G_MAXLONG, |
148 | False, utf8_string, |
149 | &type, &format, &nitems, |
150 | &bytes_after, (guchar **)&val); |
151 | XSync (xdisplay, False); |
152 | err = gdk_x11_display_error_trap_pop (display); |
153 | |
154 | if (err != Success || |
155 | result != Success) |
156 | return NULL; |
157 | |
158 | if (type != utf8_string || |
159 | format != 8 || |
160 | nitems == 0) |
161 | { |
162 | if (val) |
163 | XFree (val); |
164 | return NULL; |
165 | } |
166 | |
167 | if (!g_utf8_validate (str: val, max_len: nitems, NULL)) |
168 | { |
169 | g_warning ("Property %s contained invalid UTF-8" , |
170 | gdk_x11_get_xatom_name_for_display (display, atom)); |
171 | XFree (val); |
172 | return NULL; |
173 | } |
174 | |
175 | retval = g_strndup (str: val, n: nitems); |
176 | |
177 | XFree (val); |
178 | |
179 | return retval; |
180 | } |
181 | |
182 | static gboolean |
183 | find_largest_sizes (gulong *data, |
184 | gulong nitems, |
185 | int *width, |
186 | int *height) |
187 | { |
188 | *width = 0; |
189 | *height = 0; |
190 | |
191 | while (nitems > 0) |
192 | { |
193 | int w, h; |
194 | |
195 | if (nitems < 3) |
196 | return FALSE; /* no space for w, h */ |
197 | |
198 | w = data[0]; |
199 | h = data[1]; |
200 | |
201 | if (nitems < ((w * h) + 2)) |
202 | return FALSE; /* not enough data */ |
203 | |
204 | *width = MAX (w, *width); |
205 | *height = MAX (h, *height); |
206 | |
207 | data += (w * h) + 2; |
208 | nitems -= (w * h) + 2; |
209 | } |
210 | |
211 | return TRUE; |
212 | } |
213 | |
214 | static gboolean |
215 | find_best_size (gulong *data, |
216 | gulong nitems, |
217 | int ideal_width, |
218 | int ideal_height, |
219 | int *width, |
220 | int *height, |
221 | gulong **start) |
222 | { |
223 | int best_w; |
224 | int best_h; |
225 | gulong *best_start; |
226 | int max_width, max_height; |
227 | |
228 | *width = 0; |
229 | *height = 0; |
230 | *start = NULL; |
231 | |
232 | if (!find_largest_sizes (data, nitems, width: &max_width, height: &max_height)) |
233 | return FALSE; |
234 | |
235 | if (ideal_width < 0) |
236 | ideal_width = max_width; |
237 | if (ideal_height < 0) |
238 | ideal_height = max_height; |
239 | |
240 | best_w = 0; |
241 | best_h = 0; |
242 | best_start = NULL; |
243 | |
244 | while (nitems > 0) |
245 | { |
246 | int w, h; |
247 | gboolean replace; |
248 | |
249 | replace = FALSE; |
250 | |
251 | if (nitems < 3) |
252 | return FALSE; /* no space for w, h */ |
253 | |
254 | w = data[0]; |
255 | h = data[1]; |
256 | |
257 | if (nitems < ((w * h) + 2)) |
258 | break; /* not enough data */ |
259 | |
260 | if (best_start == NULL) |
261 | { |
262 | replace = TRUE; |
263 | } |
264 | else |
265 | { |
266 | /* work with averages */ |
267 | const int ideal_size = (ideal_width + ideal_height) / 2; |
268 | int best_size = (best_w + best_h) / 2; |
269 | int this_size = (w + h) / 2; |
270 | |
271 | /* larger than desired is always better than smaller */ |
272 | if (best_size < ideal_size && |
273 | this_size >= ideal_size) |
274 | replace = TRUE; |
275 | /* if we have too small, pick anything bigger */ |
276 | else if (best_size < ideal_size && |
277 | this_size > best_size) |
278 | replace = TRUE; |
279 | /* if we have too large, pick anything smaller |
280 | * but still >= the ideal |
281 | */ |
282 | else if (best_size > ideal_size && |
283 | this_size >= ideal_size && |
284 | this_size < best_size) |
285 | replace = TRUE; |
286 | } |
287 | |
288 | if (replace) |
289 | { |
290 | best_start = data + 2; |
291 | best_w = w; |
292 | best_h = h; |
293 | } |
294 | |
295 | data += (w * h) + 2; |
296 | nitems -= (w * h) + 2; |
297 | } |
298 | |
299 | if (best_start) |
300 | { |
301 | *start = best_start; |
302 | *width = best_w; |
303 | *height = best_h; |
304 | return TRUE; |
305 | } |
306 | else |
307 | return FALSE; |
308 | } |
309 | |
310 | static void |
311 | argbdata_to_pixdata (gulong *argb_data, |
312 | int len, |
313 | guchar **pixdata) |
314 | { |
315 | guchar *p; |
316 | int i; |
317 | |
318 | *pixdata = g_new (guchar, len * 4); |
319 | p = *pixdata; |
320 | |
321 | /* One could speed this up a lot. */ |
322 | i = 0; |
323 | while (i < len) |
324 | { |
325 | guint argb; |
326 | guint rgba; |
327 | |
328 | argb = argb_data[i]; |
329 | rgba = (argb << 8) | (argb >> 24); |
330 | |
331 | *p = rgba >> 24; |
332 | ++p; |
333 | *p = (rgba >> 16) & 0xff; |
334 | ++p; |
335 | *p = (rgba >> 8) & 0xff; |
336 | ++p; |
337 | *p = rgba & 0xff; |
338 | ++p; |
339 | |
340 | ++i; |
341 | } |
342 | } |
343 | |
344 | static gboolean |
345 | read_rgb_icon (GdkDisplay *display, |
346 | Display *xdisplay, |
347 | Window xwindow, |
348 | int ideal_width, |
349 | int ideal_height, |
350 | int *width, |
351 | int *height, |
352 | guchar **pixdata) |
353 | { |
354 | Atom type; |
355 | int format; |
356 | gulong nitems; |
357 | gulong bytes_after; |
358 | int result, err; |
359 | gulong *data; |
360 | gulong *best; |
361 | int w, h; |
362 | |
363 | gdk_x11_display_error_trap_push (display); |
364 | type = None; |
365 | data = NULL; |
366 | result = XGetWindowProperty (xdisplay, |
367 | xwindow, |
368 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_WM_ICON" ), |
369 | 0, G_MAXLONG, |
370 | False, XA_CARDINAL, &type, &format, &nitems, |
371 | &bytes_after, (void*)&data); |
372 | |
373 | XSync (xdisplay, False); |
374 | err = gdk_x11_display_error_trap_pop (display); |
375 | |
376 | if (err != Success || |
377 | result != Success) |
378 | return FALSE; |
379 | |
380 | if (type != XA_CARDINAL) |
381 | { |
382 | XFree (data); |
383 | return FALSE; |
384 | } |
385 | |
386 | if (!find_best_size (data, nitems, |
387 | ideal_width, ideal_height, |
388 | width: &w, height: &h, start: &best)) |
389 | { |
390 | XFree (data); |
391 | return FALSE; |
392 | } |
393 | |
394 | *width = w; |
395 | *height = h; |
396 | |
397 | argbdata_to_pixdata (argb_data: best, len: w * h, pixdata); |
398 | |
399 | XFree (data); |
400 | |
401 | return TRUE; |
402 | } |
403 | |
404 | static void |
405 | free_pixels (guchar *pixels, gpointer data) |
406 | { |
407 | g_free (mem: pixels); |
408 | } |
409 | |
410 | static GdkTexture * |
411 | scaled_from_pixdata (guchar *pixdata, |
412 | int w, |
413 | int h, |
414 | int new_w, |
415 | int new_h) |
416 | { |
417 | GdkPixbuf *src; |
418 | GdkPixbuf *dest; |
419 | GdkTexture *ret; |
420 | |
421 | src = gdk_pixbuf_new_from_data (data: pixdata, |
422 | colorspace: GDK_COLORSPACE_RGB, |
423 | TRUE, |
424 | bits_per_sample: 8, |
425 | width: w, height: h, rowstride: w * 4, |
426 | destroy_fn: free_pixels, |
427 | NULL); |
428 | |
429 | if (src == NULL) |
430 | return NULL; |
431 | |
432 | if (w != h) |
433 | { |
434 | GdkPixbuf *tmp; |
435 | int size; |
436 | |
437 | size = MAX (w, h); |
438 | |
439 | tmp = gdk_pixbuf_new (colorspace: GDK_COLORSPACE_RGB, TRUE, bits_per_sample: 8, width: size, height: size); |
440 | |
441 | if (tmp != NULL) |
442 | { |
443 | gdk_pixbuf_fill (pixbuf: tmp, pixel: 0); |
444 | gdk_pixbuf_copy_area (src_pixbuf: src, src_x: 0, src_y: 0, width: w, height: h, |
445 | dest_pixbuf: tmp, |
446 | dest_x: (size - w) / 2, dest_y: (size - h) / 2); |
447 | |
448 | g_object_unref (object: src); |
449 | src = tmp; |
450 | } |
451 | } |
452 | |
453 | if (w != new_w || h != new_h) |
454 | { |
455 | dest = gdk_pixbuf_scale_simple (src, dest_width: new_w, dest_height: new_h, interp_type: GDK_INTERP_BILINEAR); |
456 | |
457 | g_object_unref (G_OBJECT (src)); |
458 | } |
459 | else |
460 | { |
461 | dest = src; |
462 | } |
463 | |
464 | ret = gdk_texture_new_for_pixbuf (pixbuf: dest); |
465 | |
466 | g_object_unref (object: dest); |
467 | |
468 | return ret; |
469 | } |
470 | |
471 | static gboolean |
472 | get_window_list (GdkDisplay *display, |
473 | Display *xdisplay, |
474 | Window xwindow, |
475 | Atom atom, |
476 | Window **windows, |
477 | int *len) |
478 | { |
479 | Atom type; |
480 | int format; |
481 | gulong nitems; |
482 | gulong bytes_after; |
483 | Window *data; |
484 | int err, result; |
485 | |
486 | *windows = NULL; |
487 | *len = 0; |
488 | |
489 | gdk_x11_display_error_trap_push (display); |
490 | type = None; |
491 | result = XGetWindowProperty (xdisplay, |
492 | xwindow, |
493 | atom, |
494 | 0, G_MAXLONG, |
495 | False, XA_WINDOW, &type, &format, &nitems, |
496 | &bytes_after, (void*)&data); |
497 | XSync (xdisplay, False); |
498 | err = gdk_x11_display_error_trap_pop (display); |
499 | |
500 | if (err != Success || |
501 | result != Success) |
502 | return FALSE; |
503 | |
504 | if (type != XA_WINDOW) |
505 | { |
506 | XFree (data); |
507 | return FALSE; |
508 | } |
509 | |
510 | *windows = g_new (Window, nitems); |
511 | memcpy (dest: *windows, src: data, n: sizeof (Window) * nitems); |
512 | *len = nitems; |
513 | |
514 | XFree (data); |
515 | |
516 | return TRUE; |
517 | } |
518 | |
519 | |
520 | /* ---------------------------------------------------------------------------------------------------- */ |
521 | |
522 | struct _GtkMountOperationLookupContext |
523 | { |
524 | /* Hash from pid (int) -> XID (int) |
525 | * |
526 | * Note that XIDs are at most 27 bits - however, also note that sizeof(XID) == 8 on |
527 | * x86_64 - that's just xlib brokenness. So it's safe to stuff the XID into a pointer. |
528 | */ |
529 | GHashTable *pid_to_window; |
530 | GdkDisplay *display; |
531 | }; |
532 | |
533 | GtkMountOperationLookupContext * |
534 | _gtk_mount_operation_lookup_context_get (GdkDisplay *display) |
535 | { |
536 | GtkMountOperationLookupContext *context; |
537 | Window *mapping; |
538 | int mapping_length; |
539 | int n; |
540 | |
541 | context = g_new0 (GtkMountOperationLookupContext, 1); |
542 | |
543 | context->pid_to_window = g_hash_table_new (hash_func: g_direct_hash, key_equal_func: g_direct_equal); |
544 | context->display = display; |
545 | |
546 | mapping = NULL; |
547 | mapping_length = 0; |
548 | get_window_list (display: context->display, |
549 | xdisplay: gdk_x11_display_get_xdisplay (display: context->display), |
550 | xwindow: gdk_x11_display_get_xrootwindow (display: context->display), |
551 | atom: gdk_x11_get_xatom_by_name_for_display (display: context->display, |
552 | atom_name: "_NET_CLIENT_LIST" ), |
553 | windows: &mapping, |
554 | len: &mapping_length); |
555 | for (n = 0; n < mapping_length; n++) |
556 | { |
557 | int pid; |
558 | |
559 | if (!get_cardinal (display: context->display, |
560 | GDK_DISPLAY_XDISPLAY (context->display), |
561 | xwindow: mapping[n], |
562 | atom: gdk_x11_get_xatom_by_name_for_display (display: context->display, |
563 | atom_name: "_NET_WM_PID" ), |
564 | val: &pid)) |
565 | continue; |
566 | |
567 | g_hash_table_insert (hash_table: context->pid_to_window, |
568 | GINT_TO_POINTER (pid), |
569 | GINT_TO_POINTER ((int) mapping[n])); |
570 | } |
571 | g_free (mem: mapping); |
572 | |
573 | return context; |
574 | } |
575 | |
576 | void |
577 | _gtk_mount_operation_lookup_context_free (GtkMountOperationLookupContext *context) |
578 | { |
579 | g_hash_table_unref (hash_table: context->pid_to_window); |
580 | g_free (mem: context); |
581 | } |
582 | |
583 | /* ---------------------------------------------------------------------------------------------------- */ |
584 | |
585 | #ifdef __linux__ |
586 | |
587 | static GPid |
588 | pid_get_parent (GPid pid) |
589 | { |
590 | GPid ppid; |
591 | char **tokens; |
592 | char *stat_filename; |
593 | char *stat_contents; |
594 | gsize stat_len; |
595 | |
596 | ppid = 0; |
597 | tokens = NULL; |
598 | stat_contents = NULL; |
599 | stat_filename = NULL; |
600 | |
601 | /* fail if trying to get the parent of the init process (no such thing) */ |
602 | if (pid == 1) |
603 | goto out; |
604 | |
605 | stat_filename = g_strdup_printf (format: "/proc/%d/status" , pid); |
606 | if (g_file_get_contents (filename: stat_filename, |
607 | contents: &stat_contents, |
608 | length: &stat_len, |
609 | NULL)) |
610 | { |
611 | guint n; |
612 | |
613 | tokens = g_strsplit (string: stat_contents, delimiter: "\n" , max_tokens: 0); |
614 | |
615 | for (n = 0; tokens[n] != NULL; n++) |
616 | { |
617 | if (g_str_has_prefix (str: tokens[n], prefix: "PPid:" )) |
618 | { |
619 | char *endp; |
620 | |
621 | endp = NULL; |
622 | ppid = strtoll (nptr: tokens[n] + sizeof "PPid:" - 1, endptr: &endp, base: 10); |
623 | if (endp == NULL || *endp != '\0') |
624 | { |
625 | g_warning ("Error parsing contents of `%s'. Parent pid is malformed." , |
626 | stat_filename); |
627 | ppid = 0; |
628 | goto out; |
629 | } |
630 | |
631 | break; |
632 | } |
633 | } |
634 | } |
635 | |
636 | out: |
637 | g_strfreev (str_array: tokens); |
638 | g_free (mem: stat_contents); |
639 | g_free (mem: stat_filename); |
640 | |
641 | return ppid; |
642 | } |
643 | |
644 | static char * |
645 | pid_get_env (GPid pid, |
646 | const char *key) |
647 | { |
648 | char *ret; |
649 | char *env_filename; |
650 | char *env; |
651 | gsize env_len; |
652 | gsize key_len; |
653 | char *end; |
654 | |
655 | ret = NULL; |
656 | |
657 | key_len = strlen (s: key); |
658 | |
659 | env_filename = g_strdup_printf (format: "/proc/%d/environ" , pid); |
660 | if (g_file_get_contents (filename: env_filename, |
661 | contents: &env, |
662 | length: &env_len, |
663 | NULL)) |
664 | { |
665 | guint n; |
666 | |
667 | /* /proc/<pid>/environ in Linux is split at '\0' points, g_strsplit() can't handle that... */ |
668 | n = 0; |
669 | while (TRUE) |
670 | { |
671 | if (n >= env_len || env[n] == '\0') |
672 | break; |
673 | |
674 | if (g_str_has_prefix (str: env + n, prefix: key) && (*(env + n + key_len) == '=')) |
675 | { |
676 | ret = g_strdup (str: env + n + key_len + 1); |
677 | |
678 | /* skip invalid UTF-8 */ |
679 | if (!g_utf8_validate (str: ret, max_len: -1, end: (const char **) &end)) |
680 | *end = '\0'; |
681 | break; |
682 | } |
683 | |
684 | for (; n < env_len && env[n] != '\0'; n++) |
685 | ; |
686 | n++; |
687 | } |
688 | g_free (mem: env); |
689 | } |
690 | g_free (mem: env_filename); |
691 | |
692 | return ret; |
693 | } |
694 | |
695 | static char * |
696 | pid_get_command_line (GPid pid) |
697 | { |
698 | char *cmdline_filename; |
699 | char *cmdline_contents; |
700 | gsize cmdline_len; |
701 | guint n; |
702 | char *end; |
703 | |
704 | cmdline_contents = NULL; |
705 | |
706 | cmdline_filename = g_strdup_printf (format: "/proc/%d/cmdline" , pid); |
707 | if (!g_file_get_contents (filename: cmdline_filename, |
708 | contents: &cmdline_contents, |
709 | length: &cmdline_len, |
710 | NULL)) |
711 | goto out; |
712 | |
713 | /* /proc/<pid>/cmdline separates args by NUL-bytes - replace with spaces */ |
714 | for (n = 0; n < cmdline_len - 1; n++) |
715 | { |
716 | if (cmdline_contents[n] == '\0') |
717 | cmdline_contents[n] = ' '; |
718 | } |
719 | |
720 | /* skip invalid UTF-8 */ |
721 | if (!g_utf8_validate (str: cmdline_contents, max_len: -1, end: (const char **) &end)) |
722 | *end = '\0'; |
723 | |
724 | out: |
725 | g_free (mem: cmdline_filename); |
726 | |
727 | return cmdline_contents; |
728 | } |
729 | |
730 | /* ---------------------------------------------------------------------------------------------------- */ |
731 | |
732 | #elif defined(__OpenBSD__) |
733 | |
734 | /* ---------------------------------------------------------------------------------------------------- */ |
735 | |
736 | static GPid |
737 | pid_get_parent (GPid pid) |
738 | { |
739 | struct kinfo_proc *kp = NULL; |
740 | size_t len; |
741 | GPid ppid = 0; |
742 | |
743 | /* fail if trying to get the parent of the init process (no such thing) */ |
744 | if (pid == 1) |
745 | goto out; |
746 | |
747 | int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, |
748 | sizeof(struct kinfo_proc), 0 }; |
749 | |
750 | if (sysctl (mib, G_N_ELEMENTS (mib), NULL, &len, NULL, 0) == -1) |
751 | goto out; |
752 | |
753 | mib[5] = (len / sizeof(struct kinfo_proc)); |
754 | |
755 | kp = g_malloc0 (len); |
756 | |
757 | if (sysctl (mib, G_N_ELEMENTS (mib), kp, &len, NULL, 0) < 0) |
758 | goto out; |
759 | |
760 | ppid = kp->p_ppid; |
761 | |
762 | out: |
763 | if (kp) |
764 | g_free (kp); |
765 | return ppid; |
766 | } |
767 | |
768 | static char * |
769 | pid_get_env (GPid pid, const char *key) |
770 | { |
771 | size_t len; |
772 | char **strs; |
773 | char *ret = NULL; |
774 | char *end; |
775 | int key_len; |
776 | int i; |
777 | |
778 | int mib[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ENV }; |
779 | |
780 | if (sysctl (mib, G_N_ELEMENTS (mib), NULL, &len, NULL, 0) == -1) |
781 | return ret; |
782 | |
783 | strs = g_malloc0 (len); |
784 | |
785 | key_len = strlen (key); |
786 | |
787 | if (sysctl (mib, G_N_ELEMENTS (mib), strs, &len, NULL, 0) != -1) |
788 | { |
789 | for (i = 0; strs[i] != NULL; i++) |
790 | { |
791 | if (g_str_has_prefix (strs[i], key) && (*(strs[i] + key_len) == '=')) |
792 | { |
793 | ret = g_strdup (strs[i] + key_len + 1); |
794 | |
795 | /* skip invalid UTF-8 */ |
796 | if (!g_utf8_validate (ret, -1, (const char **) &end)) |
797 | *end = '\0'; |
798 | break; |
799 | } |
800 | } |
801 | } |
802 | |
803 | g_free (strs); |
804 | return ret; |
805 | } |
806 | |
807 | static char * |
808 | pid_get_command_line (GPid pid) |
809 | { |
810 | size_t len; |
811 | char **strs; |
812 | char *ret, *end; |
813 | |
814 | int mib[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV }; |
815 | |
816 | if (sysctl (mib, G_N_ELEMENTS (mib), NULL, &len, NULL, 0) == -1) |
817 | return NULL; |
818 | |
819 | strs = g_malloc0 (len); |
820 | |
821 | if (sysctl (mib, G_N_ELEMENTS (mib), strs, &len, NULL, 0) == -1) { |
822 | g_free (strs); |
823 | return NULL; |
824 | } |
825 | |
826 | ret = g_strjoinv (" " , strs); |
827 | /* skip invalid UTF-8 */ |
828 | if (!g_utf8_validate (ret, -1, (const char **) &end)) |
829 | *end = '\0'; |
830 | |
831 | g_free (strs); |
832 | return ret; |
833 | } |
834 | |
835 | #else |
836 | |
837 | /* TODO: please implement for your OS - must return valid UTF-8 */ |
838 | |
839 | static GPid |
840 | pid_get_parent (GPid pid) |
841 | { |
842 | return 0; |
843 | } |
844 | |
845 | static char * |
846 | pid_get_env (GPid pid, |
847 | const char *key) |
848 | { |
849 | return NULL; |
850 | } |
851 | |
852 | static char * |
853 | pid_get_command_line (GPid pid) |
854 | { |
855 | return NULL; |
856 | } |
857 | |
858 | #endif |
859 | |
860 | /* ---------------------------------------------------------------------------------------------------- */ |
861 | |
862 | static char * |
863 | get_name_for_window_with_pid (GtkMountOperationLookupContext *context, |
864 | GPid pid) |
865 | { |
866 | Window window; |
867 | Window windowid_window; |
868 | char *ret; |
869 | |
870 | ret = NULL; |
871 | |
872 | window = GPOINTER_TO_INT (g_hash_table_lookup (context->pid_to_window, GINT_TO_POINTER (pid))); |
873 | if (window == None) |
874 | { |
875 | char *windowid_value; |
876 | |
877 | /* check for $WINDOWID (set by terminals) and see if we can get the title that way */ |
878 | windowid_value = pid_get_env (pid, key: "WINDOWID" ); |
879 | if (windowid_value != NULL) |
880 | { |
881 | char *endp; |
882 | |
883 | endp = NULL; |
884 | windowid_window = (Window) g_ascii_strtoll (nptr: windowid_value, endptr: &endp, base: 10); |
885 | if (endp != NULL && *endp == '\0') |
886 | { |
887 | window = windowid_window; |
888 | } |
889 | g_free (mem: windowid_value); |
890 | } |
891 | |
892 | /* otherwise, check for parents */ |
893 | if (window == None) |
894 | { |
895 | do |
896 | { |
897 | pid = pid_get_parent (pid); |
898 | if (pid == 0) |
899 | break; |
900 | |
901 | window = GPOINTER_TO_INT (g_hash_table_lookup (context->pid_to_window, GINT_TO_POINTER (pid))); |
902 | if (window != None) |
903 | break; |
904 | } |
905 | while (TRUE); |
906 | } |
907 | } |
908 | |
909 | if (window != None) |
910 | { |
911 | ret = get_utf8_property (display: context->display, |
912 | GDK_DISPLAY_XDISPLAY (context->display), |
913 | xwindow: window, |
914 | atom: gdk_x11_get_xatom_by_name_for_display (display: context->display, |
915 | atom_name: "_NET_WM_NAME" )); |
916 | if (ret == NULL) |
917 | ret = get_utf8_property (display: context->display, |
918 | GDK_DISPLAY_XDISPLAY (context->display), |
919 | xwindow: window, atom: gdk_x11_get_xatom_by_name_for_display (display: context->display, |
920 | atom_name: "_NET_WM_ICON_NAME" )); |
921 | } |
922 | |
923 | return ret; |
924 | } |
925 | |
926 | /* ---------------------------------------------------------------------------------------------------- */ |
927 | |
928 | static GdkTexture * |
929 | get_texture_for_window_with_pid (GtkMountOperationLookupContext *context, |
930 | GPid pid, |
931 | int size_pixels) |
932 | { |
933 | Window window; |
934 | GdkTexture *ret; |
935 | |
936 | ret = NULL; |
937 | |
938 | window = GPOINTER_TO_INT (g_hash_table_lookup (context->pid_to_window, GINT_TO_POINTER (pid))); |
939 | if (window == None) |
940 | { |
941 | /* otherwise, check for parents */ |
942 | do |
943 | { |
944 | pid = pid_get_parent (pid); |
945 | if (pid == 0) |
946 | break; |
947 | |
948 | window = GPOINTER_TO_INT (g_hash_table_lookup (context->pid_to_window, GINT_TO_POINTER (pid))); |
949 | if (window != None) |
950 | break; |
951 | } |
952 | while (TRUE); |
953 | } |
954 | |
955 | if (window != None) |
956 | { |
957 | int width; |
958 | int height; |
959 | guchar *pixdata; |
960 | |
961 | if (read_rgb_icon (display: context->display, |
962 | GDK_DISPLAY_XDISPLAY (context->display), |
963 | xwindow: window, |
964 | ideal_width: size_pixels, ideal_height: size_pixels, |
965 | width: &width, height: &height, |
966 | pixdata: &pixdata)) |
967 | { |
968 | /* steals pixdata */ |
969 | |
970 | ret = scaled_from_pixdata (pixdata, |
971 | w: width, h: height, |
972 | new_w: size_pixels, new_h: size_pixels); |
973 | } |
974 | } |
975 | |
976 | return ret; |
977 | } |
978 | |
979 | /* ---------------------------------------------------------------------------------------------------- */ |
980 | |
981 | static const char *well_known_commands[] = |
982 | { |
983 | /* translators: this string is a name for the 'less' command */ |
984 | "less" , N_("Terminal Pager" ), |
985 | "top" , N_("Top Command" ), |
986 | "bash" , N_("Bourne Again Shell" ), |
987 | "sh" , N_("Bourne Shell" ), |
988 | "zsh" , N_("Z Shell" ), |
989 | NULL, |
990 | }; |
991 | |
992 | gboolean |
993 | _gtk_mount_operation_lookup_info (GtkMountOperationLookupContext *context, |
994 | GPid pid, |
995 | int size_pixels, |
996 | char **out_name, |
997 | char **out_command_line, |
998 | GdkTexture **out_texture) |
999 | { |
1000 | g_return_val_if_fail (out_name != NULL && *out_name == NULL, FALSE); |
1001 | g_return_val_if_fail (out_command_line != NULL && *out_command_line == NULL, FALSE); |
1002 | g_return_val_if_fail (out_texture != NULL && *out_texture == NULL, FALSE); |
1003 | |
1004 | /* We perform two different lookups for name and icon size.. this is |
1005 | * because we want the name from the window with WINDOWID and this |
1006 | * normally does not give you an icon |
1007 | * |
1008 | * (the canonical example is a tab in gnome-terminal - the shell/command running |
1009 | * in the shell will have WINDOWID set - but this window won't have an icon - so |
1010 | * we want to continue up until the gnome-terminal window so we can get that icon) |
1011 | */ |
1012 | |
1013 | *out_command_line = pid_get_command_line (pid); |
1014 | |
1015 | *out_name = get_name_for_window_with_pid (context, pid); |
1016 | |
1017 | *out_texture = get_texture_for_window_with_pid (context, pid, size_pixels); |
1018 | |
1019 | /* if we didn't manage to find the name via X, fall back to the basename |
1020 | * of the first element of the command line and, for maximum geek-comfort, |
1021 | * map a few well-known commands to proper translated names |
1022 | */ |
1023 | if (*out_name == NULL && *out_command_line != NULL && |
1024 | strlen (s: *out_command_line) > 0 && (*out_command_line)[0] != ' ') |
1025 | { |
1026 | guint n; |
1027 | char *s; |
1028 | char *p; |
1029 | |
1030 | /* find the first character after the first argument */ |
1031 | s = strchr (s: *out_command_line, c: ' '); |
1032 | if (s == NULL) |
1033 | s = *out_command_line + strlen (s: *out_command_line); |
1034 | |
1035 | for (p = s; p > *out_command_line; p--) |
1036 | { |
1037 | if (*p == '/') |
1038 | { |
1039 | p++; |
1040 | break; |
1041 | } |
1042 | } |
1043 | |
1044 | *out_name = g_strndup (str: p, n: s - p); |
1045 | |
1046 | for (n = 0; well_known_commands[n] != NULL; n += 2) |
1047 | { |
1048 | /* sometimes the command is prefixed with a -, e.g. '-bash' instead |
1049 | * of 'bash' - handle that as well |
1050 | */ |
1051 | if ((strcmp (s1: well_known_commands[n], s2: *out_name) == 0) || |
1052 | ((*out_name)[0] == '-' && (strcmp (s1: well_known_commands[n], s2: (*out_name) + 1) == 0))) |
1053 | { |
1054 | g_free (mem: *out_name); |
1055 | *out_name = g_strdup (_(well_known_commands[n+1])); |
1056 | break; |
1057 | } |
1058 | } |
1059 | } |
1060 | |
1061 | return TRUE; |
1062 | } |
1063 | |
1064 | gboolean |
1065 | _gtk_mount_operation_kill_process (GPid pid, |
1066 | GError **error) |
1067 | { |
1068 | gboolean ret; |
1069 | |
1070 | ret = TRUE; |
1071 | |
1072 | if (kill (pid: (pid_t) pid, SIGTERM) != 0) |
1073 | { |
1074 | int errsv = errno; |
1075 | |
1076 | /* TODO: On EPERM, we could use a setuid helper using polkit (very easy to implement |
1077 | * via pkexec(1)) to allow the user to e.g. authenticate to gain the authorization |
1078 | * to kill the process. But that's not how things currently work. |
1079 | */ |
1080 | |
1081 | ret = FALSE; |
1082 | g_set_error (err: error, |
1083 | G_IO_ERROR, |
1084 | code: g_io_error_from_errno (err_no: errsv), |
1085 | _("Cannot end process with PID %d: %s" ), |
1086 | pid, |
1087 | g_strerror (errnum: errsv)); |
1088 | } |
1089 | |
1090 | return ret; |
1091 | } |
1092 | |