Решено: Не завершать потомка при завершении родителя (Потомок запущен через fork)

Модератор: Модераторы разделов

Ism
Сообщения: 1261
Статус: Никто, по сути быдло

Решено: Не завершать потомка при завершении родителя

Сообщение Ism »

Привет, делаю одну программку, упрощенный вариант gksu или kdesu

Никак не могу добиться, чтоб форкнутый потомок продолжал работать после завершения запустившего его приложения (весь гугл облазил, но именно данного функционала примеров не нашел)

Сначала идет forkpty, затем выполнение команды в форкнутом псевдотерминале ( su ), чтение и запись в терминал и тут загвоздка, запущенное в терминале приложение при закрытии родительского тоже завершается

код на pascal , но это не важно, так как используются функции posix, очень бы хотелось сделать все по правилам


Вот исходники, если что
http://code.google.com/p/xroot/source/browse/
Спасибо сказали:
ssh
Сообщения: 78
ОС: Debian

Re: Решено: Не завершать потомка при завершении родителя

Сообщение ssh »

if( fork() != 0 )
{
return 0 ; // завершаем папу.
}


// отключаем сынка от
// папиного терминала
i = open( "/dev/null", O_RDWR ) ;
close( 0 ) ; dup2( i, 0 ) ;
close( 1 ) ; dup2( i, 1 ) ;
close( 2 ) ; dup2( i, 2 ) ;
...
Спасибо сказали:
Ism
Сообщения: 1261
Статус: Никто, по сути быдло

Re: Решено: Не завершать потомка при завершении родителя

Сообщение Ism »

Спасибо ! Да хранит тебя мрачный князь Опенсорс.
Спасибо сказали:
Ism
Сообщения: 1261
Статус: Никто, по сути быдло

Re: Решено: Не завершать потомка при завершении родителя

Сообщение Ism »

рано радовался , на паскале аналогичный код не помог, решил проблему запуском программы через nohup

error_res:=fpexeclp ('nohup',['sh','-c','sudo '+ParamStr (2)+' > /dev/null 2>&1']) ;
Спасибо сказали:
watashiwa_daredeska
Бывший модератор
Сообщения: 4038
Статус: Искусственный интеллект (pre-alpha)
ОС: Debian GNU/Linux

Re: Решено: Не завершать потомка при завершении родителя

Сообщение watashiwa_daredeska »

Насколько я понимаю, аналоги gksu и пр. делаются без fork.
Если я правильно понял Вашу проблему, то:
1. Эмулятор терминала запускает my-gksu (или как там она у Вас называется) и ждет ее завершения.
2. my-gksu запускает дочернюю команду на том же терминале.
3. my-gksu завершается.
4. Эмулятор терминала радостно завершается и прибивает pty.
5. Дочерняя команда дохнет в муках.

Из этой ситуации есть два выхода:
1. exec* без fork.
2. родитель ждет child'а и не завершается раньше времени.
Спасибо сказали:
Ism
Сообщения: 1261
Статус: Никто, по сути быдло

Re: Решено: Не завершать потомка при завершении родителя

Сообщение Ism »

Весь смысл завершить родителя не завершая потомка

У меня программа выполняет error_res:=fpexeclp ('su',['--login','-c','нужная прога'+' > /dev/null 2>&1'])

описанный выше вариант почемуто не разрывает связь между родителем и потомком. Вернее рвет не ту связь .

алгоритм

1 Запускается gui программа, вводится пароль, жмется кнопка Ок
2 делается forkpty
3 в нем стартует error_res:=fpexeclp ('su',['--login','-c','нужная прога'+' > /dev/null 2>&1'])
4 su в псевдотерминале просит пароль
5 вводим пароль write ('pass\n', ..................)
6 тут мы ждем sleep несколько секунд пока стартует дочерняя прога под рут
7 Тут мы должна порвать связь gui оболочки и дочерних порожденных процессов. Что мне не удается.

Так вот в gksu какимто образом сделано, что запущенный псевдотерминал pty и запущенное в нем после завершения gui продолжает жить

вот содержимое libgksu-2.0.5 libgksu.c там тоже сделано через forkpty

я все там рассмотрел, но так и не понял где и как отцепляется дочерний процесс. Похоже надо будет детально разбирать код

Код: Выделить всё

/*
 * Gksu -- a library providing access to su functionality
 * Copyright (C) 2004 Gustavo Noronha Silva
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <pty.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <errno.h>

#include <glibtop.h>
#include <glibtop/procstate.h>

#include <gdk/gdk.h>
#include <gdk/gdkx.h>

#define SN_API_NOT_YET_FROZEN
#include <libsn/sn.h>

#include <gtk/gtk.h>
#include <locale.h>

#include <gconf/gconf-client.h>
#include <gnome-keyring.h>

#include "defines.h"
#include "../config.h"

#include "libgksu.h"
#include "../libgksuui/gksuui-dialog.h"

static void
gksu_context_launch_complete (GksuContext *context);

GType
gksu_error_get_type (void)
{
  static GType etype = 0;
  if (etype == 0) {
    static const GEnumValue values[] = {
      { GKSU_ERROR_HELPER, "GKSU_ERROR_HELPER", "helper" },
      { GKSU_ERROR_NOCOMMAND, "GKSU_ERROR_NOCOMMAND", "nocommand" },
      { GKSU_ERROR_NOPASSWORD, "GKSU_ERROR_NOPASSWORD", "nopassword" },
      { GKSU_ERROR_FORK, "GKSU_ERROR_FORK", "fork" },
      { GKSU_ERROR_EXEC, "GKSU_ERROR_EXEC", "exec" },
      { GKSU_ERROR_PIPE, "GKSU_ERROR_PIPE", "pipe" },
      { GKSU_ERROR_PIPEREAD, "GKSU_ERROR_PIPEREAD", "piperead" },
      { GKSU_ERROR_WRONGPASS, "GKSU_ERROR_WRONGPASS", "wrongpass" },
      { GKSU_ERROR_CHILDFAILED, "GKSU_ERROR_CHILDFAILED", "childfailed" },
      { GKSU_ERROR_CANCELED, "GKSU_ERROR_CANCELED", "canceled" },
      { GKSU_ERROR_WRONGAUTOPASS, "GKSU_ERROR_WRONGAUTOPASS", "wrongautopass" },
      { 0, NULL, NULL }
    };
    etype = g_enum_register_static ("GksuError", values);
  }
  return etype;
}

static pid_t
test_lock(const char* fname)
{
   int FD = open(fname, 0);
   if(FD < 0) {
      if(errno == ENOENT) {
     // File does not exist
     return 0;
      } else {
     perror("open");
     return(-1);
      }
   }
   struct flock fl;
   fl.l_type = F_WRLCK;
   fl.l_whence = SEEK_SET;
   fl.l_start = 0;
   fl.l_len = 0;
   if (fcntl(FD, F_GETLK, &fl) < 0) {
      g_critical("fcntl error");
      close(FD);
      return(-1);
   }
   close(FD);
   // lock is available
   if(fl.l_type == F_UNLCK)
      return(0);
   // file is locked by another process
   return (fl.l_pid);
}

static int
get_lock(const char *File)
{
   int FD = open(File,O_RDWR | O_CREAT | O_TRUNC,0640);
   if (FD < 0)
   {
      // Read only .. cant have locking problems there.
      if (errno == EROFS)
      {
     g_warning(_("Not using locking for read only lock file %s"),File);
     return dup(0);       // Need something for the caller to close
      }

      // Feh.. We do this to distinguish the lock vs open case..
      errno = EPERM;
      return -1;
   }
   fcntl(FD,F_SETFD, FD_CLOEXEC);

   // Aquire a write lock
   struct flock fl;
   fl.l_type = F_WRLCK;
   fl.l_whence = SEEK_SET;
   fl.l_start = 0;
   fl.l_len = 0;
   if (fcntl(FD,F_SETLK,&fl) == -1)
   {
      if (errno == ENOLCK)
      {
     g_warning(_("Not using locking for nfs mounted lock file %s"), File);
     unlink(File);
     close(FD);
     return dup(0);       // Need something for the caller to close
      }

      int Tmp = errno;
      close(FD);
      errno = Tmp;
      return -1;
   }

   return FD;
}

/*
 * code 'stolen' from gnome-session's logout.c
 *
 * Written by Owen Taylor <otaylor@redhat.com>
 * Copyright (C) Red Hat
 */
typedef struct {
  GdkScreen    *screen;
  int           monitor;
  GdkRectangle  area;
  int           rowstride;
  GdkWindow    *root_window;
  GdkWindow    *draw_window;
  GdkPixbuf    *start_pb, *end_pb, *frame;
  guchar       *start_p, *end_p, *frame_p;
  GTimeVal      start_time;
  GdkGC        *gc;
} FadeoutData;

FadeoutData *fade_data = NULL;
static GList *fadeout_windows = NULL;

#define FADE_DURATION 500.0

int
gsm_screen_get_width (GdkScreen *screen,
              int        monitor)
{
    GdkRectangle geometry;

    gdk_screen_get_monitor_geometry (screen, monitor, &geometry);

    return geometry.width;
}

int
gsm_screen_get_height (GdkScreen *screen,
               int        monitor)
{
    GdkRectangle geometry;

    gdk_screen_get_monitor_geometry (screen, monitor, &geometry);

    return geometry.height;
}

int
gsm_screen_get_x (GdkScreen *screen,
          int        monitor)
{
    GdkRectangle geometry;

    gdk_screen_get_monitor_geometry (screen, monitor, &geometry);

    return geometry.x;
}

int
gsm_screen_get_y (GdkScreen *screen,
          int        monitor)
{
    GdkRectangle geometry;

    gdk_screen_get_monitor_geometry (screen, monitor, &geometry);

    return geometry.y;
}

static void
get_current_frame (FadeoutData *fadeout,
           double    sat)
{
  guchar *sp, *ep, *fp;
  int i, j, width, offset;

  width = fadeout->area.width * 3;
  offset = 0;

  for (i = 0; i < fadeout->area.height; i++)
    {
      sp = fadeout->start_p + offset;
      ep = fadeout->end_p   + offset;
      fp = fadeout->frame_p + offset;

      for (j = 0; j < width; j += 3)
    {
      guchar r = abs (*(sp++) - ep[0]);
      guchar g = abs (*(sp++) - ep[1]);
      guchar b = abs (*(sp++) - ep[2]);

      *(fp++) = *(ep++) + r * sat;
      *(fp++) = *(ep++) + g * sat;
      *(fp++) = *(ep++) + b * sat;
    }

      offset += fadeout->rowstride;
    }
}

