From 0ff211f520c18550454289e7265061d7e8ac41c0 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 16 Mar 2011 13:54:28 -0400 Subject: [PATCH] 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(-) create mode 100644 glib/glib-unix.c create mode 100644 glib/glib-unix.h create mode 100644 glib/tests/unix.c 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 +
+UNIX Compatibility Functions +gunix +G_UNIX_ERROR +g_unix_pipe_flags + +
+ # Data Structures
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 + */ + +#include "config.h" + +#include "glib-unix.h" + +#include + +/** + * 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 +#endif +#include +#include +#include +#include +#ifdef _G_GNU_SOURCE_TEMPORARILY_DEFINED +#undef _GNU_SOURCE +#undef _G_GNU_SOURCE_TEMPORARILY_DEFINED +#endif + +#include + +/** + * 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 #include #include @@ -84,11 +88,6 @@ #include #endif /* G_OS_BEOS */ -#ifdef G_OS_UNIX -#include -#include -#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 + */ + +#include "config.h" + +#include "glib-unix.h" +#include + +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(); +} -- 2.7.4