[glib] glib-unix: New Unix-specific API



commit 0ff211f520c18550454289e7265061d7e8ac41c0
Author: Colin Walters <walters verbum org>
Date:   Wed Mar 16 13:54:28 2011 -0400

    glib-unix: New Unix-specific API
    
    GLib historically has been designed to be "mostly" portable; there
    are some functions only available on Unix like g_io_channel_unix_new(),
    but these are typically paired with obvious counterparts for Win32.
    
    However, as GLib is used not only by portable software, but components
    targeting Unix (or even just Linux), there are a few cases where it
    would be very convenient if GLib shipped built-in functionality.
    
    This initial patch is a basic wrapper around pipe2(), including
    fallbacks for older kernels.  This pairs well with the
    existing g_spawn_*() API and its child_setup functionality.
    
    However, in the future, I want to add a signal() wrapper here,
    complete with proxying the signal to a mainloop.  I have initial code
    for this, but doing it sanely (including factoring out gmain.c's
    private worker thread), is a complex task, and I don't want to block
    on that.
    
    See also gwin32.h for Win32 specific functionality.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=644941

 docs/reference/glib/glib-sections.txt |    8 ++
 glib/Makefile.am                      |    7 ++
 glib/glib-unix.c                      |  135 +++++++++++++++++++++++++++++++++
 glib/glib-unix.h                      |   70 +++++++++++++++++
 glib/glib.symbols                     |    9 ++
 glib/gmain.c                          |   30 +++-----
 glib/tests/Makefile.am                |    3 +
 glib/tests/unix.c                     |   60 +++++++++++++++
 8 files changed, 302 insertions(+), 20 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 037f577..9873393 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -1924,6 +1924,14 @@ g_win32_ftruncate
 
 </SECTION>
 
+<SECTION>
+<TITLE>UNIX Compatibility Functions</TITLE>
+<FILE>gunix</FILE>
+G_UNIX_ERROR
+g_unix_pipe_flags
+
+</SECTION>
+
 # Data Structures
 
 <SECTION>
diff --git a/glib/Makefile.am b/glib/Makefile.am
index 6c53e17..ba76729 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -206,6 +206,9 @@ libglib_2_0_la_SOURCES = 	\
 	gprintf.c		\
 	gprintfint.h
 
+if OS_UNIX
+libglib_2_0_la_SOURCES += glib-unix.c
+endif	
 
 EXTRA_libglib_2_0_la_SOURCES = \
 	giounix.c	\
@@ -219,6 +222,10 @@ glibinclude_HEADERS =   \
 	glib-object.h	\
 	glib.h
 
+if OS_UNIX
+glibinclude_HEADERS += glib-unix.h
+endif
+
 glibsubincludedir=$(includedir)/glib-2.0/glib
 glibsubinclude_HEADERS =   \
 	galloca.h	\