static void
darken_pixbuf (GdkPixbuf *pb)
{
  int width, height, rowstride;
  int i, j;
  guchar *p, *pixels;

  width     = gdk_pixbuf_get_width (pb) * 3;
  height    = gdk_pixbuf_get_height (pb);
  rowstride = gdk_pixbuf_get_rowstride (pb);
  pixels    = gdk_pixbuf_get_pixels (pb);

  for (i = 0; i < height; i++)
    {
      p = pixels + (i * rowstride);
      for (j = 0; j < width; j++)
    p [j] >>= 1;
    }
}

static gboolean
fadeout_callback (FadeoutData *fadeout)
{
  GTimeVal current_time;
  double elapsed, percent;

  g_get_current_time (&current_time);
  elapsed = ((((double)current_time.tv_sec - fadeout->start_time.tv_sec) * G_USEC_PER_SEC +
          (current_time.tv_usec - fadeout->start_time.tv_usec))) / 1000.0;

  if (elapsed < 0)
    {
      g_warning ("System clock seemed to go backwards?");
      elapsed = G_MAXDOUBLE;
    }

  if (elapsed > FADE_DURATION)
    {
      gdk_draw_pixbuf (fadeout->draw_window,
               fadeout->gc,
               fadeout->end_pb,
               0, 0,
               0, 0,
               fadeout->area.width,
               fadeout->area.height,
               GDK_RGB_DITHER_NONE,
               0, 0);

      return FALSE;
    }

  percent = elapsed / FADE_DURATION;

  get_current_frame (fadeout, 1.0 - percent);
  gdk_draw_pixbuf (fadeout->draw_window,
           fadeout->gc,
           fadeout->frame,
           0, 0,
           0, 0,
           fadeout->area.width,
           fadeout->area.height,
           GDK_RGB_DITHER_NONE,
           0, 0);

  gdk_flush ();

  return TRUE;
}

static void
hide_fadeout_windows (void)
{
  GList *l;

  for (l = fadeout_windows; l; l = l->next)
    {
      gdk_window_hide (GDK_WINDOW (l->data));
      g_object_unref (l->data);
    }

  g_list_free (fadeout_windows);
  fadeout_windows = NULL;
}

static gboolean
fadein_callback (FadeoutData *fadeout)
{
  GTimeVal current_time;
  double elapsed, percent;

  g_get_current_time (&current_time);
  elapsed = ((((double)current_time.tv_sec - fadeout->start_time.tv_sec) * G_USEC_PER_SEC +
          (current_time.tv_usec - fadeout->start_time.tv_usec))) / 1000.0;

  if (elapsed < 0)
    {
      g_warning ("System clock seemed to go backwards?");
      elapsed = G_MAXDOUBLE;
    }

  if (elapsed > FADE_DURATION)
    {
      gdk_draw_pixbuf (fadeout->draw_window,
               fadeout->gc,
               fadeout->end_pb,
               0, 0,
               0, 0,
               fadeout->area.width,
               fadeout->area.height,
               GDK_RGB_DITHER_NONE,
               0, 0);

      g_object_unref (fadeout->gc);
      g_object_unref (fadeout->start_pb);
      g_object_unref (fadeout->end_pb);
      g_object_unref (fadeout->frame);

      g_free (fadeout);

      hide_fadeout_windows ();

      return FALSE;
    }

  percent = elapsed / FADE_DURATION;

  get_current_frame (fadeout, percent);
  gdk_draw_pixbuf (fadeout->draw_window,
           fadeout->gc,
           fadeout->frame,
           0, 0,
           0, 0,
           fadeout->area.width,
           fadeout->area.height,
           GDK_RGB_DITHER_NONE,
           0, 0);

  gdk_flush ();

  return TRUE;
}

static void
fadeout_screen (GdkScreen *screen,
        int        monitor)
{
  GdkWindowAttr attr;
  int attr_mask;
  GdkGCValues values;
  FadeoutData *fadeout;

  fadeout = g_new (FadeoutData, 1);

  fadeout->screen = screen;
  fadeout->monitor = monitor;

  fadeout->area.x = gsm_screen_get_x (screen, monitor);
  fadeout->area.y = gsm_screen_get_y (screen, monitor);
  fadeout->area.width = gsm_screen_get_width (screen, monitor);
  fadeout->area.height = gsm_screen_get_height (screen, monitor);

  fadeout->root_window = gdk_screen_get_root_window (screen);
  attr.window_type = GDK_WINDOW_CHILD;
  attr.x = fadeout->area.x;
  attr.y = fadeout->area.y;
  attr.width = fadeout->area.width;
  attr.height = fadeout->area.height;
  attr.wclass = GDK_INPUT_OUTPUT;
  attr.visual = gdk_screen_get_system_visual (fadeout->screen);
  attr.colormap = gdk_screen_get_default_colormap (fadeout->screen);
  attr.override_redirect = TRUE;
  attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_NOREDIR;

  fadeout->draw_window = gdk_window_new (fadeout->root_window, &attr, attr_mask);
  fadeout_windows = g_list_prepend (fadeout_windows, fadeout->draw_window);

  fadeout->start_pb = gdk_pixbuf_get_from_drawable (NULL,
                            fadeout->root_window,
                            NULL,
                            fadeout->area.x,
                            fadeout->area.y,
                            0, 0,
                            fadeout->area.width,
                            fadeout->area.height);

  fadeout->end_pb = gdk_pixbuf_copy (fadeout->start_pb);
  darken_pixbuf (fadeout->end_pb);

  fadeout->frame = gdk_pixbuf_copy (fadeout->start_pb);
  fadeout->rowstride = gdk_pixbuf_get_rowstride (fadeout->start_pb);

  fadeout->start_p = gdk_pixbuf_get_pixels (fadeout->start_pb);
  fadeout->end_p   = gdk_pixbuf_get_pixels (fadeout->end_pb);
  fadeout->frame_p = gdk_pixbuf_get_pixels (fadeout->frame);

  values.subwindow_mode = GDK_INCLUDE_INFERIORS;

  fadeout->gc = gdk_gc_new_with_values (fadeout->root_window, &values, GDK_GC_SUBWINDOW);

  gdk_window_set_back_pixmap (fadeout->draw_window, NULL, FALSE);
  gdk_window_show (fadeout->draw_window);
  gdk_draw_pixbuf (fadeout->draw_window,
           fadeout->gc,
           fadeout->frame,
           0, 0,
           0, 0,
           fadeout->area.width,
           fadeout->area.height,
           GDK_RGB_DITHER_NONE,
           0, 0);

  g_get_current_time (&fadeout->start_time);
  g_idle_add ((GSourceFunc) fadeout_callback, fadeout);

  fade_data = fadeout;
}

/* End of 'stolen' code */

#define GRAB_TRIES    16
#define GRAB_WAIT    250 /* milliseconds */

typedef enum
  {
    FAILED_GRAB_MOUSE,
    FAILED_GRAB_KEYBOARD
  } FailedGrabWhat;

void
report_failed_grab (FailedGrabWhat what)
{
  GtkWidget *dialog;

  dialog = g_object_new (GTK_TYPE_MESSAGE_DIALOG,
             "message-type", GTK_MESSAGE_WARNING,
             "buttons", GTK_BUTTONS_CLOSE,
             NULL);

  switch (what)
    {
    case FAILED_GRAB_MOUSE:
      gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG(dialog),
                     _("<b><big>Could not grab your mouse.</big></b>"
                       "\n\n"
                       "A malicious client may be eavesdropping "
                       "on your session or you may have just clicked "
                       "a menu or some application just decided to get "
                       "focus."
                       "\n\n"
                       "Try again."));

      break;
    case FAILED_GRAB_KEYBOARD:
      gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG(dialog),
                     _("<b><big>Could not grab your keyboard.</big></b>"
                       "\n\n"
                       "A malicious client may be eavesdropping "
                       "on your session or you may have just clicked "
                       "a menu or some application just decided to get "
                       "focus."
                       "\n\n"
                       "Try again."));
      break;
    }

  gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
  gtk_dialog_run (GTK_DIALOG(dialog));
  gtk_widget_destroy (dialog);

  while (gtk_events_pending ())
    gtk_main_iteration ();

}

int
grab_keyboard_and_mouse (GtkWidget *dialog)
{
  GdkGrabStatus status;
  gint grab_tries = 0;
  gint lock = -1;

  gchar *fname = g_strdup_printf ("%s/.gksu.lock", getenv ("HOME"));
  pid_t pid = test_lock (fname);

  if (pid != 0)
    {
      g_warning ("Lock taken by pid: %i. Exiting.", pid);
      exit (0);
    }

  lock = get_lock(fname);
  if( lock < 0)
    g_warning ("Unable to create lock file.");
  g_free (fname);

  fadeout_screen (gdk_screen_get_default (), 0);
  gtk_widget_show_all (dialog);

  /* reset cursor */
  gdk_window_set_cursor(dialog->window, gdk_cursor_new(GDK_LEFT_PTR));

  for(;;)
    {
      status = gdk_pointer_grab ((GTK_WIDGET(dialog))->window, TRUE, 0, NULL,
                 NULL, GDK_CURRENT_TIME);
      if (status == GDK_GRAB_SUCCESS)
    break;
      usleep (GRAB_WAIT * 1000);
      if (++grab_tries > GRAB_TRIES)
    {
      gtk_widget_hide (dialog);
      g_get_current_time (&fade_data->start_time);
      while (fadein_callback (fade_data) != FALSE);
      report_failed_grab (FAILED_GRAB_MOUSE);
      exit (1);
      break;
    }
    }

  for(;;)
    {
      status = gdk_keyboard_grab ((GTK_WIDGET(dialog))->window,
                  FALSE, GDK_CURRENT_TIME);
      if (status == GDK_GRAB_SUCCESS)
    break;

      usleep(GRAB_WAIT * 1000);

      if (++grab_tries > GRAB_TRIES)
    {
      gtk_widget_hide (dialog);
      g_get_current_time (&fade_data->start_time);
      while (fadein_callback (fade_data) != FALSE);
      report_failed_grab (FAILED_GRAB_KEYBOARD);
      exit (1);
      break;
    }
    }

  /* we "raise" the window because there is a race here for
   * focus-follow-mouse and auto-raise WMs that may put the window
   * in the background and confuse users
   */
  gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);

  while (gtk_events_pending ())
    gtk_main_iteration ();

  return lock;
}

void
ungrab_keyboard_and_mouse (int lock)
{
  /* Ungrab */
  gdk_pointer_ungrab(GDK_CURRENT_TIME);
  gdk_keyboard_ungrab(GDK_CURRENT_TIME);
  gdk_flush();

  g_get_current_time (&fade_data->start_time);
  while (fadein_callback (fade_data) != FALSE);

  close(lock);
}

