#include "gskfork.h"
#include "gskerrno.h"
#include "gskerror.h"
#include "gskmacros.h"
#include "gskmainloop.h"
#include <unistd.h>
#include <errno.h>
static GHashTable *fd_table = NULL;
G_LOCK_DEFINE_STATIC (fd_table);
/**
* gsk_fork:
* @action: function to call from the background process.
* @data: user-data to pass to @action.
* @error: optional place to errors to be put.
*
* Fork a new thread, cleaning up loose file-descriptors,
* then running the background function,
* and exiting with the return value of the background function.
*
* returns: the process-id of the background thread,
* or -1 on error.
*/
gint
gsk_fork (GskForkFunc action,
gpointer data,
GError **error)
{
int rv = fork ();
if (rv < 0)
{
int e = errno;
if (!gsk_errno_is_ignorable (e))
g_set_error (error, GSK_G_ERROR_DOMAIN,
gsk_error_code_from_errno (e),
_("couldn't fork: %s"),
g_strerror (e));
}
else if (rv == 0)
{
gsk_fork_child_cleanup ();
_exit (action (data));
}
return rv;
}
/**
* gsk_fork_add_cleanup_fd:
* @fd: a file descriptor that should be closed from a background thread.
*
* Add a file-descriptor to the list that should be closed
* when fork() is run.
*
* A file-descriptor must only be added once.
*/
void
gsk_fork_add_cleanup_fd (int fd)
{
G_LOCK (fd_table);
if (fd_table == NULL)
fd_table = g_hash_table_new (NULL, NULL);
#if ENABLE_CHECKS
if (g_hash_table_lookup (fd_table, GUINT_TO_POINTER (fd)) != NULL)
g_warning ("gsk_fork_add_cleanup_fd: fd %d added twice", fd);
#endif
g_hash_table_insert (fd_table, GUINT_TO_POINTER (fd), GUINT_TO_POINTER (1));
G_UNLOCK (fd_table);
}
/**
* gsk_fork_remove_cleanup_fd:
* @fd: remove a file descriptor that was previously registered
* to be closed on fork with gsk_fork_add_cleanup_fd().
*
* Remove a file-descriptor from the list that should be closed
* when fork() is run.
*/
void
gsk_fork_remove_cleanup_fd (int fd)
{
G_LOCK (fd_table);
#if ENABLE_CHECKS
if (fd_table == NULL
|| g_hash_table_lookup (fd_table, GUINT_TO_POINTER (fd)) == NULL)
g_warning ("gsk_fork_remove_cleanup_fd: fd %d removed spuriously", fd);
#endif
g_hash_table_remove (fd_table, GUINT_TO_POINTER (fd));
G_UNLOCK (fd_table);
}
static void close_fd_as_ptr (gpointer fd_ptr)
{
int fd = GPOINTER_TO_UINT (fd_ptr);
close (fd);
}
/**
* gsk_fork_child_cleanup:
*
* Do all cleanup that should follow a fork(2) system call
* from the child process.
*/
void
gsk_fork_child_cleanup(void)
{
if (fd_table != NULL)
{
g_hash_table_foreach (fd_table, (GHFunc) close_fd_as_ptr, NULL);
g_hash_table_destroy (fd_table);
fd_table = NULL;
}
_gsk_main_loop_fork_notify ();
}
syntax highlighted by Code2HTML, v. 0.9.1