diff --git a/glib/glib-unix.c b/glib/glib-unix.c
new file mode 100644
index 0000000..4006a0d
--- /dev/null
+++ b/glib/glib-unix.c
@@ -0,0 +1,135 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * glib-unix.c: Unix specific API wrappers and convenience functions 
+ *
+ * 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.
+ *
+ * Authors: Colin Walters <walters verbum org>
+ */
+
+#include "config.h"
+
+#include "glib-unix.h"
+
+#include <string.h>
+
+/**
+ * SECTION:gunix
+ * @short_description: Unix-specific utilities and integration
+ * @include: glib-unix.h
+ *
+ * Most of GLib is intended to be portable; in constrast, this set of
+ * functions is designed for programs which explicitly target UNIX, or
+ * are using it to build higher level abstractions which would be
+ * conditionally compiled if the platform matches G_OS_UNIX.
+ *
+ * To use these functions, you must explicitly include the
+ * "glib-unix.h" header.
+ */
+
+GQuark
+g_unix_error_quark (void)
+{
+  return g_quark_from_static_string ("g-unix-error-quark");
+}
+
+static gboolean
+g_unix_set_error_from_errno (GError **error)
+{
+  int saved_errno = errno;
+  g_set_error_literal (error,
+		       G_UNIX_ERROR,
+		       0,
+		       g_strerror (errno));
+  errno = saved_errno;
+  return FALSE;
+}
+
+static gboolean
+g_unix_set_error_from_errno_saved (GError **error,
+				    int      saved_errno)
+{
+  g_set_error_literal (error,
+		       G_UNIX_ERROR,
+		       0,
+		       g_strerror (saved_errno));
+  errno = saved_errno;
+  return FALSE;
+}
+
+/**
+ * g_unix_pipe_flags:
+ * @fds: Array of two integers
+ * @flags: Bitfield of file descriptor flags, see "man 2 fcntl"
+ * @error: a #GError
+ *
+ * Similar to the Unix pipe() call, but on modern systems like
+ * Linux uses the pipe2 system call, which atomically creates
+ * a pipe with the configured flags.  The only supported flag
+ * currently is FD_CLOEXEC.  If for example you want to configure
+ * O_NONBLOCK, that must still be done separately with fcntl().
+ *
+ * Note in particular this function does *not* take O_CLOEXEC, it
+ * takes FD_CLOEXEC as if for fcntl(); these are different on
+ * Linux/glibc.
+ *
+ * Returns: %TRUE on success, %FALSE if not (and errno will be set).
+ *
+ * Since: 2.30
+ */
+gboolean
+g_unix_pipe_flags (int     *fds,
+		   int      flags,
+		   GError **error)
+{
+  int ecode;
+
+  /* We only support FD_CLOEXEC */
+  g_return_val_if_fail ((flags & (FD_CLOEXEC)) == flags, FALSE);
+
+#ifdef HAVE_PIPE2
+  {
+    int pipe2_flags = 0;
+    if (flags & FD_CLOEXEC)
+      pipe2_flags |= O_CLOEXEC;
+    /* Atomic */
+    ecode = pipe2 (fds, pipe2_flags);
+    if (ecode == -1 && errno != ENOSYS)
+      return g_unix_set_error_from_errno (error);
+    /* Fall through on -ENOSYS, we must be running on an old kernel */
+  }
+#endif
+  ecode = pipe (fds);
+  if (ecode == -1)
+    return g_unix_set_error_from_errno (error);
+  ecode = fcntl (fds[0], flags);
+  if (ecode == -1)
+    {
+      int saved_errno = errno;
+      close (fds[0]);
+      return g_unix_set_error_from_errno_saved (error, saved_errno);
+    }
+  ecode = fcntl (fds[0], flags);
+  if (ecode == -1)
+    {
+      int saved_errno = errno;
+      close (fds[0]);
+      close (fds[1]);
+      return g_unix_set_error_from_errno_saved (error, saved_errno);
+    }
+  return TRUE;
+}
diff --git a/glib/glib-unix.h b/glib/glib-unix.h
new file mode 100644
index 0000000..f2f784d
--- /dev/null
+++ b/glib/glib-unix.h
@@ -0,0 +1,70 @@
+/* glib-unix.h - Unix specific integration
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+
+#ifndef __G_UNIX_H__
+#define __G_UNIX_H__
+
+/* We need to include the UNIX headers needed to use the APIs below,
+ * but we also take this opportunity to include a wide selection of
+ * other UNIX headers.  If one of the headers below is broken on some
+ * system, work around it here (or better, fix the system or tell
+ * people to use a better one).
+ */
+#ifndef _GNU_SOURCE
+#define _G_GNU_SOURCE_TEMPORARILY_DEFINED
+#define _GNU_SOURCE
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#ifdef _G_GNU_SOURCE_TEMPORARILY_DEFINED
+#undef _GNU_SOURCE
+#undef _G_GNU_SOURCE_TEMPORARILY_DEFINED
+#endif
+
+#include <glib.h>
+
+/**
+ * G_UNIX_ERROR:
+ * 
+ * Error domain for API in the "g_unix_" namespace.  Note that there
+ * is no exported enumeration mapping "errno".  Instead, all functions
+ * ensure that "errno" is relevant.  The code for all G_UNIX_ERROR is
+ * always 0, and the error message is always generated via
+ * g_strerror().
+ *
+ * It is expected that most code will not look at "errno" from these
+ * APIs. Important cases where one would want to differentiate between
+ * errors are already covered by existing cross-platform GLib API,
+ * such as e.g. GFile wrapping "ENOENT".  However, it is provided for
+ * completeness, at least.
+ */
+#define G_UNIX_ERROR (g_unix_error_quark())
+
+GQuark g_unix_error_quark (void);
+
+gboolean g_unix_pipe_flags (int      *fds,
+			    int       flags,
+			    GError  **error);
+
+#endif
diff --git a/glib/glib.symbols b/glib/glib.symbols
index 6c86667..be3acfa 100644
--- a/glib/glib.symbols
+++ b/glib/glib.symbols
@@ -1978,6 +1978,15 @@ g_hostname_to_unicode
 #endif
 #endif
 
+#if IN_HEADER(__G_UNIX_H__)
+#if IN_FILE(__G_UNIX_C__)
+#ifdef G_OS_UNIX
+g_unix_pipe_flags
+g_unix_error_quark
+#endif
+#endif
+#endif
+
 #ifdef INCLUDE_VARIABLES
 g_ascii_table
 g_utf8_skip