static gchar*
get_gnome_keyring_password (GksuContext *context)
{
  GnomeKeyringAttributeList *attributes;
  GnomeKeyringAttribute attribute;
  GnomeKeyringResult result;
  GList *list;

  attributes = gnome_keyring_attribute_list_new ();

  attribute.name = g_strdup ("user");
  attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
  attribute.value.string = g_strdup (gksu_context_get_user (context));
  g_array_append_val (attributes, attribute);

  attribute.name = g_strdup ("type");
  attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
  attribute.value.string = g_strdup ("local");
  g_array_append_val (attributes, attribute);

  attribute.name = g_strdup ("creator");
  attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
  attribute.value.string = g_strdup ("gksu");
  g_array_append_val (attributes, attribute);

  list = g_list_alloc();

  result = gnome_keyring_find_items_sync (GNOME_KEYRING_ITEM_GENERIC_SECRET,
                      attributes,
                      &list);
  gnome_keyring_attribute_list_free (attributes);
  if (
      (result == GNOME_KEYRING_RESULT_OK) &&
      (g_list_length(list) >= 1)
      )
    {
      GnomeKeyringFound *found = list->data;
      gint password_length = strlen (found->secret);
      gchar *password;

      password = g_locale_from_utf8 (found->secret,
                     password_length,
                     NULL, NULL, NULL);
      password_length = strlen (password);

      if (password[password_length-1] == '\n')
    password[password_length-1] = '\0';
      return password;
    }

  return NULL;
}

static void
keyring_create_item_cb (GnomeKeyringResult result,
                        guint32 id, gpointer keyring_loop)
{
  g_main_loop_quit (keyring_loop);
}

static void
set_gnome_keyring_password (GksuContext *context, gchar *password)
{
  GConfClient *gconf_client;
  gboolean save_to_keyring;

  gconf_client = context->gconf_client;
  save_to_keyring = gconf_client_get_bool (gconf_client, BASE_PATH"save-to-keyring", NULL);

  if (password && save_to_keyring)
    {
      static GMainLoop *keyring_loop = NULL;
      GnomeKeyringAttributeList *attributes;
      GnomeKeyringAttribute attribute;
      GnomeKeyringResult result;

      gchar *keyring_name;
      gchar *key_name;

      attributes = gnome_keyring_attribute_list_new ();

      attribute.name = g_strdup ("user");
      attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
      attribute.value.string = g_strdup (gksu_context_get_user (context));
      g_array_append_val (attributes, attribute);

      attribute.name = g_strdup ("type");
      attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
      attribute.value.string = g_strdup ("local");
      g_array_append_val (attributes, attribute);

      attribute.name = g_strdup ("creator");
      attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
      attribute.value.string = g_strdup ("gksu");
      g_array_append_val (attributes, attribute);

      key_name = g_strdup_printf ("Local password for user %s",
                  gksu_context_get_user (context));

      keyring_loop = g_main_loop_new (NULL, FALSE);

      keyring_name = gconf_client_get_string (gconf_client, BASE_PATH"save-keyring", NULL);
      if (keyring_name == NULL)
    keyring_name = g_strdup ("session");

      /* make sure the keyring exists; if an error occurs, use
         the session keyring */
      result = gnome_keyring_create_sync(keyring_name, NULL);
      if ((result != GNOME_KEYRING_RESULT_OK) &&
      (result != GNOME_KEYRING_RESULT_ALREADY_EXISTS))
    keyring_name = g_strdup ("session");

      gnome_keyring_item_create (keyring_name,
                 GNOME_KEYRING_ITEM_GENERIC_SECRET,
                 key_name,
                 attributes,
                 password,
                 TRUE,
                 keyring_create_item_cb,
                 keyring_loop, NULL);
      gnome_keyring_attribute_list_free (attributes);
      g_free (keyring_name);
      g_main_loop_run (keyring_loop);
    }
}

static void
unset_gnome_keyring_password (GksuContext *context)
{
  GConfClient *gconf_client;
  gboolean save_to_keyring;

  GnomeKeyringAttributeList *attributes;
  GnomeKeyringAttribute attribute;
  GnomeKeyringResult result;
  GList *list;

  gconf_client = context->gconf_client;
  save_to_keyring = gconf_client_get_bool (gconf_client, BASE_PATH"save-to-keyring", NULL);

  if (save_to_keyring)
    {
      attributes = gnome_keyring_attribute_list_new ();

      attribute.name = g_strdup ("user");
      attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
      attribute.value.string = g_strdup (gksu_context_get_user (context));
      g_array_append_val (attributes, attribute);

      attribute.name = g_strdup ("type");
      attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
      attribute.value.string = g_strdup ("local");
      g_array_append_val (attributes, attribute);

      attribute.name = g_strdup ("creator");
      attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
      attribute.value.string = g_strdup ("gksu");
      g_array_append_val (attributes, attribute);

      list = g_list_alloc();

      result = gnome_keyring_find_items_sync (GNOME_KEYRING_ITEM_GENERIC_SECRET,
                          attributes,
                          &list);
      gnome_keyring_attribute_list_free (attributes);
      if (
      (result == GNOME_KEYRING_RESULT_OK) &&
      (g_list_length(list) == 1)
      )
    {
      GnomeKeyringFound *found = list->data;

      gnome_keyring_item_delete_sync (found->keyring,
                      found->item_id);
    }
    }
}

void
get_configuration_options (GksuContext *context)
{
  GConfClient *gconf_client = context->gconf_client;
  gboolean force_grab;

  context->grab = !gconf_client_get_bool (gconf_client, BASE_PATH "disable-grab",
                      NULL);
  force_grab = gconf_client_get_bool (gconf_client, BASE_PATH "force-grab",
                      NULL);
  if (force_grab)
    context->grab = TRUE;

  context->sudo_mode = gconf_client_get_bool (gconf_client, BASE_PATH "sudo-mode",
                          NULL);
}

/**
 * su_ask_password:
 * @context: a #GksuContext
 * @prompt: the prompt that should be used instead of "Password: "
 * @data: data that is passed by gksu_*_full
 * @error: a pointer to pointer #GError that will be filled with
 * data if an error happens.
 *
 * This is a convenience function to create a #GksuuiDialog and
 * request the password.
 *
 * Returns: a newly allocated gchar containing the password
 * or NULL if an error happens or the user cancels the action
 */
static gchar*
su_ask_password (GksuContext *context, gchar *prompt,
         gpointer data, GError **error)
{
  GtkWidget *dialog = NULL;
  gchar *msg;
  gchar *password = NULL, *tmp = NULL;
  int retvalue = 0;
  int lock = 0;
  GQuark gksu_quark;

  gksu_quark = g_quark_from_string (PACKAGE);

  if (context->grab)
    dialog = g_object_new (GKSUUI_TYPE_DIALOG,
               "type", GTK_WINDOW_POPUP,
               "sudo-mode", context->sudo_mode,
               NULL);
  else
    dialog = gksuui_dialog_new (context->sudo_mode);

  if (prompt)
    gksuui_dialog_set_prompt (GKSUUI_DIALOG(dialog), _(prompt));

  if (context->message)
    gksuui_dialog_set_message (GKSUUI_DIALOG(dialog), context->message);
  else
    {
      gchar *command = NULL;
      if (context->description)
    command = context->description;
      else
    command = context->command;

      if (context->sudo_mode)
    {
      if (!strcmp(context->user, "root"))
        msg = g_strdup_printf (_("<b><big>Enter your password to perform"
                     " administrative tasks</big></b>\n\n"
                     "The application '%s' lets you "
                     "modify essential parts of your "
                     "system."),
                   command);
      else
        msg = g_strdup_printf (_("<b><big>Enter your password to run "
                     "the application '%s' as user %s"
                     "</big></b>"),
                   command, context->user);
    }
      else
    {
        if (strcmp(gksu_context_get_user (context), "root") == 0)
          msg = g_strdup_printf (_("<b><big>Enter the administrative password"
                                   "</big></b>\n\n"
                                   "The application '%s' lets you "
                                   "modify essential parts of your "
                                   "system."),
                   command);
        else
          msg = g_strdup_printf (_("<b><big>Enter the password of %s to run "
                                   "the application '%s'"
                                   "</big></b>"),
                   command, context->user);
      }

      gksuui_dialog_set_message (GKSUUI_DIALOG(dialog), msg);
      g_free (msg);
    }

  if (context->alert)
    gksuui_dialog_set_alert (GKSUUI_DIALOG(dialog), context->alert);

  if (context->grab)
    lock = grab_keyboard_and_mouse (dialog);
  retvalue = gtk_dialog_run (GTK_DIALOG(dialog));
  gtk_widget_hide (dialog);
  if (context->grab)
    ungrab_keyboard_and_mouse (lock);

  while (gtk_events_pending ())
    gtk_main_iteration ();

  if (retvalue != GTK_RESPONSE_OK)
    {
      switch (retvalue)
    {
    case GTK_RESPONSE_CANCEL:
    case GTK_RESPONSE_DELETE_EVENT:
      g_set_error (error, gksu_quark,
               GKSU_ERROR_CANCELED,
               _("Password prompt canceled."));
      if (context->sn_context)
        gksu_context_launch_complete (context);
    }

      gtk_widget_destroy (dialog);
      while (gtk_events_pending ())
    gtk_main_iteration ();

      return NULL;
    }

  tmp = gksuui_dialog_get_password (GKSUUI_DIALOG(dialog));
  password = g_locale_from_utf8 (tmp, strlen (tmp), NULL, NULL, NULL);
  g_free (tmp);

  gtk_widget_destroy (dialog);
  while (gtk_events_pending ())
    gtk_main_iteration ();

  return password;
}

static void
cb_toggled_cb (GtkWidget *button, gpointer data)
{
  GConfClient *gconf_client;
  gchar *key;
  gboolean toggled;
  gchar *key_name;

  g_return_if_fail (data != NULL);

  key_name = (gchar*)data;

  gconf_client = gconf_client_get_default ();
  toggled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button));

  key = g_strdup_printf (BASE_PATH "%s", key_name);

  if (!strcmp (key_name, "display-no-pass-info"))
    {
      /* the meaning of the key is the exact opposite of the meaning
     of the answer - when the check button is checked the key must
     be off
      */
      gconf_client_set_bool (gconf_client, key, !toggled, NULL);
    }
  else
    gconf_client_set_bool (gconf_client, key, toggled, NULL);

  g_object_unref (gconf_client);

  g_free (key);
}

