/* 
 * Copyright (C) 2012 Colin Walters <walters@verbum.org>
 *
 * This work is provided "as is"; redistribution and modification
 * in whole or in part, in any medium, physical or electronic is
 * permitted without restriction.
 *
 * This work 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.
 *
 * In no event shall the authors or contributors be liable for any
 * direct, indirect, incidental, special, exemplary, or consequential
 * damages (including, but not limited to, procurement of substitute
 * goods or services; loss of use, data, or profits; or business
 * interruption) however caused and on any theory of liability, whether
 * in contract, strict liability, or tort (including negligence or
 * otherwise) arising in any way out of the use of this software, even
 * if advised of the possibility of such damage.
 */

#include <unistd.h>
#include <stdlib.h>
#include <sys/prctl.h>
#ifndef PR_SET_CHILD_SUBREAPER
#define PR_SET_CHILD_SUBREAPER	36
#endif

#include <glib-unix.h>
#include <gio/gio.h>

static void
on_catchall_child (GPid      pid,
                   gint      exit_status,
                   gpointer  user_data)
{
  GError *local_error = NULL;
  GError **error = &local_error;
  
  if (!g_spawn_check_exit_status (exit_status, error))
    goto out;

  g_print ("Child %lu exited successfully\n", (gulong) pid);

 out:
  if (local_error)
    {
      g_printerr ("%s\n", local_error->message);
      g_clear_error (&local_error);
    }
  g_main_loop_quit (user_data);
}

int
main (int   argc,
      char *argv[])
{
  gboolean ret = FALSE;
  GError *local_error = NULL;
  GError **error = &local_error;
  pid_t pid;
  int estatus;
  GMainLoop *loop = NULL;

  if (prctl (PR_SET_CHILD_SUBREAPER, 1) < 0)
    {
      g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno),
                           "Failed to set PR_SET_CHILD_SUBREAPER");
      goto out;
    }

  if ((pid = fork ()) < 0)
    {
      g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno),
                           "Failed to fork");
      goto out;
    }
  else if (pid > 0)
    {
      /* Parent - continue */
    }
  else
    {
      pid_t child_pid;
      if ((child_pid = fork ()) < 0)
        {
          g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno),
                               "Failed to fork");
          goto out;
        }
      if (child_pid != 0)
        exit (0);
      if (execlp ("sleep", "sleep", "60", NULL) < 0)
        {
          g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno),
                               "Failed to execute sleep");
          goto out;
        }
    }
  
  if (waitpid (pid, &estatus, 0) < 0)
    {
      g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno),
                           "Failed to waitpid");
      goto out;
    }

  if (!g_spawn_check_exit_status (estatus, error))
    goto out;

  loop = g_main_loop_new (NULL, TRUE);

  {
    GSource *catchall_source = NULL;
    catchall_source = g_unix_catchall_child_source_new ();
    g_source_set_callback (catchall_source, (GSourceFunc) on_catchall_child, loop, NULL);
    g_source_attach (catchall_source, NULL);
    g_source_unref (catchall_source);
  }
  
  g_main_loop_run (loop);
  
  ret = TRUE;
 out:
  g_clear_pointer (&loop, (GDestroyNotify) g_main_loop_unref);
  if (local_error)
    {
      g_printerr ("%s\n", local_error->message);
      g_clear_error (&local_error);
    }
  
  return ret ? 0 : 1;
}
