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
get_cardinal(GdkDisplay * display,Display * xdisplay,Window xwindow,Atom atom,int * val)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*
get_utf8_property(GdkDisplay * display,Display * xdisplay,Window xwindow,Atom atom)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, "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 (val, 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 (val, nitems);
176
177 XFree (val);
178
179 return retval;
180 }
181
182 static gboolean
find_largest_sizes(gulong * data,gulong nitems,int * width,int * height)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
find_best_size(gulong * data,gulong nitems,int ideal_width,int ideal_height,int * width,int * height,gulong ** start)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, &max_width, &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
argbdata_to_pixdata(gulong * argb_data,int len,guchar ** pixdata)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
read_rgb_icon(GdkDisplay * display,Display * xdisplay,Window xwindow,int ideal_width,int ideal_height,int * width,int * height,guchar ** pixdata)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, "_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 &w, &h, &best))
389 {
390 XFree (data);
391 return FALSE;
392 }
393
394 *width = w;
395 *height = h;
396
397 argbdata_to_pixdata (best, w * h, pixdata);
398
399 XFree (data);
400
401 return TRUE;
402 }
403
404 static void
free_pixels(guchar * pixels,gpointer data)405 free_pixels (guchar *pixels, gpointer data)
406 {
407 g_free (pixels);
408 }
409
410 static GdkTexture *
scaled_from_pixdata(guchar * pixdata,int w,int h,int new_w,int new_h)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 (pixdata,
422 GDK_COLORSPACE_RGB,
423 TRUE,
424 8,
425 w, h, w * 4,
426 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 (GDK_COLORSPACE_RGB, TRUE, 8, size, size);
440
441 if (tmp != NULL)
442 {
443 gdk_pixbuf_fill (tmp, 0);
444 gdk_pixbuf_copy_area (src, 0, 0, w, h,
445 tmp,
446 (size - w) / 2, (size - h) / 2);
447
448 g_object_unref (src);
449 src = tmp;
450 }
451 }
452
453 if (w != new_w || h != new_h)
454 {
455 dest = gdk_pixbuf_scale_simple (src, new_w, new_h, 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 (dest);
465
466 g_object_unref (dest);
467
468 return ret;
469 }
470
471 static gboolean
get_window_list(GdkDisplay * display,Display * xdisplay,Window xwindow,Atom atom,Window ** windows,int * len)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 (*windows, data, 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 *
_gtk_mount_operation_lookup_context_get(GdkDisplay * display)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 (g_direct_hash, g_direct_equal);
544 context->display = display;
545
546 mapping = NULL;
547 mapping_length = 0;
548 get_window_list (context->display,
549 gdk_x11_display_get_xdisplay (context->display),
550 gdk_x11_display_get_xrootwindow (context->display),
551 gdk_x11_get_xatom_by_name_for_display (context->display,
552 "_NET_CLIENT_LIST"),
553 &mapping,
554 &mapping_length);
555 for (n = 0; n < mapping_length; n++)
556 {
557 int pid;
558
559 if (!get_cardinal (context->display,
560 GDK_DISPLAY_XDISPLAY (context->display),
561 mapping[n],
562 gdk_x11_get_xatom_by_name_for_display (context->display,
563 "_NET_WM_PID"),
564 &pid))
565 continue;
566
567 g_hash_table_insert (context->pid_to_window,
568 GINT_TO_POINTER (pid),
569 GINT_TO_POINTER ((int) mapping[n]));
570 }
571 g_free (mapping);
572
573 return context;
574 }
575
576 void
_gtk_mount_operation_lookup_context_free(GtkMountOperationLookupContext * context)577 _gtk_mount_operation_lookup_context_free (GtkMountOperationLookupContext *context)
578 {
579 g_hash_table_unref (context->pid_to_window);
580 g_free (context);
581 }
582
583 /* ---------------------------------------------------------------------------------------------------- */
584
585 #ifdef __linux__
586
587 static GPid
pid_get_parent(GPid pid)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 ("/proc/%d/status", pid);
606 if (g_file_get_contents (stat_filename,
607 &stat_contents,
608 &stat_len,
609 NULL))
610 {
611 guint n;
612
613 tokens = g_strsplit (stat_contents, "\n", 0);
614
615 for (n = 0; tokens[n] != NULL; n++)
616 {
617 if (g_str_has_prefix (tokens[n], "PPid:"))
618 {
619 char *endp;
620
621 endp = NULL;
622 ppid = strtoll (tokens[n] + sizeof "PPid:" - 1, &endp, 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 (tokens);
638 g_free (stat_contents);
639 g_free (stat_filename);
640
641 return ppid;
642 }
643
644 static char *
pid_get_env(GPid pid,const char * key)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 (key);
658
659 env_filename = g_strdup_printf ("/proc/%d/environ", pid);
660 if (g_file_get_contents (env_filename,
661 &env,
662 &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 (env + n, key) && (*(env + n + key_len) == '='))
675 {
676 ret = g_strdup (env + n + key_len + 1);
677
678 /* skip invalid UTF-8 */
679 if (!g_utf8_validate (ret, -1, (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 (env);
689 }
690 g_free (env_filename);
691
692 return ret;
693 }
694
695 static char *
pid_get_command_line(GPid pid)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 ("/proc/%d/cmdline", pid);
707 if (!g_file_get_contents (cmdline_filename,
708 &cmdline_contents,
709 &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 (cmdline_contents, -1, (const char **) &end))
722 *end = '\0';
723
724 out:
725 g_free (cmdline_filename);
726
727 return cmdline_contents;
728 }
729
730 /* ---------------------------------------------------------------------------------------------------- */
731
732 #elif defined(__OpenBSD__)
733
734 /* ---------------------------------------------------------------------------------------------------- */
735
736 static GPid
pid_get_parent(GPid pid)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 *
pid_get_env(GPid pid,const char * key)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 *
pid_get_command_line(GPid pid)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
pid_get_parent(GPid pid)840 pid_get_parent (GPid pid)
841 {
842 return 0;
843 }
844
845 static char *
pid_get_env(GPid pid,const char * key)846 pid_get_env (GPid pid,
847 const char *key)
848 {
849 return NULL;
850 }
851
852 static char *
pid_get_command_line(GPid pid)853 pid_get_command_line (GPid pid)
854 {
855 return NULL;
856 }
857
858 #endif
859
860 /* ---------------------------------------------------------------------------------------------------- */
861
862 static char *
get_name_for_window_with_pid(GtkMountOperationLookupContext * context,GPid pid)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, "WINDOWID");
879 if (windowid_value != NULL)
880 {
881 char *endp;
882
883 endp = NULL;
884 windowid_window = (Window) g_ascii_strtoll (windowid_value, &endp, 10);
885 if (endp != NULL && *endp == '\0')
886 {
887 window = windowid_window;
888 }
889 g_free (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 (context->display,
912 GDK_DISPLAY_XDISPLAY (context->display),
913 window,
914 gdk_x11_get_xatom_by_name_for_display (context->display,
915 "_NET_WM_NAME"));
916 if (ret == NULL)
917 ret = get_utf8_property (context->display,
918 GDK_DISPLAY_XDISPLAY (context->display),
919 window, gdk_x11_get_xatom_by_name_for_display (context->display,
920 "_NET_WM_ICON_NAME"));
921 }
922
923 return ret;
924 }
925
926 /* ---------------------------------------------------------------------------------------------------- */
927
928 static GdkTexture *
get_texture_for_window_with_pid(GtkMountOperationLookupContext * context,GPid pid,int size_pixels)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 (context->display,
962 GDK_DISPLAY_XDISPLAY (context->display),
963 window,
964 size_pixels, size_pixels,
965 &width, &height,
966 &pixdata))
967 {
968 /* steals pixdata */
969
970 ret = scaled_from_pixdata (pixdata,
971 width, height,
972 size_pixels, 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
_gtk_mount_operation_lookup_info(GtkMountOperationLookupContext * context,GPid pid,int size_pixels,char ** out_name,char ** out_command_line,GdkTexture ** out_texture)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 (*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 (*out_command_line, ' ');
1032 if (s == NULL)
1033 s = *out_command_line + strlen (*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 (p, 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 (well_known_commands[n], *out_name) == 0) ||
1052 ((*out_name)[0] == '-' && (strcmp (well_known_commands[n], (*out_name) + 1) == 0)))
1053 {
1054 g_free (*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
_gtk_mount_operation_kill_process(GPid pid,GError ** error)1065 _gtk_mount_operation_kill_process (GPid pid,
1066 GError **error)
1067 {
1068 gboolean ret;
1069
1070 ret = TRUE;
1071
1072 if (kill ((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 (error,
1083 G_IO_ERROR,
1084 g_io_error_from_errno (errsv),
1085 _("Cannot end process with PID %d: %s"),
1086 pid,
1087 g_strerror (errsv));
1088 }
1089
1090 return ret;
1091 }
1092