void
no_pass (GksuContext *context, gpointer data)
{
  GtkWidget *dialog;
  GtkWidget *alignment;
  GtkWidget *check_button;

  gchar *command = NULL;

  if (context->description)
    command = context->description;
  else
    command = context->command;

  dialog = gtk_message_dialog_new_with_markup (NULL, 0,
                           GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
                           _("<b><big>Granted permissions without asking "
                         "for password</big></b>"
                         "\n\n"
                         "The '%s' program was started with "
                         "the privileges of the %s user without "
                         "the need to ask for a password, due to "
                         "your system's authentication mechanism "
                         "setup."
                         "\n\n"
                         "It is possible that you are being allowed "
                         "to run specific programs as user %s "
                         "without the need for a password, or that "
                         "the password is cached."
                         "\n\n"
                         "This is not a problem report; it's "
                         "simply a notification to make sure "
                         "you are aware of this."),
                           command,
                           context->user,
                           context->user);

  alignment = gtk_alignment_new (0.5, 0.5, 0.6, 1);
  gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), alignment, TRUE, TRUE, 2);

  check_button = gtk_check_button_new_with_mnemonic (_("Do _not display this message again"));
  g_signal_connect (G_OBJECT(check_button), "toggled",
            G_CALLBACK(cb_toggled_cb), "display-no-pass-info");
  gtk_container_add (GTK_CONTAINER(alignment), check_button);

  gtk_widget_show_all (dialog);
  gtk_dialog_run (GTK_DIALOG(dialog));
  gtk_widget_destroy (GTK_WIDGET(dialog));

  while (gtk_events_pending ())
    gtk_main_iteration ();
}

static void
gksu_prompt_grab (GksuContext *context)
{
  GtkWidget *d;

  d = gtk_message_dialog_new_with_markup (NULL, 0, GTK_MESSAGE_QUESTION,
                      GTK_BUTTONS_YES_NO,
                      _("<b>Would you like your screen to be \"grabbed\"\n"
                        "while you enter the password?</b>"
                        "\n\n"
                        "This means all applications will be paused to avoid\n"
                        "the eavesdropping of your password by a a malicious\n"
                        "application while you type it."));

  if (gtk_dialog_run (GTK_DIALOG(d)) == GTK_RESPONSE_NO)
    context->grab = FALSE;
  else
    context->grab = TRUE;

  gtk_widget_destroy (d);
}

static void
nullify_password (gchar *pass)
{
  if (pass)
    {
      memset(pass, 0, strlen(pass));
      g_free (pass);
    }
  pass = NULL;
}

static gchar *
get_process_name (pid_t pid)
{
  static gboolean init;
  glibtop_proc_state buf;

  if (!init) {
    glibtop_init();
    init = TRUE;
  }

  glibtop_get_proc_state (&buf, pid);
  return strdup(buf.cmd);
}

static gchar *
get_xauth_token (GksuContext *context, gchar *display)
{
  gchar *xauth_bin = NULL;
  FILE *xauth_output;
  gchar *tmp = NULL;
  gchar *xauth = g_new0 (gchar, 256);

  /* find out where the xauth binary is located */
  if (g_file_test ("/usr/bin/xauth", G_FILE_TEST_IS_EXECUTABLE))
    xauth_bin = "/usr/bin/xauth";
  else if (g_file_test ("/usr/X11R6/bin/xauth", G_FILE_TEST_IS_EXECUTABLE))
    xauth_bin = "/usr/X11R6/bin/xauth";
  else
    {
      fprintf (stderr,
           "Failed to obtain xauth key: xauth binary not found "
           "at usual locations");

      return NULL;
    }

  /* get the authorization token */
  tmp = g_strdup_printf ("%s list %s | "
             "head -1 | awk '{ print $3 }'",
             xauth_bin,
             display);
  if ((xauth_output = popen (tmp, "r")) == NULL)
    {
      fprintf (stderr,
           "Failed to obtain xauth key: %s",
           strerror(errno));
      return NULL;
    }
  fread (xauth, sizeof(char), 255, xauth_output);
  pclose (xauth_output);
  g_free (tmp);

  if (context->debug)
    {
      fprintf(stderr,
          "xauth: -%s-\n"
          "display: -%s-\n",
          xauth, display);
    }

  return xauth;
}

/**
 * prepare_xauth:
 *
 * Sets up the variables with values for the $DISPLAY
 * environment variable and xauth-related stuff. Also
 * creates a temporary directory to hold a .Xauthority
 *
 * Returns: TRUE if it suceeds, FALSE if it fails.
 */
static int
prepare_xauth (GksuContext *context)
{
  gchar *display = NULL;
  gchar *xauth = NULL;

  display = g_strdup (getenv ("DISPLAY"));
  xauth = get_xauth_token (context, display);
  if (xauth == NULL)
    {
      g_free (display);
      return FALSE;
    }

  /* If xauth is the empty string, then try striping the
   * hostname part of the DISPLAY string for getting the
   * auth token; this is needed for ssh-forwarded usage
   */
  if (!strcmp ("", xauth))
    {
      gchar *cut_display = NULL;

      g_free (xauth);
      cut_display = g_strdup (g_strrstr (display, ":"));
      xauth = get_xauth_token (context, cut_display);

      g_free (display);
      display = cut_display;
    }

  context->xauth = xauth;
  context->display = display;

  if (context->debug)
    {
      fprintf(stderr,
          "final xauth: -%s-\n"
          "final display: -%s-\n",
          context->xauth, context->display);
    }

  return TRUE;
}

/* Write all of buf, even if write(2) is interrupted. */
static ssize_t
full_write (int d, const char *buf, size_t nbytes)
{
  ssize_t r, w = 0;

  /* Loop until nbytes of buf have been written. */
  while (w < nbytes) {
    /* Keep trying until write succeeds without interruption. */
    do {
      r = write(d, buf + w, nbytes - w);
    } while (r < 0 && errno == EINTR);

    if (r < 0) {
      return -1;
    }

    w += r;
  }

  return w;
}

static gboolean
copy (const char *fn, const char *dir)
{
  int in, out;
  int r;
  char *newfn;
  char buf[BUFSIZ] = "";

  newfn = g_strdup_printf("%s/.Xauthority", dir);

  out = open(newfn, O_WRONLY | O_CREAT | O_EXCL, 0600);
  if (out == -1)
    {
      if (errno == EEXIST)
    fprintf (stderr,
         "Impossible to create the .Xauthority file: a file "
         "already exists. This might be a security issue; "
         "please investigate.");
      else
    fprintf (stderr,
         "Error copying '%s' to '%s': %s",
         fn, dir, strerror(errno));

      return FALSE;
    }

  in = open(fn, O_RDONLY);
  if (in == -1)
    {
      fprintf (stderr,
           "Error copying '%s' to '%s': %s",
           fn, dir, strerror(errno));
      return FALSE;
    }

  while ((r = read(in, buf, BUFSIZ)) > 0)
    {
      if (full_write(out, buf, r) == -1)
    {
      fprintf (stderr,
           "Error copying '%s' to '%s': %s",
           fn, dir, strerror(errno));
      return FALSE;
    }
    }

  if (r == -1)
    {
      fprintf (stderr,
           "Error copying '%s' to '%s': %s",
           fn, dir, strerror(errno));
      return FALSE;
    }

  return TRUE;
}

static gboolean
sudo_prepare_xauth (GksuContext *context)
{
  gchar template[] = "/tmp/" PACKAGE "-XXXXXX";
  gboolean error_copying = FALSE;
  gchar *xauth = NULL;

  context->dir = g_strdup (mkdtemp(template));
  if (!context->dir)
    {
      fprintf (stderr, strerror(errno));
      return FALSE;
    }

  xauth = g_strdup(g_getenv ("XAUTHORITY"));
  if (xauth == NULL)
    xauth = g_strdup_printf ("%s/.Xauthority", g_get_home_dir());

  error_copying = !copy (xauth, context->dir);
  g_free (xauth);

  if (error_copying)
    return FALSE;

  return TRUE;
}

static void
sudo_reset_xauth (GksuContext *context, gchar *xauth,
          gchar *xauth_env)
{
  /* reset the env var as it was before or clean it  */
  if (xauth_env)
    setenv ("XAUTHORITY", xauth_env, TRUE);
  else
    unsetenv ("XAUTHORITY");

  if (context->debug)
    fprintf (stderr, "xauth: %s\nxauth_env: %s\ndir: %s\n",
         xauth, xauth_env, context->dir);

  unlink (xauth);
  rmdir (context->dir);

  g_free (xauth);
}

static void
startup_notification_initialize (GksuContext *context)
{
  SnDisplay *sn_display;
  sn_display = sn_display_new (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
                   NULL, NULL);
  context->sn_context = sn_launcher_context_new (sn_display, gdk_screen_get_number (gdk_display_get_default_screen (gdk_display_get_default ())));
  sn_launcher_context_set_description (context->sn_context, _("Granting Rights"));
  sn_launcher_context_set_name (context->sn_context, g_get_prgname ());
}

/**
 * gksu_context_new
 *
 * This function should be used when creating a new #GksuContext to
 * pass to gksu_su_full or gksu_sudo_full. The #GksuContext must be
 * freed with gksu_context_free.
 *
 * Returns: a newly allocated #GksuContext
 */
GksuContext*
gksu_context_new ()
{
  GksuContext *context;

  context = g_new (GksuContext, 1);

  context->xauth = NULL;
  context->dir = NULL;
  context->display = NULL;

  context->gconf_client = gconf_client_get_default ();

  context->sudo_mode = FALSE;

  context->user = g_strdup ("root");
  context->command = NULL;

  context->login_shell = FALSE;
  context->keep_env = FALSE;

  context->description = NULL;
  context->message = NULL;
  context->alert = NULL;
  context->grab = TRUE;

  context->debug = FALSE;

  context->sn_context = NULL;
  context->sn_id = NULL;

  context->ref_count = 1;

  get_configuration_options (context);
  startup_notification_initialize (context);

  return context;
}

/**
 * gksu_context_set_user:
 * @context: the #GksuContext you want to modify
 * @username: the target username
 *
 * Sets up what user the command will be run as. The default
 * is root, but you can run the command as any user.
 *
 */
void
gksu_context_set_user (GksuContext *context, gchar *username)
{
  g_assert (username != NULL);

  if (context->user)
    g_free (context->user);
  context->user = g_strdup (username);
}

/**
 * gksu_context_get_user:
 * @context: the #GksuContext from which to grab the information
 *
 * Gets the user the command will be run as, as set
 * by gksu_context_set_user.
 *
 * Returns: a string with the user or NULL if not set.
 */
const gchar*
gksu_context_get_user (GksuContext *context)
{
  return context->user;
}

/**
 * gksu_context_set_command:
 * @context: the #GksuContext you want to modify
 * @command: the command that shall be ran
 *
 * Sets up what command will run with the target user.
 *
 */
void
gksu_context_set_command (GksuContext *context, gchar *command)
{
  g_assert (command != NULL);

  if (context->command)
    g_free (context->command);
  context->command = g_strdup (command);

  /* startup notification */
  sn_launcher_context_set_binary_name (context->sn_context,
                       command);
}

