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
52static gboolean get_window_list (GdkDisplay *display,
53 Display *xdisplay,
54 Window xwindow,
55 Atom atom,
56 Window **windows,
57 int *len);
58
59static char* get_utf8_property (GdkDisplay *display,
60 Display *xdisplay,
61 Window xwindow,
62 Atom atom);
63
64static gboolean get_cardinal (GdkDisplay *display,
65 Display *xdisplay,
66 Window xwindow,
67 Atom atom,
68 int *val);
69
70static 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
80static gboolean
81get_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
124static char*
125get_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
182static gboolean
183find_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
214static gboolean
215find_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
310static void
311argbdata_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
344static gboolean
345read_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
404static void
405free_pixels (guchar *pixels, gpointer data)
406{
407 g_free (mem: pixels);
408}
409
410static GdkTexture *
411scaled_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
471static gboolean
472get_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
522struct _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
533GtkMountOperationLookupContext *
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
576void
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
587static GPid
588pid_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
644static char *
645pid_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
695static char *
696pid_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
736static GPid
737pid_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
762out:
763 if (kp)
764 g_free (kp);
765 return ppid;
766}
767
768static char *
769pid_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
807static char *
808pid_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
839static GPid
840pid_get_parent (GPid pid)
841{
842 return 0;
843}
844
845static char *
846pid_get_env (GPid pid,
847 const char *key)
848{
849 return NULL;
850}
851
852static char *
853pid_get_command_line (GPid pid)
854{
855 return NULL;
856}
857
858#endif
859
860/* ---------------------------------------------------------------------------------------------------- */
861
862static char *
863get_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
928static GdkTexture *
929get_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
981static 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
992gboolean
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
1064gboolean
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

source code of gtk/gtk/gtkmountoperation-x11.c