diff --git a/glib/gmain.c b/glib/gmain.c
index 718c36c..c045e1c 100644
--- a/glib/gmain.c
+++ b/glib/gmain.c
@@ -62,6 +62,10 @@
 #define G_MAIN_POLL_DEBUG
 #endif
 
+#ifdef G_OS_UNIX
+#include "glib-unix.h"
+#endif
+
 #include <signal.h>
 #include <sys/types.h>
 #include <time.h>
@@ -84,11 +88,6 @@
 #include <sys/wait.h>
 #endif /* G_OS_BEOS */
 
-#ifdef G_OS_UNIX
-#include <fcntl.h>
-#include <sys/wait.h>
-#endif
-
 #include "gmain.h"
 
 #include "garray.h"
@@ -515,23 +514,14 @@ g_main_context_unref (GMainContext *context)
 static void 
 g_main_context_init_pipe (GMainContext *context)
 {
+  GError *error = NULL;
+
 # ifndef G_OS_WIN32
   if (context->wake_up_pipe[0] != -1)
     return;
 
-#ifdef HAVE_PIPE2
-  /* if this fails, we fall through and try pipe */
-  pipe2 (context->wake_up_pipe, O_CLOEXEC);
-#endif
-  if (context->wake_up_pipe[0] == -1)
-    {
-      if (pipe (context->wake_up_pipe) < 0)
-        g_error ("Cannot create pipe main loop wake-up: %s\n",
-  	         g_strerror (errno));
- 
-      fcntl (context->wake_up_pipe[0], F_SETFD, FD_CLOEXEC);
-      fcntl (context->wake_up_pipe[1], F_SETFD, FD_CLOEXEC);
-    }
+  if (!g_unix_pipe_flags (context->wake_up_pipe, FD_CLOEXEC, &error))
+    g_error ("Cannot create pipe main loop wake-up: %s", error->message);
 
   context->wake_up_rec.fd = context->wake_up_pipe[0];
   context->wake_up_rec.events = G_IO_IN;
@@ -4334,8 +4324,8 @@ g_child_watch_source_init_multi_threaded (void)
 
   g_assert (g_thread_supported());
 
-  if (pipe (child_watch_wake_up_pipe) < 0)
-    g_error ("Cannot create wake up pipe: %s\n", g_strerror (errno));
+  if (!g_unix_pipe_flags (child_watch_wake_up_pipe, FD_CLOEXEC, &error))
+    g_error ("Cannot create wake up pipe: %s\n", error->message);
   fcntl (child_watch_wake_up_pipe[1], F_SETFL, O_NONBLOCK | fcntl (child_watch_wake_up_pipe[1], F_GETFL));
 
   /* We create a helper thread that polls on the wakeup pipe indefinitely */
diff --git a/glib/tests/Makefile.am b/glib/tests/Makefile.am
index ea7da1f..ca42eb4 100644
--- a/glib/tests/Makefile.am
+++ b/glib/tests/Makefile.am
@@ -170,6 +170,9 @@ sort_LDADD     = $(progs_ldadd)
 
 if OS_UNIX
 
+TEST_PROGS    += unix
+unix_LDADD  = $(progs_ldadd)
+
 # some testing of gtester funcitonality
 XMLLINT=xmllint
 gtester-xmllint-check: # check testreport xml with xmllint if present
diff --git a/glib/tests/unix.c b/glib/tests/unix.c
new file mode 100644
index 0000000..2dc2364
--- /dev/null
+++ b/glib/tests/unix.c
@@ -0,0 +1,60 @@
+/* 
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * Author: Colin Walters <walters verbum org> 
+ */
+
+#include "config.h"
+
+#include "glib-unix.h"
+#include <string.h>
+
+static void
+test_pipe (void)
+{
+  GError *error = NULL;
+  int pipefd[2];
+  char buf[1024];
+  ssize_t bytes_read;
+
+  g_unix_pipe_flags (pipefd, FD_CLOEXEC, NULL);
+  g_assert_no_error (error);
+    
+  write (pipefd[1], "hello", sizeof ("hello"));
+  memset (buf, 0, sizeof (buf));
+  bytes_read = read (pipefd[0], buf, sizeof(buf) - 1);
+  g_assert_cmpint (bytes_read, >, 0);
+  
+  close (pipefd[0]);
+  close (pipefd[1]);
+
+  g_assert (g_str_has_prefix (buf, "hello"));
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/glib-unix/pipe", test_pipe);
+
+  return g_test_run();
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]