/**
 * gksu_context_get_command:
 * @context: the #GksuContext from which to grab the information
 *
 * Gets the command that will be run, as set by
 * gksu_context_set_command.
 *
 * Returns: a string with the command or NULL if not set.
 */
const gchar*
gksu_context_get_command (GksuContext *context)
{
  return context->command;
}

/**
 * gksu_context_set_login_shell:
 * @context: the #GksuContext you want to modify
 * @value: TRUE or FALSE
 *
 * Should the shell in which the command will be run be
 * a login shell?
 */
void
gksu_context_set_login_shell (GksuContext *context, gboolean value)
{
  context->login_shell = value;
}

/**
 * gksu_context_get_login_shell:
 * @context: the #GksuContext from which to grab the information
 *
 * Finds out if the shell created by the underlying su process
 * will be a login shell.
 *
 * Returns: TRUE if the shell will be a login shell, FALSE otherwise.
 */
gboolean
gksu_context_get_login_shell (GksuContext *context)
{
  return context->login_shell;
}

/**
 * gksu_context_set_keep_env:
 * @context: the #GksuContext you want to modify
 * @value: TRUE or FALSE
 *
 * Should the environment be kept as it is? Defaults to
 * TRUE. Notice that setting this to FALSE may cause the
 * X authorization stuff to fail.
 */
void
gksu_context_set_keep_env (GksuContext *context, gboolean value)
{
  context->keep_env = value;
}

/**
 * gksu_context_get_keep_env:
 * @context: the #GksuContext from which to grab the information
 *
 * Finds out if the environment in which the program will be
 * run will be reset.
 *
 * Returns: TRUE if the environment is going to be kept,
 * FALSE otherwise.
 */
gboolean
gksu_context_get_keep_env (GksuContext *context)
{
  return context->keep_env;
}

/**
 * gksu_context_set_description:
 * @context: the #GksuContext you want to modify
 * @description: a string to set the description for
 *
 * Set the nice name for the action that is being run that the window
 * that asks for the password will have.  This is only meant to be
 * used if the default window is used, of course.
 */
void
gksu_context_set_description (GksuContext *context, gchar *description)
{
  if (context->description)
    g_free (context->description);
  context->description = g_strdup (description);
}

/**
 * gksu_context_get_description:
 * @context: the #GksuContext you want to get the description from.
 *
 * Get the description that the window will have when the
 * default function for requesting the password is
 * called.
 *
 * Returns: a string with the description or NULL if not set.
 */
gchar*
gksu_context_get_description (GksuContext *context)
{
  return context->description;
}

/**
 * gksu_context_set_message:
 * @context: the #GksuContext you want to modify
 * @message: a string to set the message for
 *
 * Set the message that the window that asks for the password will have.
 * This is only meant to be used if the default window is used, of course.
 */
void
gksu_context_set_message (GksuContext *context, gchar *message)
{
  if (context->message)
    g_free (context->message);
  context->message = g_strdup (message);
}

/**
 * gksu_context_get_message:
 * @context: the #GksuContext you want to get the message from.
 *
 * Get the message that the window will have when the
 * default function for requesting the password is
 * called.
 *
 * Returns: a string with the message or NULL if not set.
 */
gchar*
gksu_context_get_message (GksuContext *context)
{
  return context->message;
}

/**
 * gksu_context_set_alert:
 * @context: the #GksuContext you want to modify
 * @alert: a string to set the alert for
 *
 * Set the alert that the window that asks for the password will have.
 * This is only meant to be used if the default window is used, of course.
 * This alert should be used to display messages such as 'incorrect password',
 * for instance.
 */
void
gksu_context_set_alert (GksuContext *context, gchar *alert)
{
  if (context->alert)
    g_free (context->alert);
  context->alert = g_strdup (alert);
}

/**
 * gksu_context_get_alert:
 * @context: the #GksuContext you want to get the alert from.
 *
 * Get the alert that the window will have when the
 * default function for requesting the password is
 * called.
 *
 * Returns: a string with the alert or NULL if not set.
 */
gchar*
gksu_context_get_alert (GksuContext *context)
{
  return context->alert;
}

/**
 * gksu_context_set_debug:
 * @context: the #GksuContext you want to modify
 * @value: TRUE or FALSE
 *
 * Set up if debuging information should be printed.
 */
void
gksu_context_set_grab (GksuContext *context, gboolean value)
{
  context->grab = value;
}

/**
 * gksu_context_get_grab:
 * @context: the #GksuContext you want to ask whether a grab will be done.
 *
 * Returns TRUE if gksu has been asked to do a grab on keyboard and mouse
 * when asking for the password.
 *
 * Returns: TRUE if yes, FALSE otherwise.
 */
gboolean
gksu_context_get_grab (GksuContext *context)
{
  return context->grab;
}

/**
 * gksu_context_set_launcher_context:
 * @context: the #GksuContext you want to set the sn context in
 * @sn_context: the #SnLauncherContext you want to set
 *
 * Tell libgksu to use the given #SnLauncherContext for startup notification.
 * Currently the library will use this to set DESKTOP_STARTUP_ID in the
 * environment of the child and to issue initiate and complete events.
 * Notice that you don't need to use this function unless you want to
 * override gksu's default behavior on startup notification, since the
 * library will create its own context.
 *
 * Returns: the #SnLauncherContext which is set, or NULL if none was set
 */
void
gksu_context_set_launcher_context (GksuContext *context,
                   SnLauncherContext *sn_context)
{
  if (context->sn_context)
    sn_launcher_context_unref (context->sn_context);
  context->sn_context = sn_context;
}

/**
 * gksu_context_get_launcher_context:
 * @context: the #GksuContext you want to get the sn context from
 *
 * Gets the current startup notification launcher context
 *
 * Returns: the #SnLauncherContext which is set, or NULL if none was set
 */
SnLauncherContext*
gksu_context_get_launcher_context (GksuContext *context)
{
  return context->sn_context;
}

/**
 * gksu_context_set_launcher_id:
 * @context: the #GksuContext you want to set the sn id in
 * @sn_context: the sn_id you want to set, as a #gchar
 */
void
gksu_context_set_launcher_id (GksuContext *context,
                  gchar *sn_id)
{
  if (context->sn_id)
    g_free (context->sn_id);
  context->sn_id = g_strdup(sn_id);
}

/**
 * gksu_context_launch_initiate:
 * @context: the #GksuContext you want to initiate the launch for
 *
 * Initiates the launch, as far as Startup Notification is concerned;
 * This will only be used internally, usually.
 */
static void
gksu_context_launch_initiate (GksuContext *context)
{
  gchar *sid = NULL;
  guint32 launch_time = gdk_x11_display_get_user_time ((GdkDisplay*)GDK_DISPLAY());
  static gboolean initiated = FALSE;

  if (!initiated)
    initiated = TRUE;
  else
    return;

  sn_launcher_context_initiate (context->sn_context,
                g_get_prgname (),
                gksu_context_get_command (context),
                launch_time);

  sid = g_strdup_printf ("%s", sn_launcher_context_get_startup_id (context->sn_context));
  gksu_context_set_launcher_id (context, sid);

  if (context->debug)
    fprintf (stderr, "STARTUP_ID: %s\n", sid);
  setenv ("DESKTOP_STARTUP_ID", sid, TRUE);
  g_free(sid);
}

/**
 * gksu_context_launch_complete:
 * @context: the #GksuContext you want to complete the launch for
 *
 * Completes the launch, as far as Startup Notification is concerned;
 * This will only be used internally, usually.
 */
static void
gksu_context_launch_complete (GksuContext *context)
{
  sn_launcher_context_complete(context->sn_context);
}

/**
 * gksu_context_set_debug:
 * @context: the #GksuContext you want to modify
 * @value: TRUE or FALSE
 *
 * Set up if debuging information should be printed.
 */
void
gksu_context_set_debug (GksuContext *context, gboolean value)
{
  context->debug = value;
}

/**
 * gksu_context_get_debug:
 * @context: the #GksuContext from which to grab the information
 *
 * Finds out if the library is configured to print debuging
 * information.
 *
 * Returns: TRUE if it is, FALSE otherwise.
 */
gboolean
gksu_context_get_debug (GksuContext *context)
{
  return context->debug;
}

/**
 * gksu_context_free
 * @context: the #GksuContext to be freed.
 *
 * Frees the given #GksuContext.
 */
void
gksu_context_free (GksuContext *context)
{
  g_free (context->xauth);
  g_free (context->dir);
  g_free (context->display);

  g_object_unref (context->gconf_client);

  g_free (context->description);
  g_free (context->message);

  g_free (context->user);
  g_free (context->command);

  g_free (context);
}

/**
 * gksu_context_ref
 * @context: A #GksuContext struct.
 *
 * Increments the reference count of the given #GksuContext.
 */
GksuContext*
gksu_context_ref (GksuContext *context)
{
  context->ref_count++;
  return context;
}

/**
 * gksu_context_unref
 * @context: A #GksuContext struct.
 *
 * Decrements the reference count of the given #GksuContext struct,
 * freeing it if the reference count falls to zero.
 */
void
gksu_context_unref (GksuContext *context)
{
  if (--context->ref_count == 0)
    {
        gksu_context_free (context);
    }
}

GType
gksu_context_get_type (void)
{
  static GType type_gksu_context = 0;

  if (!type_gksu_context)
    type_gksu_context = g_boxed_type_register_static
      ("GksuContext",
       (GBoxedCopyFunc) gksu_context_ref,
       (GBoxedFreeFunc) gksu_context_unref);

  return type_gksu_context;
}




/**
 * gksu_su_full:
 * @context: a #GksuContext
 * @ask_pass: a #GksuAskPassFunc to be called when the lib determines
 * requesting a password is necessary; it may be NULL, in which case
 * the standard password request dialog will be used
 * @ask_pass_data: a #gpointer with user data to be passed to the
 * #GksuAskPasswordFunc
 * @pass_not_needed: a #GksuPassNotNeededFunc that will be called
 * when the command is being run without the need for requesting
 * a password; it will only be called if the display-no-pass-info
 * gconf key is enabled; NULL will have the standard dialog be shown
 * @pass_not_needed_data: a #gpointer with the user data to be passed to the
 * #GksuPasswordNotNeededFunc
 * @error: a #GError object to be filled with the error code or NULL
 *
 * This could be considered one of the main functions in GKSu.
 * it is responsible for doing the 'user changing' magic calling
 * the #GksuAskPassFunc function to request a password if needed.
 * and the #GksuPassNotNeededFunc function if a password won't be
 * needed, so the application has the oportunity of warning the user
 * what it's doing.
 *
 * This function uses su as backend.
 *
 * Returns: TRUE if all went fine, FALSE if failed
 */
