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