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