gboolean
gksu_su_full (GksuContext *context,
          GksuAskPassFunc ask_pass,
          gpointer ask_pass_data,
          GksuPassNotNeededFunc pass_not_needed,
          gpointer pass_not_needed_data,
          GError **error)
{
  GQuark gksu_quark;
  int i = 0;

  gchar auxcommand[] = PREFIX "/lib/" PACKAGE "/gksu-run-helper";

  int fdpty;
  pid_t pid;

  context->sudo_mode = FALSE;

  gksu_quark = g_quark_from_string (PACKAGE);

  if (!context->command)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_NOCOMMAND,
           _("gksu_run needs a command to be run, "
             "none was provided."));
      return FALSE;
    }

  if (!context->user)
    context->user = g_strdup ("root");

  if (!g_file_test (auxcommand, G_FILE_TEST_IS_EXECUTABLE))
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_HELPER,
           _("The gksu-run-helper command was not found or "
             "is not executable."));
      return FALSE;
    }

  if (!prepare_xauth (context))
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_XAUTH,
           _("Unable to copy the user's Xauthorization file."));
      return FALSE;
    }

  if (context->sn_context)
    gksu_context_launch_initiate (context);

  pid = forkpty(&fdpty, NULL, NULL, NULL);
  if (pid == 0)
    {
      gchar **cmd = g_malloc (sizeof(gchar*)*7);

      setsid();   // make us session leader
      cmd[i] = g_strdup ("/bin/su"); i++;
      if (context->login_shell)
    {
      cmd[i] = g_strdup ("-"); i++;
    }
      cmd[i] = g_strdup (context->user); i++;
      if (context->keep_env)
    {
      cmd[i] = g_strdup ("-p"); i++;
    }
      cmd[i] = g_strdup ("-c"); i++;

      /* needs to get X authorization prior to running the program */
      cmd[i] = g_strdup_printf ("%s \"%s\"", auxcommand,
                context->command); i++;

      cmd[i] = NULL;

      /* executes the command */
      if (execv (cmd[0], cmd) == -1)
    {
      fprintf (stderr,
           "Unable to run /bin/su: %s",
           strerror(errno));
    }

      for (i = 0 ; cmd[i] != NULL ; i++)
    g_free (cmd[i]);
      g_free(cmd);
    }
  else if (pid == -1)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_FORK,
           _("Failed to fork new process: %s"),
           strerror(errno));
      return FALSE;
    }
  else
    {
      fd_set rfds;

      struct timeval tv;

      struct passwd *pwd = NULL;
      gint target_uid = -1;
      gint my_uid = 0;

      gchar buf[256] = {0};
      gint status;

      gchar *password = NULL;
      gchar *cmdline = NULL;
      gboolean password_needed = FALSE;
      gboolean used_gnome_keyring = FALSE;

      my_uid = getuid();
      pwd = getpwnam (context->user);
      if (pwd)
    target_uid = pwd->pw_uid;

      if (ask_pass == NULL)
    {
      ask_pass = su_ask_password;
    }

      if (pass_not_needed == NULL)
    {
      pass_not_needed = no_pass;
    }

      /* no need to ask for password if we're already root */
      if (my_uid != target_uid && my_uid)
    {
      gint count;
      struct termios tio;

      read (fdpty, buf, 255);
      if (context->debug)
        fprintf (stderr, "gksu_context_run: buf: -%s-\n", buf);

      /* make sure we notice that ECHO is turned off, if it gets
         turned off */
      tcgetattr (fdpty, &tio);
      for (count = 0; (tio.c_lflag & ECHO) && count < 15; count++)
        {
          usleep (1000);
          tcgetattr (fdpty, &tio);
        }

      if (!(tio.c_lflag & ECHO))
        {
          gboolean prompt_grab;
          prompt_grab = gconf_client_get_bool (context->gconf_client, BASE_PATH "prompt",
                           NULL);

          if (prompt_grab)
        gksu_prompt_grab (context);

          /* try to get the password from the GNOME Keyring first */
          password = get_gnome_keyring_password (context);
          if (password == NULL)
        {
          password = ask_pass (context, buf, ask_pass_data, error);
          if (context->debug)
            {
              fprintf (stderr, "no password on keyring\n");
              if (password == NULL)
            fprintf (stderr, "no password from ask_pass!\n");
            }
        }
          else
        {
          if (context->debug)
            fprintf (stderr, "password from keyring found\n");
          used_gnome_keyring = TRUE;
        }
          if (password == NULL || (error && (*error)))
        {
          if (context->debug)
            fprintf (stderr, "gksu_su_full: problem getting password - getting out\n");
          if (context->debug && error)
            fprintf (stderr, "error: %s\n", (*error)->message);
          nullify_password (password);
          return TRUE;
        }

          write (fdpty, password, strlen(password) + 1);
          write (fdpty, "\n", 1);
          password_needed = TRUE;
        }
    }

      if (context->debug)
    fprintf (stderr, "DEBUG (run:after-pass) buf: -%s-\n", buf);
      if (strncmp (buf, "gksu", 4) && strncmp (buf, "su", 2))
    {
      /* drop the \n echoed on password entry if su did request
         a password */
      if (password_needed)
        read (fdpty, buf, 255);
      if (context->debug)
        fprintf (stderr, "DEBUG (run:post-after-pass) buf: -%s-\n", buf);
      read (fdpty, buf, 255);
      if (context->debug)
        fprintf (stderr, "DEBUG (run:post-after-pass) buf: -%s-\n", buf);
    }

      FD_ZERO (&rfds);
      FD_SET (fdpty, &rfds);
      tv.tv_sec = 1;
      tv.tv_usec = 0;
      int loop_count = 0;
      while (TRUE)
    {
      int retval = 0;

      if (!strncmp (buf, "su", 2))
        {
          gchar **strings;

          if (password)
        {
          nullify_password (password);
          unset_gnome_keyring_password (context);
        }

          strings = g_strsplit (buf, ":", 2);
          if (strings[1] && !strncmp (strings[1], " Authentication failure", 23))
        {
          if (used_gnome_keyring)
            g_set_error (error, gksu_quark,
                 GKSU_ERROR_WRONGAUTOPASS,
                 _("Wrong password got from keyring."));
          else
            g_set_error (error, gksu_quark,
                 GKSU_ERROR_WRONGPASS,
                 _("Wrong password."));
        }
          g_strfreev (strings);

          if (context->debug)
        fprintf (stderr, "DEBUG (auth_failed) buf: -%s-\n", buf);

          break;
        }
      else if (!strncmp (buf, "gksu: waiting", 13))
        {
          gchar *line;

          if (password)
        {
          set_gnome_keyring_password (context, password);
          nullify_password (password);
        }

          if (context->debug)
        fprintf (stderr, "DEBUG (gksu: waiting) buf: -%s-\n", buf);

          line = g_strdup_printf ("gksu-run: %s\n", context->display);
          write (fdpty, line, strlen(line));
          g_free (line);

          line = g_strdup_printf ("gksu-run: %s\n", context->sn_id);
          write (fdpty, line, strlen(line));
          g_free (line);

          line = g_strdup_printf ("gksu-run: %s\n", context->xauth);
          write (fdpty, line, strlen(line));
          g_free (line);

          tcdrain (fdpty);

          bzero (buf, 256);
          read (fdpty, buf, 255);

          break;
        }

      retval = select (fdpty + 1, &rfds, NULL, NULL, &tv);
      if ((loop_count > 50) || (!retval))
        {
          gchar *emsg = NULL;
          gchar *converted_str = NULL;
          GError *converr = NULL;

          if (password)
        nullify_password (password);

          converted_str = g_locale_to_utf8 (buf, -1, NULL, NULL, &converr);
          if (converr)
        {
          g_warning (_("Failed to communicate with "
                   "gksu-run-helper.\n\n"
                   "Received:\n"
                   " %s\n"
                   "While expecting:\n"
                   " %s"), buf, "gksu: waiting");
          emsg = g_strdup_printf (_("Failed to communicate with "
                        "gksu-run-helper.\n\n"
                        "Received bad string "
                        "while expecting:\n"
                        " %s"), "gksu: waiting");
          g_error_free (converr);
        }
          else
        emsg = g_strdup_printf (_("Failed to communicate with "
                      "gksu-run-helper.\n\n"
                      "Received:\n"
                      " %s\n"
                      "While expecting:\n"
                      " %s"), converted_str, "gksu: waiting");
          g_free (converted_str);

          g_set_error (error, gksu_quark, GKSU_ERROR_HELPER, emsg);
          g_free (emsg);

          if (context->debug)
        fprintf (stderr, "DEBUG (failed!) buf: -%s-\n", buf);

          return FALSE;
        }
      else if (retval == -1)
        {
          if (context->debug)
        fprintf (stderr, "DEBUG (select failed!) buf: %s\n", buf);
          return FALSE;
        }

      read (fdpty, buf, 255);
      if (context->debug)
        fprintf (stderr, "DEBUG (run:after-pass) buf: -%s-\n", buf);
      loop_count++;
    }

      if (!password_needed || used_gnome_keyring)
    {
      gboolean should_display;

      should_display = gconf_client_get_bool (context->gconf_client,
                          BASE_PATH "display-no-pass-info", NULL);

      /* configuration tells us to show this message */
      if (should_display)
        {
          if (context->debug)
        fprintf (stderr, "Calling pass_not_needed window...\n");
          pass_not_needed (context, pass_not_needed_data);
          /* make sure it is displayed */
          while (gtk_events_pending ())
        gtk_main_iteration ();
        }
    }

      cmdline = g_strdup("bin/su");
      /* wait for the child process to end or become something other
     than su */
      while ((!waitpid (pid, &status, WNOHANG)) &&
         (g_str_has_suffix(cmdline, "bin/su")))
    {
      if (cmdline)
        g_free (cmdline);
      cmdline = get_process_name (pid);
      usleep(100000);
    }

      if (context->sn_context)
    gksu_context_launch_complete (context);

      bzero(buf, 256);
      while (read (fdpty, buf, 255) > 0)
    {
      fprintf (stderr, "%s", buf);
      bzero(buf, 256);
    }

      if (!WIFEXITED(status))
    waitpid(pid, &status, 0);

      if (WEXITSTATUS(status))
    {
      if(cmdline)
        {
          /* su already exec()ed something else, don't report
           * exit status errors in that case
           */
          if (!g_str_has_suffix (cmdline, "su"))
        {
          g_free (cmdline);
          return FALSE;
        }
          g_free (cmdline);
        }

      if (error == NULL)
        g_set_error (error, gksu_quark,
             GKSU_ERROR_CHILDFAILED,
             _("su terminated with %d status"),
             WEXITSTATUS(status));
    }
    }

  if (error)
    return FALSE;

  return TRUE;
}

