summaryrefslogtreecommitdiff
path: root/subprojects/gstreamer/libs/gst/helpers/gst-ptp-helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/gstreamer/libs/gst/helpers/gst-ptp-helper.c')
-rw-r--r--subprojects/gstreamer/libs/gst/helpers/gst-ptp-helper.c689
1 files changed, 689 insertions, 0 deletions
diff --git a/subprojects/gstreamer/libs/gst/helpers/gst-ptp-helper.c b/subprojects/gstreamer/libs/gst/helpers/gst-ptp-helper.c
new file mode 100644
index 0000000000..91a2570fac
--- /dev/null
+++ b/subprojects/gstreamer/libs/gst/helpers/gst-ptp-helper.c
@@ -0,0 +1,689 @@
+/* GStreamer
+ * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com>
+ *
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/* Helper process that runs setuid root or with appropriate privileges to
+ * listen on ports < 1024, do multicast operations and get MAC addresses of
+ * interfaces. Privileges are dropped after these operations are done.
+ *
+ * It listens on the PTP multicast group on port 319 and 320 and forwards
+ * everything received there to stdout, while forwarding everything received
+ * on stdout to those sockets.
+ * Additionally it provides the MAC address of a network interface via stdout
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <string.h>
+
+#ifdef HAVE_GETIFADDRS_AF_LINK
+#include <ifaddrs.h>
+#include <net/if_dl.h>
+#endif
+
+#ifdef HAVE_PTP_HELPER_SETUID
+#include <grp.h>
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_PTP_HELPER_CAPABILITIES
+#include <sys/capability.h>
+#endif
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <gst/gst.h>
+#include <gst/net/gstptp_private.h>
+
+#define PTP_MULTICAST_GROUP "224.0.1.129"
+#define PTP_EVENT_PORT 319
+#define PTP_GENERAL_PORT 320
+
+static gchar **ifaces = NULL;
+static gboolean verbose = FALSE;
+static guint64 clock_id = (guint64) - 1;
+static guint8 clock_id_array[8];
+
+static GOptionEntry opt_entries[] = {
+ {"interface", 'i', 0, G_OPTION_ARG_STRING_ARRAY, &ifaces,
+ "Interface to listen on", NULL},
+ {"clock-id", 'c', 0, G_OPTION_ARG_INT64, &clock_id,
+ "PTP clock id", NULL},
+ {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
+ "Be verbose", NULL},
+ {NULL}
+};
+
+static GSocketAddress *event_saddr, *general_saddr;
+static GSocket *socket_event, *socket_general;
+static GIOChannel *stdin_channel, *stdout_channel;
+
+static gboolean
+have_socket_data_cb (GSocket * socket, GIOCondition condition,
+ gpointer user_data)
+{
+ gchar buffer[8192];
+ gssize read;
+ gsize written;
+ GError *err = NULL;
+ GIOStatus status;
+ StdIOHeader header = { 0, };
+
+ read = g_socket_receive (socket, buffer, sizeof (buffer), NULL, &err);
+ if (read == -1)
+ g_error ("Failed to read from socket: %s", err->message);
+ g_clear_error (&err);
+
+ if (verbose)
+ g_message ("Received %" G_GSSIZE_FORMAT " bytes from %s socket", read,
+ (socket == socket_event ? "event" : "general"));
+
+ header.size = read;
+ header.type = (socket == socket_event) ? TYPE_EVENT : TYPE_GENERAL;
+
+ status =
+ g_io_channel_write_chars (stdout_channel, (gchar *) & header,
+ sizeof (header), &written, &err);
+ if (status == G_IO_STATUS_ERROR) {
+ g_error ("Failed to write to stdout: %s", err->message);
+ g_clear_error (&err);
+ } else if (status == G_IO_STATUS_EOF) {
+ g_message ("EOF on stdout");
+ exit (0);
+ } else if (status != G_IO_STATUS_NORMAL) {
+ g_error ("Unexpected stdout write status: %d", status);
+ } else if (written != sizeof (header)) {
+ g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written);
+ }
+
+ status =
+ g_io_channel_write_chars (stdout_channel, buffer, read, &written, &err);
+ if (status == G_IO_STATUS_ERROR) {
+ g_error ("Failed to write to stdout: %s", err->message);
+ g_clear_error (&err);
+ } else if (status == G_IO_STATUS_EOF) {
+ g_message ("EOF on stdout");
+ exit (0);
+ } else if (status != G_IO_STATUS_NORMAL) {
+ g_error ("Unexpected stdout write status: %d", status);
+ } else if (written != read) {
+ g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written);
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+have_stdin_data_cb (GIOChannel * channel, GIOCondition condition,
+ gpointer user_data)
+{
+ GIOStatus status;
+ StdIOHeader header = { 0, };
+ gchar buffer[8192];
+ GError *err = NULL;
+ gsize read;
+ gssize written;
+
+ if ((condition & G_IO_STATUS_EOF)) {
+ g_message ("EOF on stdin");
+ exit (0);
+ }
+
+ status =
+ g_io_channel_read_chars (channel, (gchar *) & header, sizeof (header),
+ &read, &err);
+ if (status == G_IO_STATUS_ERROR) {
+ g_error ("Failed to read from stdin: %s", err->message);
+ g_clear_error (&err);
+ } else if (status == G_IO_STATUS_EOF) {
+ g_message ("EOF on stdin");
+ exit (0);
+ } else if (status != G_IO_STATUS_NORMAL) {
+ g_error ("Unexpected stdin read status: %d", status);
+ } else if (read != sizeof (header)) {
+ g_error ("Unexpected read size: %" G_GSIZE_FORMAT, read);
+ } else if (header.size > 8192) {
+ g_error ("Unexpected size: %u", header.size);
+ }
+
+ status = g_io_channel_read_chars (channel, buffer, header.size, &read, &err);
+ if (status == G_IO_STATUS_ERROR) {
+ g_error ("Failed to read from stdin: %s", err->message);
+ g_clear_error (&err);
+ } else if (status == G_IO_STATUS_EOF) {
+ g_message ("EOF on stdin");
+ exit (0);
+ } else if (status != G_IO_STATUS_NORMAL) {
+ g_error ("Unexpected stdin read status: %d", status);
+ } else if (read != header.size) {
+ g_error ("Unexpected read size: %" G_GSIZE_FORMAT, read);
+ }
+
+ switch (header.type) {
+ case TYPE_EVENT:
+ case TYPE_GENERAL:
+ written =
+ g_socket_send_to (header.type ==
+ TYPE_EVENT ? socket_event : socket_general,
+ (header.type == TYPE_EVENT ? event_saddr : general_saddr), buffer,
+ header.size, NULL, &err);
+ if (written == -1)
+ g_error ("Failed to write to socket: %s", err->message);
+ else if (written != header.size)
+ g_error ("Unexpected write size: %" G_GSSIZE_FORMAT, written);
+ g_clear_error (&err);
+ if (verbose)
+ g_message ("Sent %" G_GSSIZE_FORMAT " bytes to %s socket", read,
+ (header.type == TYPE_EVENT ? "event" : "general"));
+ break;
+ default:
+ break;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+setup_sockets (void)
+{
+ GInetAddress *bind_addr, *mcast_addr;
+ GSocketAddress *bind_saddr;
+ GSource *socket_event_source, *socket_general_source;
+ gchar **probed_ifaces = NULL;
+ GError *err = NULL;
+
+ /* Create sockets */
+ socket_event =
+ g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
+ G_SOCKET_PROTOCOL_UDP, &err);
+ if (!socket_event)
+ g_error ("Couldn't create event socket: %s", err->message);
+ g_clear_error (&err);
+ g_socket_set_multicast_loopback (socket_event, FALSE);
+
+ socket_general =
+ g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
+ G_SOCKET_PROTOCOL_UDP, &err);
+ if (!socket_general)
+ g_error ("Couldn't create general socket: %s", err->message);
+ g_clear_error (&err);
+ g_socket_set_multicast_loopback (socket_general, FALSE);
+
+ /* Bind sockets */
+ bind_addr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
+ bind_saddr = g_inet_socket_address_new (bind_addr, PTP_EVENT_PORT);
+ if (!g_socket_bind (socket_event, bind_saddr, TRUE, &err))
+ g_error ("Couldn't bind event socket: %s", err->message);
+ g_object_unref (bind_saddr);
+ bind_saddr = g_inet_socket_address_new (bind_addr, PTP_GENERAL_PORT);
+ if (!g_socket_bind (socket_general, bind_saddr, TRUE, &err))
+ g_error ("Couldn't bind general socket: %s", err->message);
+ g_object_unref (bind_saddr);
+ g_object_unref (bind_addr);
+
+ /* Probe all non-loopback interfaces */
+ if (!ifaces) {
+#if defined(HAVE_SIOCGIFCONF_SIOCGIFFLAGS_SIOCGIFHWADDR)
+ struct ifreq ifr;
+ struct ifconf ifc;
+ gchar buf[8192];
+
+ ifc.ifc_len = sizeof (buf);
+ ifc.ifc_buf = buf;
+ if (ioctl (g_socket_get_fd (socket_event), SIOCGIFCONF, &ifc) != -1) {
+ guint i, idx = 0;
+
+ probed_ifaces = g_new0 (gchar *, ifc.ifc_len + 1);
+
+ for (i = 0; i < ifc.ifc_len / sizeof (struct ifreq); i++) {
+ strncpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name, IFNAMSIZ);
+ if (ioctl (g_socket_get_fd (socket_event), SIOCGIFFLAGS, &ifr) == 0) {
+ if ((ifr.ifr_flags & IFF_LOOPBACK))
+ continue;
+ probed_ifaces[idx] = g_strndup (ifc.ifc_req[i].ifr_name, IFNAMSIZ);
+ idx++;
+ } else {
+ g_warning ("can't get flags of interface '%s'",
+ ifc.ifc_req[i].ifr_name);
+ probed_ifaces[idx] = g_strndup (ifc.ifc_req[i].ifr_name, IFNAMSIZ);
+ idx++;
+ }
+ if (idx != 0)
+ ifaces = probed_ifaces;
+ }
+ }
+#elif defined(HAVE_GETIFADDRS_AF_LINK)
+ struct ifaddrs *ifaddr, *ifa;
+
+ if (getifaddrs (&ifaddr) != -1) {
+ GPtrArray *arr;
+
+ arr = g_ptr_array_new ();
+
+ for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
+ if ((ifa->ifa_flags & IFF_LOOPBACK))
+ continue;
+
+ if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+
+ g_ptr_array_add (arr, g_strdup (ifa->ifa_name));
+ }
+ freeifaddrs (ifaddr);
+
+ g_ptr_array_add (arr, NULL);
+ ifaces = probed_ifaces = (gchar **) g_ptr_array_free (arr, FALSE);
+ }
+#else
+#warning "Implement something to list all network interfaces"
+#endif
+ }
+
+ /* Get a clock id from the MAC address if none was given */
+ if (clock_id == (guint64) - 1) {
+ gboolean success = FALSE;
+
+#if defined(HAVE_SIOCGIFCONF_SIOCGIFFLAGS_SIOCGIFHWADDR)
+ struct ifreq ifr;
+
+ if (ifaces) {
+ gchar **ptr = ifaces;
+
+ while (*ptr) {
+ memcpy (ifr.ifr_name, *ptr, IFNAMSIZ);
+ if (ioctl (g_socket_get_fd (socket_event), SIOCGIFHWADDR, &ifr) == 0) {
+ clock_id_array[0] = ifr.ifr_hwaddr.sa_data[0];
+ clock_id_array[1] = ifr.ifr_hwaddr.sa_data[1];
+ clock_id_array[2] = ifr.ifr_hwaddr.sa_data[2];
+ clock_id_array[3] = 0xff;
+ clock_id_array[4] = 0xfe;
+ clock_id_array[5] = ifr.ifr_hwaddr.sa_data[3];
+ clock_id_array[6] = ifr.ifr_hwaddr.sa_data[4];
+ clock_id_array[7] = ifr.ifr_hwaddr.sa_data[5];
+ success = TRUE;
+ break;
+ }
+ }
+
+ ptr++;
+ } else {
+ struct ifconf ifc;
+ gchar buf[8192];
+
+ ifc.ifc_len = sizeof (buf);
+ ifc.ifc_buf = buf;
+ if (ioctl (g_socket_get_fd (socket_event), SIOCGIFCONF, &ifc) != -1) {
+ guint i;
+
+ for (i = 0; i < ifc.ifc_len / sizeof (struct ifreq); i++) {
+ strncpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name, IFNAMSIZ);
+ if (ioctl (g_socket_get_fd (socket_event), SIOCGIFFLAGS, &ifr) == 0) {
+ if ((ifr.ifr_flags & IFF_LOOPBACK))
+ continue;
+
+ if (ioctl (g_socket_get_fd (socket_event), SIOCGIFHWADDR,
+ &ifr) == 0) {
+ clock_id_array[0] = ifr.ifr_hwaddr.sa_data[0];
+ clock_id_array[1] = ifr.ifr_hwaddr.sa_data[1];
+ clock_id_array[2] = ifr.ifr_hwaddr.sa_data[2];
+ clock_id_array[3] = 0xff;
+ clock_id_array[4] = 0xfe;
+ clock_id_array[5] = ifr.ifr_hwaddr.sa_data[3];
+ clock_id_array[6] = ifr.ifr_hwaddr.sa_data[4];
+ clock_id_array[7] = ifr.ifr_hwaddr.sa_data[5];
+ success = TRUE;
+ break;
+ }
+ } else {
+ g_warning ("can't get flags of interface '%s'",
+ ifc.ifc_req[i].ifr_name);
+ }
+ }
+ }
+ }
+#elif defined(HAVE_GETIFADDRS_AF_LINK)
+ struct ifaddrs *ifaddr, *ifa;
+
+ if (getifaddrs (&ifaddr) != -1) {
+ for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *) ifa->ifa_addr;
+ guint8 mac_addr[6];
+
+ if ((ifa->ifa_flags & IFF_LOOPBACK))
+ continue;
+
+ if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+
+ if (ifaces) {
+ gchar **p = ifaces;
+ gboolean found = FALSE;
+
+ while (*p) {
+ if (strcmp (*p, ifa->ifa_name) == 0) {
+ found = TRUE;
+ break;
+ }
+ p++;
+ }
+
+ if (!found)
+ continue;
+ }
+
+ if (sdl->sdl_alen != 6)
+ continue;
+
+ memcpy (mac_addr, LLADDR (sdl), sdl->sdl_alen);
+
+ clock_id_array[0] = mac_addr[0];
+ clock_id_array[1] = mac_addr[1];
+ clock_id_array[2] = mac_addr[2];
+ clock_id_array[3] = 0xff;
+ clock_id_array[4] = 0xfe;
+ clock_id_array[5] = mac_addr[3];
+ clock_id_array[6] = mac_addr[4];
+ clock_id_array[7] = mac_addr[5];
+ success = TRUE;
+ break;
+ }
+
+ freeifaddrs (ifaddr);
+ }
+#else
+#warning "Implement something to get MAC addresses of network interfaces"
+#endif
+
+ if (!success) {
+ g_warning ("can't get any MAC address, using random clock id");
+ clock_id = (((guint64) g_random_int ()) << 32) | (g_random_int ());
+ GST_WRITE_UINT64_BE (clock_id_array, clock_id);
+ clock_id_array[3] = 0xff;
+ clock_id_array[4] = 0xfe;
+ }
+ } else {
+ GST_WRITE_UINT64_BE (clock_id_array, clock_id);
+ }
+
+ /* Join multicast groups */
+ mcast_addr = g_inet_address_new_from_string (PTP_MULTICAST_GROUP);
+ if (ifaces) {
+ gchar **ptr = ifaces;
+ gboolean success = FALSE;
+
+ while (*ptr) {
+ gint c = 0;
+ if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, *ptr,
+ &err)
+ && !g_error_matches (err, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE))
+ g_warning ("Couldn't join multicast group on interface '%s': %s", *ptr,
+ err->message);
+ else
+ c++;
+ g_clear_error (&err);
+
+ if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE,
+ *ptr, &err)
+ && !g_error_matches (err, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE))
+ g_warning ("Couldn't join multicast group on interface '%s': %s", *ptr,
+ err->message);
+ else
+ c++;
+ g_clear_error (&err);
+
+ if (c == 2)
+ success = TRUE;
+ ptr++;
+ }
+
+ if (!success) {
+ /* Join multicast group without any interface */
+ if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, NULL,
+ &err))
+ g_error ("Couldn't join multicast group: %s", err->message);
+ g_clear_error (&err);
+ if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE,
+ NULL, &err))
+ g_error ("Couldn't join multicast group: %s", err->message);
+ g_clear_error (&err);
+ }
+ } else {
+ /* Join multicast group without any interface */
+ if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, NULL,
+ &err))
+ g_error ("Couldn't join multicast group: %s", err->message);
+ g_clear_error (&err);
+ if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE, NULL,
+ &err))
+ g_error ("Couldn't join multicast group: %s", err->message);
+ g_clear_error (&err);
+ }
+
+ event_saddr = g_inet_socket_address_new (mcast_addr, PTP_EVENT_PORT);
+ general_saddr = g_inet_socket_address_new (mcast_addr, PTP_GENERAL_PORT);
+
+ /* Create socket sources */
+ socket_event_source =
+ g_socket_create_source (socket_event, G_IO_IN | G_IO_PRI, NULL);
+ g_source_set_priority (socket_event_source, G_PRIORITY_HIGH);
+ g_source_set_callback (socket_event_source, (GSourceFunc) have_socket_data_cb,
+ NULL, NULL);
+ g_source_attach (socket_event_source, NULL);
+ socket_general_source =
+ g_socket_create_source (socket_general, G_IO_IN | G_IO_PRI, NULL);
+ g_source_set_priority (socket_general_source, G_PRIORITY_DEFAULT);
+ g_source_set_callback (socket_general_source,
+ (GSourceFunc) have_socket_data_cb, NULL, NULL);
+ g_source_attach (socket_general_source, NULL);
+
+ g_strfreev (probed_ifaces);
+}
+
+static void
+drop_privileges (void)
+{
+#ifdef HAVE_PTP_HELPER_SETUID
+ /* Switch to the given user/group */
+#ifdef HAVE_PTP_HELPER_SETUID_GROUP
+ {
+ struct group *grp;
+
+ grp = getgrnam (HAVE_PTP_HELPER_SETUID_GROUP);
+ if (!grp)
+ g_error ("Failed to get group information '%s': %s",
+ HAVE_PTP_HELPER_SETUID_GROUP, g_strerror (errno));
+
+ if (setgid (grp->gr_gid) != 0)
+ g_error ("Failed to change to group '%s': %s",
+ HAVE_PTP_HELPER_SETUID_GROUP, g_strerror (errno));
+ }
+#endif
+
+#ifdef HAVE_PTP_HELPER_SETUID_USER
+ {
+ struct passwd *pwd;
+
+ pwd = getpwnam (HAVE_PTP_HELPER_SETUID_USER);
+ if (!pwd)
+ g_error ("Failed to get user information '%s': %s",
+ HAVE_PTP_HELPER_SETUID_USER, g_strerror (errno));
+
+#ifndef HAVE_PTP_HELPER_SETUID_GROUP
+ if (setgid (pwd->pw_gid) != 0)
+ g_error ("Failed to change to user group '%s': %s",
+ HAVE_PTP_HELPER_SETUID_USER, g_strerror (errno));
+#endif
+
+ if (setuid (pwd->pw_uid) != 0)
+ g_error ("Failed to change to user '%s': %s", HAVE_PTP_HELPER_SETUID_USER,
+ g_strerror (errno));
+ }
+#endif
+#endif
+#ifdef HAVE_PTP_HELPER_CAPABILITIES
+ /* Drop all capabilities */
+ {
+ cap_t caps;
+
+ caps = cap_get_proc ();
+ if (caps == 0)
+ g_error ("Failed to get process caps: %s", g_strerror (errno));
+ if (cap_clear (caps) != 0)
+ g_error ("Failed to clear caps: %s", g_strerror (errno));
+ if (cap_set_proc (caps) != 0)
+ g_error ("Failed to set process caps: %s", g_strerror (errno));
+ }
+#endif
+}
+
+static void
+setup_stdio_channels (void)
+{
+ GSource *stdin_source;
+
+ /* Create stdin source */
+ stdin_channel = g_io_channel_unix_new (STDIN_FILENO);
+ if (g_io_channel_set_encoding (stdin_channel, NULL,
+ NULL) == G_IO_STATUS_ERROR)
+ g_error ("Failed to set stdin to binary encoding");
+ g_io_channel_set_buffered (stdin_channel, FALSE);
+ stdin_source =
+ g_io_create_watch (stdin_channel, G_IO_IN | G_IO_PRI | G_IO_HUP);
+ g_source_set_priority (stdin_source, G_PRIORITY_DEFAULT);
+ g_source_set_callback (stdin_source, (GSourceFunc) have_stdin_data_cb, NULL,
+ NULL);
+ g_source_attach (stdin_source, NULL);
+
+ /* Create stdout channel */
+ stdout_channel = g_io_channel_unix_new (STDOUT_FILENO);
+ if (g_io_channel_set_encoding (stdout_channel, NULL,
+ NULL) == G_IO_STATUS_ERROR)
+ g_error ("Failed to set stdout to binary encoding");
+ g_io_channel_set_buffered (stdout_channel, FALSE);
+}
+
+static void
+write_clock_id (void)
+{
+ GError *err = NULL;
+ GIOStatus status;
+ StdIOHeader header = { 0, };
+ gsize written;
+
+ /* Write clock id to stdout */
+
+ header.type = TYPE_CLOCK_ID;
+ header.size = 8;
+ status =
+ g_io_channel_write_chars (stdout_channel, (gchar *) & header,
+ sizeof (header), &written, &err);
+ if (status == G_IO_STATUS_ERROR) {
+ g_error ("Failed to write to stdout: %s", err->message);
+ g_clear_error (&err);
+ } else if (status == G_IO_STATUS_EOF) {
+ g_message ("EOF on stdout");
+ exit (0);
+ } else if (status != G_IO_STATUS_NORMAL) {
+ g_error ("Unexpected stdout write status: %d", status);
+ } else if (written != sizeof (header)) {
+ g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written);
+ }
+
+ status =
+ g_io_channel_write_chars (stdout_channel,
+ (const gchar *) clock_id_array, sizeof (clock_id_array), &written, &err);
+ if (status == G_IO_STATUS_ERROR) {
+ g_error ("Failed to write to stdout: %s", err->message);
+ g_clear_error (&err);
+ } else if (status == G_IO_STATUS_EOF) {
+ g_message ("EOF on stdout");
+ exit (0);
+ } else if (status != G_IO_STATUS_NORMAL) {
+ g_error ("Unexpected stdout write status: %d", status);
+ } else if (written != sizeof (clock_id_array)) {
+ g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written);
+ }
+}
+
+#ifdef __APPLE__
+static gint
+dummy_poll (GPollFD * fds, guint nfds, gint timeout)
+{
+ return g_poll (fds, nfds, timeout);
+}
+#endif
+
+gint
+main (gint argc, gchar ** argv)
+{
+ GOptionContext *opt_ctx;
+ GMainLoop *loop;
+ GError *err = NULL;
+
+ /* FIXME: Work around some side effects of the changes from
+ * https://bugzilla.gnome.org/show_bug.cgi?id=741054
+ *
+ * The modified poll function somehow calls setugid(), which
+ * then abort()s the application. Make sure that we use g_poll()
+ * here!
+ */
+#ifdef __APPLE__
+ {
+ GMainContext *context = g_main_context_default ();
+ g_main_context_set_poll_func (context, dummy_poll);
+ }
+#endif
+
+#ifdef HAVE_PTP_HELPER_SETUID
+ if (setuid (0) < 0)
+ g_error ("not running with superuser privileges");
+#endif
+
+ opt_ctx = g_option_context_new ("- GStreamer PTP helper process");
+ g_option_context_add_main_entries (opt_ctx, opt_entries, NULL);
+ if (!g_option_context_parse (opt_ctx, &argc, &argv, &err))
+ g_error ("Error parsing options: %s", err->message);
+ g_clear_error (&err);
+ g_option_context_free (opt_ctx);
+
+ setup_sockets ();
+ drop_privileges ();
+ setup_stdio_channels ();
+ write_clock_id ();
+
+ /* Get running */
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+
+ /* We never exit cleanly, so don't do cleanup */
+ g_assert_not_reached ();
+
+ return 0;
+}