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