/**
 * gksu_su
 * @command_line: the command line that will be executed as other user
 * @error: a #GError to be set with the error condition, if an error
 * happens
 *
 * This function is a wrapper for gksu_su_run_full. It will call it
 * without giving the callback functions, which leads to the standard
 * ones being called. A simple #GksuContext is created to hold the
 * user name and the command.
 *
 * Returns: TRUE if all went well, FALSE if an error happend.
 */
gboolean
gksu_su (gchar *command_line, GError **error)
{
  GksuContext *context = gksu_context_new ();
  gboolean retval;

  context->command = g_strdup (command_line);
  context->user = g_strdup ("root");
  retval = gksu_su_full (context,
             NULL, NULL,
             NULL, NULL,
             error);
  gksu_context_free (context);
  return retval;
}

static void
read_line (int fd, gchar *buffer, int n)
{
  gint counter = 0;
  gchar tmp[2] = {0};

  for (; counter < (n - 1); counter++)
    {
      read (fd, tmp, 1);
      if (tmp[0] == '\n')
    break;
      buffer[counter] = tmp[0];
    }
  buffer[counter+1] = '\0';
}

/**
 * gksu_sudo_full:
 * @context: a #GksuContext
 * @ask_pass: a #GksuAskPassFunc to be called when the lib determines
 * requesting a password is necessary; it may be NULL, in which case
 * the standard password request dialog will be used
 * @ask_pass_data: a #gpointer with user data to be passed to the
 * #GksuAskPasswordFunc
 * @pass_not_needed: a #GksuPassNotNeededFunc that will be called
 * when the command is being run without the need for requesting
 * a password; it will only be called if the display-no-pass-info
 * gconf key is enabled; NULL will have the standard dialog be shown
 * @pass_not_needed_data: a #gpointer with the user data to be passed to the
 * #GksuPasswordNotNeededFunc
 * @error: a #GError object to be filled with the error code or NULL
 *
 * This could be considered one of the main functions in GKSu.
 * it is responsible for doing the 'user changing' magic calling
 * the #GksuAskPassFunc function to request a password if needed.
 * and the #GksuPassNotNeededFunc function if a password won't be
 * needed, so the application has the oportunity of warning the user
 * what it's doing.
 *
 * This function uses the sudo backend.
 *
 * Returns: TRUE if all went fine, FALSE if failed
 */
gboolean
gksu_sudo_full (GksuContext *context,
        GksuAskPassFunc ask_pass,
        gpointer ask_pass_data,
        GksuPassNotNeededFunc pass_not_needed,
        gpointer pass_not_needed_data,
        GError **error)
{
  char **cmd;
  char buffer[256] = {0};
  int argcount = 8;
  int i, j;

  GQuark gksu_quark;

  gchar *xauth = NULL,
    *xauth_env = NULL;

  pid_t pid;
  int status;
  FILE *infile, *outfile;
  int parent_pipe[2];    /* For talking to the parent */
  int child_pipe[2];    /* For talking to the child */

  context->sudo_mode = TRUE;

  gksu_quark = g_quark_from_string (PACKAGE);

  if (!context->command)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_NOCOMMAND,
           _("gksu_sudo_run needs a command to be run, "
             "none was provided."));
      return FALSE;
    }

  if (!context->user)
    context->user = g_strdup ("root");

  if (ask_pass == NULL)
    {
      if (context->debug)
    fprintf (stderr, "No ask_pass set, using default!\n");
      ask_pass = su_ask_password;
    }

  if (pass_not_needed == NULL)
    {
      pass_not_needed = no_pass;
    }

  /*
     FIXME: need to set GError in a more detailed way
  */
  if (!sudo_prepare_xauth (context))
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_XAUTH,
           _("Unable to copy the user's Xauthorization file."));
      return FALSE;
    }

  /* sets XAUTHORITY */
  xauth = g_strdup_printf ("%s/.Xauthority", context->dir);
  xauth_env = getenv ("XAUTHORITY");
  setenv ("XAUTHORITY", xauth, TRUE);
  if (context->debug)
    fprintf (stderr, "xauth: %s\n", xauth);

  /* set startup id */
  if (context->sn_context)
    gksu_context_launch_initiate (context);

  cmd = g_new (gchar *, argcount + 1);

  argcount = 0;

  /* sudo binary */
  cmd[argcount] = g_strdup("/usr/bin/sudo");
  argcount++;

  if (!context->keep_env)
    {
      /* Make sudo set $HOME */
      cmd[argcount] = g_strdup("-H");
      argcount++;
    }

  /* Make sudo read from stdin */
  cmd[argcount] = g_strdup("-S");
  argcount++;

  /* Make sudo use next arg as prompt */
  cmd[argcount] = g_strdup("-p");
  argcount++;

  /* prompt */
  cmd[argcount] = g_strdup("GNOME_SUDO_PASS");
  argcount++;

  /* Make sudo use the selected user */
  cmd[argcount] = g_strdup("-u");
  argcount++;

  /* user */
  cmd[argcount] = g_strdup(context->user);
  argcount++;

  /* sudo does not understand this if we do not use -H
     weird.
  */
  if (!context->keep_env)
    {
      /* Make sudo stop processing options */
      cmd[argcount] = g_strdup("--");
      argcount++;
    }

  {
    gchar *tmp_arg = g_malloc (sizeof(gchar)*1);
    gboolean inside_quotes = FALSE;

    tmp_arg[0] = '\0';

    for (i = j = 0; ; i++)
      {
    if ((context->command[i] == '\'') && (context->command[i-1] != '\\'))
      {
        i = i + 1;
        inside_quotes = !inside_quotes;
      }

    if ((context->command[i] == ' ' && inside_quotes == FALSE)
        || context->command[i] == '\0')
      {
        tmp_arg = g_realloc (tmp_arg, sizeof(gchar)*(j+1));
        tmp_arg[j] = '\0';
        cmd = g_realloc (cmd, sizeof(gchar*) * (argcount + 1));
        cmd[argcount] = g_strdup (tmp_arg);

        g_free (tmp_arg);

        argcount = argcount + 1;
        j = 0;

        if (context->command[i] == '\0')
          break;

        tmp_arg = g_malloc (sizeof(gchar)*1);
        tmp_arg[0] = '\0';
      }
    else
      {
        if (context->command[i] == '\\' && context->command[i+1] != '\\')
          i = i + 1;
        tmp_arg = g_realloc (tmp_arg, sizeof(gchar)*(j+1));
        tmp_arg[j] = context->command[i];
        j = j + 1;
      }
      }
  }
  cmd = g_realloc (cmd, sizeof(gchar*) * (argcount + 1));
  cmd[argcount] = NULL;

  if (context->debug)
    {
      for (i = 0; cmd[i] != NULL; i++)
    fprintf (stderr, "cmd[%d]: %s\n", i, cmd[i]);
    }

  if ((pipe(parent_pipe)) == -1)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_PIPE,
           _("Error creating pipe: %s"),
           strerror(errno));
      sudo_reset_xauth (context, xauth, xauth_env);
      return FALSE;
    }

  if ((pipe(child_pipe)) == -1)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_PIPE,
           _("Error creating pipe: %s"),
           strerror(errno));
      sudo_reset_xauth (context, xauth, xauth_env);
      return FALSE;
    }

  pid = fork();
  if (pid == -1)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_FORK,
           _("Failed to fork new process: %s"),
           strerror(errno));
      sudo_reset_xauth (context, xauth, xauth_env);
      return FALSE;
    }
  else if (pid == 0)
    {
      // Child
      setsid();   // make us session leader
      close(child_pipe[1]);
      dup2(child_pipe[0], STDIN_FILENO);
      dup2(parent_pipe[1], STDERR_FILENO);

      execv(cmd[0], cmd);

      g_set_error (error, gksu_quark, GKSU_ERROR_EXEC,
           _("Failed to exec new process: %s"),
           strerror(errno));
      sudo_reset_xauth (context, xauth, xauth_env);
      return FALSE;
    }
  else
    {
      gint counter = 0;
      gchar *cmdline = NULL;

      // Parent
      close(parent_pipe[1]);

      infile = fdopen(parent_pipe[0], "r");
      if (!infile)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_PIPE,
               _("Error opening pipe: %s"),
               strerror(errno));
      sudo_reset_xauth (context, xauth, xauth_env);
      return FALSE;
    }

      outfile = fdopen(child_pipe[1], "w");
      if (!outfile)
    {
      g_set_error (error, gksu_quark, GKSU_ERROR_PIPE,
               _("Error opening pipe: %s"),
               strerror(errno));
      sudo_reset_xauth (context, xauth, xauth_env);
      return FALSE;
    }

      /*
    we are expecting to receive a GNOME_SUDO_PASS
    if we don't there are two possibilities: an error
    or a password is not needed
      */
      fcntl (parent_pipe[0], F_SETFL, O_NONBLOCK);

      { /* no matter if we can read, since we're using
       O_NONBLOCK; this is just to avoid the prompt
       showing up after the read */
    fd_set rfds;
    struct timeval tv;

    FD_ZERO(&rfds);
    FD_SET(parent_pipe[0], &rfds);
    tv.tv_sec = 1;
    tv.tv_usec = 0;

    select (parent_pipe[0] + 1, &rfds, NULL, NULL, &tv);
      }

      /* Try hard to find the prompt; it may happen that we're
       * seeing sudo's lecture, or that some pam module is spitting
       * debugging stuff at the screen
       */
      for (counter = 0; counter < 50; counter++)
    {
      if (strncmp (buffer, "GNOME_SUDO_PASS", 15) == 0)
        break;

      read_line (parent_pipe[0], buffer, 256);

      if (context->debug)
        fprintf (stderr, "buffer: -%s-\n", buffer);
    }

      if (context->debug)
    fprintf (stderr, "brute force GNOME_SUDO_PASS ended...\n");

      if (strncmp(buffer, "GNOME_SUDO_PASS", 15) == 0)
    {
      gchar *password = NULL;
      gboolean prompt_grab;

      if (context->debug)
        fprintf (stderr, "Yeah, we're in...\n");

      prompt_grab = gconf_client_get_bool (context->gconf_client, BASE_PATH "prompt",
                           NULL);
      if (prompt_grab)
        gksu_prompt_grab (context);

      password = ask_pass (context, _("Password: "),
                   ask_pass_data, error);
      if (password == NULL || (*error))
        {
          nullify_password (password);
          return FALSE;
        }

      usleep (1000);

      fprintf (outfile, "%s\n", password);
      fclose (outfile);

      nullify_password (password);

      /* turn NONBLOCK off */
      fcntl(parent_pipe[0], F_SETFL, fcntl(parent_pipe[0], F_GETFL) & ~O_NONBLOCK);
      /* ignore the first newline that comes right after sudo receives
         the password */
      fgets (buffer, 255, infile);
      /* this is the status we are interessted in */
      fgets (buffer, 255, infile);
    }
      else
    {
      gboolean should_display;
      if (context->debug)
        fprintf (stderr, "No password prompt found; we'll assume we don't need a password.\n");

      should_display = gconf_client_get_bool (context->gconf_client,
                          BASE_PATH "display-no-pass-info", NULL);

      /* configuration tells us to show this message */
      if (should_display)
        {
          if (context->debug)
        fprintf (stderr, "Calling pass_not_needed window...\n");
          pass_not_needed (context, pass_not_needed_data);
          /* make sure it is displayed */
          while (gtk_events_pending ())
        gtk_main_iteration ();
        }

      fprintf (stderr, "%s", buffer);
    }

      if (!strcmp (buffer, "Sorry, try again.\n"))
    g_set_error (error, gksu_quark, GKSU_ERROR_WRONGPASS,
             _("Wrong password."));
      else if (!strncmp (buffer, "Sorry, user ", 12))
    g_set_error (error, gksu_quark, GKSU_ERROR_NOT_ALLOWED,
             _("The underlying authorization mechanism (sudo) "
               "does not allow you to run this program. Contact "
               "the system administrator."));
      else
    {
      gchar *haystack = buffer;
      gchar *needle;

      needle = g_strstr_len (haystack, strlen (haystack), " ");
      if (needle && (needle + 1))
        {
          needle += 1;
          if (!strncmp (needle, "is not in", 9))
        g_set_error (error, gksu_quark, GKSU_ERROR_NOT_ALLOWED,
                 _("The underlying authorization mechanism (sudo) "
                   "does not allow you to run this program. Contact "
                   "the system administrator."));
        }
    }

      cmdline = g_strdup("sudo");
      /* wait for the child process to end or become something other
     then sudo */
      while ((!waitpid (pid, &status, WNOHANG)) &&
         (g_str_has_suffix(cmdline, "sudo")))
    {
      if (cmdline)
        g_free (cmdline);
      cmdline = get_process_name (pid);
      usleep(100000);
    }

      if (context->sn_context)
    gksu_context_launch_complete (context);

      while (read (parent_pipe[0], buffer, 255) > 0)
    {
      fprintf (stderr, "%s", buffer);
      bzero(buffer, 256);
    }

      if (!WIFEXITED(status))
    waitpid(pid, &status, 0);
      sudo_reset_xauth (context, xauth, xauth_env);

      if (WEXITSTATUS(status))
    {
      if(cmdline)
        {
          /* sudo already exec()ed something else, don't report
           * exit status errors in that case
           */
          if (!g_str_has_suffix (cmdline, "sudo"))
        {
          g_free (cmdline);
          return FALSE;
        }
          g_free (cmdline);
        }
      if (error == NULL)
        g_set_error (error, gksu_quark,
             GKSU_ERROR_CHILDFAILED,
             _("sudo terminated with %d status"),
             WEXITSTATUS(status));
    }
    }

  /* if error is set we have found an error condition */
  if (error)
    return FALSE;

  return TRUE;
}

/**
 * gksu_sudo
 * @command_line: the command line that will be executed as other user
 * @error: a #GError to be set with the error condition, if an error
 * happens
 *
 * This function is a wrapper for gksu_sudo_run_full. It will call it
 * without giving the callback functions, which leads to the standard
 * ones being called. A simple #GksuContext is created to hold the
 * user name and the command.
 *
 * Returns: TRUE if all went well, FALSE if an error happend.
 */
gboolean
gksu_sudo (gchar *command_line,
       GError **error)
{
  GksuContext *context = gksu_context_new ();
  gboolean retval;

  context->command = g_strdup (command_line);
  context->user = g_strdup ("root");
  retval = gksu_sudo_full (context,
               NULL, NULL,
               NULL, NULL,
               error);
  gksu_context_free (context);

  return retval;
}

/**
 * gksu_run_full:
 * @context: a #GksuContext
 * @ask_pass: a #GksuAskPassFunc to be called when the lib determines
 * requesting a password is necessary; it may be NULL, in which case
 * the standard password request dialog will be used
 * @ask_pass_data: a #gpointer with user data to be passed to the
 * #GksuAskPasswordFunc
 * @pass_not_needed: a #GksuPassNotNeededFunc that will be called
 * when the command is being run without the need for requesting
 * a password; it will only be called if the display-no-pass-info
 * gconf key is enabled; NULL will have the standard dialog be shown
 * @pass_not_needed_data: a #gpointer with the user data to be passed to the
 * #GksuPasswordNotNeededFunc
 * @error: a #GError object to be filled with the error code or NULL
 *
 * This function is a wrapper for gksu_sudo_full/gksu_su_full. It will
 * call one of them, depending on the GConf key that defines whether
 * the default behavior for gksu is su or sudo mode. This is the
 * recommended way of using the library functionality.
 *
 * Returns: TRUE if all went fine, FALSE if failed
 */
gboolean
gksu_run_full (GksuContext *context,
           GksuAskPassFunc ask_pass,
           gpointer ask_pass_data,
           GksuPassNotNeededFunc pass_not_needed,
           gpointer pass_not_needed_data,
           GError **error)
{
  GConfClient *gconf_client;
  gboolean sudo_mode;

  gconf_client = gconf_client_get_default ();
  sudo_mode = gconf_client_get_bool (gconf_client, BASE_PATH "sudo-mode",
                     NULL);
  g_object_unref (gconf_client);

  if (sudo_mode)
    return gksu_sudo_full (context, ask_pass, ask_pass_data,
               pass_not_needed, pass_not_needed_data,
               error);

  return gksu_su_full (context, ask_pass, ask_pass_data,
               pass_not_needed, pass_not_needed_data,
               error);
}

/**
 * gksu_run
 * @command_line: the command line that will be executed as other user
 * @error: a #GError to be set with the error condition, if an error
 * happens
 *
 * This function is a wrapper for gksu_sudo/gksu_su. It will call one
 * of them, depending on the GConf key that defines whether the default
 * behavior for gksu is su or sudo mode. This is the recommended way of
 * using the library functionality.
 *
 * Returns: FALSE if all went well, TRUE if an error happend.
 */
gboolean
gksu_run (gchar *command_line,
      GError **error)
{
  GConfClient *gconf_client;
  gboolean sudo_mode;

  gconf_client = gconf_client_get_default ();
  sudo_mode = gconf_client_get_bool (gconf_client, BASE_PATH "sudo-mode",
                     NULL);
  g_object_unref (gconf_client);

  if (sudo_mode)
    return gksu_sudo (command_line, error);

  return gksu_su (command_line, error);
}

/**
 * gksu_ask_password_full:
 * @context: a #GksuContext
 * @prompt: a prompt different from Password:
 * @error: a #GError object to be filled with the error code or NULL
 *
 * This function uses the gksu infra-structure to request for a
 * password, but instead of passing it to su or sudo to run a command
 * it simply returns the password.
 *
 * Returns: a newly allocated string with the password;
 */
gchar*
gksu_ask_password_full (GksuContext *context, gchar *prompt,
            GError **error)
{
  gchar *ret_value = su_ask_password (context, _(prompt), NULL, error);
  if (context->sn_context)
    gksu_context_launch_complete (context);
  return ret_value;
}

/**
 * gksu_ask_password
 * @error: a #GError to be set with the error condition, if an error
 * happens
 *
 * This function uses the gksu infra-structure to request for a
 * password, but instead of passing it to su or sudo to run a command
 * it simply returns the password. This is just a convenience wrapper
 * for gksu_ask_password_full.
 *
 * Returns: a newly allocated string with the password;
 */
gchar*
gksu_ask_password (GError **error)
{
  GksuContext *context = gksu_context_new ();
  gchar* retval;

  context->user = g_strdup ("root");
  retval = gksu_ask_password_full (context, NULL, error);
  gksu_context_free (context);

  return retval;
}
Спасибо сказали:
Ism
Сообщения: 1261
Статус: Никто, по сути быдло

Re: Решено: Не завершать потомка при завершении родителя

Сообщение Ism »

хм только что заметил функцию Setsid http://www.opennet.ru/man.shtml?topic=sets...2&russian=0

походу в ней секрет, надо попробовать
Спасибо сказали:
Ism
Сообщения: 1261
Статус: Никто, по сути быдло

Re: Решено: Не завершать потомка при завершении родителя

Сообщение Ism »

странно в opensuse Setsid срабатывает, а в ubuntu тотже код ведет себя по другому, то есть процесс судя по системному монитору остается связанным в иерархии
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Решено: Не завершать потомка при завершении родителя

Сообщение drBatty »

Ism писал(а):
02.10.2011 01:52
Никак не могу добиться, чтоб форкнутый потомок продолжал работать после завершения запустившего его приложения (весь гугл облазил, но именно данного функционала примеров не нашел)

здесь обсуждалось, вроде-бы с год назад. И примеры были рабочие. Только с ходу что-то не найти...
А в мане нашёл такое:
* The prctl(2) PR_SET_PDEATHSIG setting is reset so that the child does not receive a signal when its parent terminates.

PR_SET_PDEATHSIG (since Linux 2.1.57)
Set the parent process death signal of the calling process to arg2 (either a signal value in the range 1..maxsig, or 0 to clear). This is the
signal that the calling process will get when its parent dies. This value is cleared for the child of a fork(2).

т.е. вы можете сменить сигнал посылаемый родителем ребёнку, и даже вообще отменить этот сигнал.
Как я понял, это и нужно.

ЗЫЖ функция prctl вызывается ребёнком, который хочет жить после смерти родителя.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Решено: Не завершать потомка при завершении родителя

Сообщение drBatty »

Ism писал(а):
06.10.2011 23:13
хм только что заметил функцию Setsid

это что-то не то ИМХО. там создаётся сессия. если процесс не является лидером группы процессов.
короче что-то непонятное (непонятно мне, что с этой функцией делать)
DESCRIPTION
setsid() creates a new session if the calling process is not a process group leader. The calling process is the leader of the new session, the
process group leader of the new process group, and has no controlling tty. The process group ID and session ID of the calling process are set to the
PID of the calling process. The calling process will be the only process in this new process group and in this new session.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Ism
Сообщения: 1261
Статус: Никто, по сути быдло

Re: Решено: Не завершать потомка при завершении родителя

Сообщение Ism »

Все гениальное просто, в исходниках nohup обнаружилась маленькая строчка

signal (SIGHUP, SIG_IGN);


достаточно эту команду перед запуском команды в псевдотерминале выполнить и сигнал завершения будет заблокирован

в общем смотрите исходники xroot
Спасибо сказали: