ptp: Check for the actual API we use instead of just looking for __APPLE__
[platform/upstream/gstreamer.git] / libs / gst / helpers / gst-ptp-helper.c
1 /* GStreamer
2  * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com>
3  *
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /* Helper process that runs setuid root or with appropriate privileges to
22  * listen on ports < 1024, do multicast operations and get MAC addresses of
23  * interfaces. Privileges are dropped after these operations are done.
24  *
25  * It listens on the PTP multicast group on port 319 and 320 and forwards
26  * everything received there to stdout, while forwarding everything received
27  * on stdout to those sockets.
28  * Additionally it provides the MAC address of a network interface via stdout
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <sys/types.h>
38 #include <errno.h>
39 #include <sys/ioctl.h>
40 #include <net/if.h>
41 #include <netinet/in.h>
42 #include <string.h>
43
44 #ifdef HAVE_GETIFADDRS_AF_LINK
45 #include <ifaddrs.h>
46 #include <net/if_dl.h>
47 #endif
48
49 #ifdef HAVE_PTP_HELPER_SETUID
50 #include <grp.h>
51 #include <pwd.h>
52 #endif
53
54 #ifdef HAVE_PTP_HELPER_CAPABILITIES
55 #include <sys/capability.h>
56 #endif
57
58 #include <glib.h>
59 #include <gio/gio.h>
60
61 #include <gst/gst.h>
62 #include <gst/net/gstptp_private.h>
63
64 #define PTP_MULTICAST_GROUP "224.0.1.129"
65 #define PTP_EVENT_PORT   319
66 #define PTP_GENERAL_PORT 320
67
68 static gchar **ifaces = NULL;
69 static gboolean verbose = FALSE;
70 static guint64 clock_id = (guint64) - 1;
71 static guint8 clock_id_array[8];
72
73 static GOptionEntry opt_entries[] = {
74   {"interface", 'i', 0, G_OPTION_ARG_STRING_ARRAY, &ifaces,
75       "Interface to listen on", NULL},
76   {"clock-id", 'c', 0, G_OPTION_ARG_INT64, &clock_id,
77       "PTP clock id", NULL},
78   {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
79       "Be verbose", NULL},
80   {NULL}
81 };
82
83 static GSocketAddress *event_saddr, *general_saddr;
84 static GSocket *socket_event, *socket_general;
85 static GIOChannel *stdin_channel, *stdout_channel;
86
87 static gboolean
88 have_socket_data_cb (GSocket * socket, GIOCondition condition,
89     gpointer user_data)
90 {
91   gchar buffer[8192];
92   gssize read;
93   gsize written;
94   GError *err = NULL;
95   GIOStatus status;
96   StdIOHeader header = { 0, };
97
98   read = g_socket_receive (socket, buffer, sizeof (buffer), NULL, &err);
99   if (read == -1)
100     g_error ("Failed to read from socket: %s", err->message);
101
102   if (verbose)
103     g_message ("Received %" G_GSSIZE_FORMAT " bytes from %s socket", read,
104         (socket == socket_event ? "event" : "general"));
105
106   header.size = read;
107   header.type = (socket == socket_event) ? TYPE_EVENT : TYPE_GENERAL;
108
109   status =
110       g_io_channel_write_chars (stdout_channel, (gchar *) & header,
111       sizeof (header), &written, &err);
112   if (status == G_IO_STATUS_ERROR) {
113     g_error ("Failed to write to stdout: %s", err->message);
114   } else if (status == G_IO_STATUS_EOF) {
115     g_message ("EOF on stdout");
116     exit (0);
117   } else if (status != G_IO_STATUS_NORMAL) {
118     g_error ("Unexpected stdout write status: %d", status);
119   } else if (written != sizeof (header)) {
120     g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written);
121   }
122
123   status =
124       g_io_channel_write_chars (stdout_channel, buffer, read, &written, &err);
125   if (status == G_IO_STATUS_ERROR) {
126     g_error ("Failed to write to stdout: %s", err->message);
127   } else if (status == G_IO_STATUS_EOF) {
128     g_message ("EOF on stdout");
129     exit (0);
130   } else if (status != G_IO_STATUS_NORMAL) {
131     g_error ("Unexpected stdout write status: %d", status);
132   } else if (written != read) {
133     g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written);
134   }
135
136   return G_SOURCE_CONTINUE;
137 }
138
139 static gboolean
140 have_stdin_data_cb (GIOChannel * channel, GIOCondition condition,
141     gpointer user_data)
142 {
143   GIOStatus status;
144   StdIOHeader header = { 0, };
145   gchar buffer[8192];
146   GError *err = NULL;
147   gsize read;
148   gssize written;
149
150   if ((condition & G_IO_STATUS_EOF)) {
151     g_message ("EOF on stdin");
152     exit (0);
153   }
154
155   status =
156       g_io_channel_read_chars (channel, (gchar *) & header, sizeof (header),
157       &read, &err);
158   if (status == G_IO_STATUS_ERROR) {
159     g_error ("Failed to read from stdin: %s", err->message);
160   } else if (status == G_IO_STATUS_EOF) {
161     g_message ("EOF on stdin");
162     exit (0);
163   } else if (status != G_IO_STATUS_NORMAL) {
164     g_error ("Unexpected stdin read status: %d", status);
165   } else if (read != sizeof (header)) {
166     g_error ("Unexpected read size: %" G_GSIZE_FORMAT, read);
167   } else if (header.size > 8192) {
168     g_error ("Unexpected size: %u", header.size);
169   }
170
171   status = g_io_channel_read_chars (channel, buffer, header.size, &read, &err);
172   if (status == G_IO_STATUS_ERROR) {
173     g_error ("Failed to read from stdin: %s", err->message);
174   } else if (status == G_IO_STATUS_EOF) {
175     g_message ("EOF on stdin");
176     exit (0);
177   } else if (status != G_IO_STATUS_NORMAL) {
178     g_error ("Unexpected stdin read status: %d", status);
179   } else if (read != header.size) {
180     g_error ("Unexpected read size: %" G_GSIZE_FORMAT, read);
181   }
182
183   switch (header.type) {
184     case TYPE_EVENT:
185     case TYPE_GENERAL:
186       written =
187           g_socket_send_to (header.type ==
188           TYPE_EVENT ? socket_event : socket_general,
189           (header.type == TYPE_EVENT ? event_saddr : general_saddr), buffer,
190           header.size, NULL, &err);
191       if (written == -1)
192         g_error ("Failed to write to socket: %s", err->message);
193       else if (written != header.size)
194         g_error ("Unexpected write size: %" G_GSSIZE_FORMAT, written);
195
196       if (verbose)
197         g_message ("Sent %" G_GSSIZE_FORMAT " bytes to %s socket", read,
198             (header.type == TYPE_EVENT ? "event" : "general"));
199       break;
200     default:
201       break;
202   }
203
204   return G_SOURCE_CONTINUE;
205 }
206
207 static void
208 setup_sockets (void)
209 {
210   GInetAddress *bind_addr, *mcast_addr;
211   GSocketAddress *bind_saddr;
212   GSource *socket_event_source, *socket_general_source;
213   gchar **probed_ifaces = NULL;
214   GError *err = NULL;
215
216   /* Create sockets */
217   socket_event =
218       g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
219       G_SOCKET_PROTOCOL_UDP, &err);
220   if (!socket_event)
221     g_error ("Couldn't create event socket: %s", err->message);
222
223   socket_general =
224       g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
225       G_SOCKET_PROTOCOL_UDP, &err);
226   if (!socket_general)
227     g_error ("Couldn't create general socket: %s", err->message);
228
229   /* Bind sockets */
230   bind_addr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
231   bind_saddr = g_inet_socket_address_new (bind_addr, PTP_EVENT_PORT);
232   if (!g_socket_bind (socket_event, bind_saddr, TRUE, &err))
233     g_error ("Couldn't bind event socket: %s", err->message);
234   g_object_unref (bind_saddr);
235   bind_saddr = g_inet_socket_address_new (bind_addr, PTP_GENERAL_PORT);
236   if (!g_socket_bind (socket_general, bind_saddr, TRUE, &err))
237     g_error ("Couldn't bind general socket: %s", err->message);
238   g_object_unref (bind_saddr);
239   g_object_unref (bind_addr);
240
241   /* Probe all non-loopback interfaces */
242   if (!ifaces) {
243 #if defined(HAVE_SIOCGIFCONF_SIOCGIFFLAGS_SIOCGIFHWADDR)
244     struct ifreq ifr;
245     struct ifconf ifc;
246     gchar buf[8192];
247
248     ifc.ifc_len = sizeof (buf);
249     ifc.ifc_buf = buf;
250     if (ioctl (g_socket_get_fd (socket_event), SIOCGIFCONF, &ifc) != -1) {
251       guint i, idx = 0;
252
253       probed_ifaces = g_new0 (gchar *, ifc.ifc_len + 1);
254
255       for (i = 0; i < ifc.ifc_len / sizeof (struct ifreq); i++) {
256         strncpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name, IFNAMSIZ);
257         if (ioctl (g_socket_get_fd (socket_event), SIOCGIFFLAGS, &ifr) == 0) {
258           if ((ifr.ifr_flags & IFF_LOOPBACK))
259             continue;
260           probed_ifaces[idx] = g_strndup (ifc.ifc_req[i].ifr_name, IFNAMSIZ);
261           idx++;
262         } else {
263           g_warning ("can't get flags of interface '%s'",
264               ifc.ifc_req[i].ifr_name);
265           probed_ifaces[idx] = g_strndup (ifc.ifc_req[i].ifr_name, IFNAMSIZ);
266           idx++;
267         }
268         if (idx != 0)
269           ifaces = probed_ifaces;
270       }
271     }
272 #elif defined(HAVE_GETIFADDRS_AF_LINK)
273     struct ifaddrs *ifaddr, *ifa;
274
275     if (getifaddrs (&ifaddr) != -1) {
276       GPtrArray *arr;
277
278       arr = g_ptr_array_new ();
279
280       for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
281         if ((ifa->ifa_flags & IFF_LOOPBACK))
282           continue;
283
284         if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_LINK)
285           continue;
286
287         g_ptr_array_add (arr, g_strdup (ifa->ifa_name));
288       }
289       freeifaddrs (ifaddr);
290
291       g_ptr_array_add (arr, NULL);
292       ifaces = probed_ifaces = (gchar **) g_ptr_array_free (arr, FALSE);
293     }
294 #else
295 #warning "Implement something to list all network interfaces"
296 #endif
297   }
298
299   /* Get a clock id from the MAC address if none was given */
300   if (clock_id == (guint64) - 1) {
301     gboolean success = FALSE;
302
303 #if defined(HAVE_SIOCGIFCONF_SIOCGIFFLAGS_SIOCGIFHWADDR)
304     struct ifreq ifr;
305
306     if (ifaces) {
307       gchar **ptr = ifaces;
308
309       while (*ptr) {
310         strncpy (ifr.ifr_name, *ptr, IFNAMSIZ);
311         if (ioctl (g_socket_get_fd (socket_event), SIOCGIFHWADDR, &ifr) == 0) {
312           clock_id_array[0] = ifr.ifr_hwaddr.sa_data[0];
313           clock_id_array[1] = ifr.ifr_hwaddr.sa_data[1];
314           clock_id_array[2] = ifr.ifr_hwaddr.sa_data[2];
315           clock_id_array[3] = 0xff;
316           clock_id_array[4] = 0xfe;
317           clock_id_array[5] = ifr.ifr_hwaddr.sa_data[3];
318           clock_id_array[6] = ifr.ifr_hwaddr.sa_data[4];
319           clock_id_array[7] = ifr.ifr_hwaddr.sa_data[5];
320           success = TRUE;
321           break;
322         }
323       }
324
325       ptr++;
326     } else {
327       struct ifconf ifc;
328       gchar buf[8192];
329
330       ifc.ifc_len = sizeof (buf);
331       ifc.ifc_buf = buf;
332       if (ioctl (g_socket_get_fd (socket_event), SIOCGIFCONF, &ifc) != -1) {
333         guint i;
334
335         for (i = 0; i < ifc.ifc_len / sizeof (struct ifreq); i++) {
336           strncpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name, IFNAMSIZ);
337           if (ioctl (g_socket_get_fd (socket_event), SIOCGIFFLAGS, &ifr) == 0) {
338             if ((ifr.ifr_flags & IFF_LOOPBACK))
339               continue;
340
341             if (ioctl (g_socket_get_fd (socket_event), SIOCGIFHWADDR,
342                     &ifr) == 0) {
343               clock_id_array[0] = ifr.ifr_hwaddr.sa_data[0];
344               clock_id_array[1] = ifr.ifr_hwaddr.sa_data[1];
345               clock_id_array[2] = ifr.ifr_hwaddr.sa_data[2];
346               clock_id_array[3] = 0xff;
347               clock_id_array[4] = 0xfe;
348               clock_id_array[5] = ifr.ifr_hwaddr.sa_data[3];
349               clock_id_array[6] = ifr.ifr_hwaddr.sa_data[4];
350               clock_id_array[7] = ifr.ifr_hwaddr.sa_data[5];
351               success = TRUE;
352               break;
353             }
354           } else {
355             g_warning ("can't get flags of interface '%s'",
356                 ifc.ifc_req[i].ifr_name);
357           }
358         }
359       }
360     }
361 #elif defined(HAVE_GETIFADDRS_AF_LINK)
362     struct ifaddrs *ifaddr, *ifa;
363
364     if (getifaddrs (&ifaddr) != -1) {
365       for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
366         struct sockaddr_dl *sdl = (struct sockaddr_dl *) ifa->ifa_addr;
367         guint8 mac_addr[6];
368
369         if ((ifa->ifa_flags & IFF_LOOPBACK))
370           continue;
371
372         if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_LINK)
373           continue;
374
375         if (ifaces) {
376           gchar **p = ifaces;
377           gboolean found = FALSE;
378
379           while (*p) {
380             if (strcmp (*p, ifa->ifa_name) == 0) {
381               found = TRUE;
382               break;
383             }
384             p++;
385           }
386
387           if (!found)
388             continue;
389         }
390
391         if (sdl->sdl_alen != 6)
392           continue;
393
394         memcpy (mac_addr, LLADDR (sdl), sdl->sdl_alen);
395
396         clock_id_array[0] = mac_addr[0];
397         clock_id_array[1] = mac_addr[1];
398         clock_id_array[2] = mac_addr[2];
399         clock_id_array[3] = 0xff;
400         clock_id_array[4] = 0xfe;
401         clock_id_array[5] = mac_addr[3];
402         clock_id_array[6] = mac_addr[4];
403         clock_id_array[7] = mac_addr[5];
404         success = TRUE;
405         break;
406       }
407
408       freeifaddrs (ifaddr);
409     }
410 #else
411 #warning "Implement something to get MAC addresses of network interfaces"
412 #endif
413
414     if (!success) {
415       g_warning ("can't get any MAC address, using random clock id");
416       clock_id = (((guint64) g_random_int ()) << 32) | (g_random_int ());
417       GST_WRITE_UINT64_BE (clock_id_array, clock_id);
418       clock_id_array[3] = 0xff;
419       clock_id_array[4] = 0xfe;
420     }
421   } else {
422     GST_WRITE_UINT64_BE (clock_id_array, clock_id);
423   }
424
425   /* Join multicast groups */
426   mcast_addr = g_inet_address_new_from_string (PTP_MULTICAST_GROUP);
427   if (ifaces) {
428     gchar **ptr = ifaces;
429     gboolean success = FALSE;
430
431     while (*ptr) {
432       gint c = 0;
433       if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, *ptr,
434               &err)
435           && !g_error_matches (err, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE))
436         g_warning ("Couldn't join multicast group on interface '%s': %s", *ptr,
437             err->message);
438       else
439         c++;
440       g_clear_error (&err);
441
442       if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE,
443               *ptr, &err)
444           && !g_error_matches (err, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE))
445         g_warning ("Couldn't join multicast group on interface '%s': %s", *ptr,
446             err->message);
447       else
448         c++;
449       g_clear_error (&err);
450
451       if (c == 2)
452         success = TRUE;
453       ptr++;
454     }
455
456     if (!success) {
457       /* Join multicast group without any interface */
458       if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, NULL,
459               &err))
460         g_error ("Couldn't join multicast group: %s", err->message);
461       if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE,
462               NULL, &err))
463         g_error ("Couldn't join multicast group: %s", err->message);
464     }
465   } else {
466     /* Join multicast group without any interface */
467     if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, NULL,
468             &err))
469       g_error ("Couldn't join multicast group: %s", err->message);
470     if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE, NULL,
471             &err))
472       g_error ("Couldn't join multicast group: %s", err->message);
473   }
474
475   event_saddr = g_inet_socket_address_new (mcast_addr, PTP_EVENT_PORT);
476   general_saddr = g_inet_socket_address_new (mcast_addr, PTP_GENERAL_PORT);
477
478   /* Create socket sources */
479   socket_event_source =
480       g_socket_create_source (socket_event, G_IO_IN | G_IO_PRI, NULL);
481   g_source_set_priority (socket_event_source, G_PRIORITY_HIGH);
482   g_source_set_callback (socket_event_source, (GSourceFunc) have_socket_data_cb,
483       NULL, NULL);
484   g_source_attach (socket_event_source, NULL);
485   socket_general_source =
486       g_socket_create_source (socket_general, G_IO_IN | G_IO_PRI, NULL);
487   g_source_set_priority (socket_general_source, G_PRIORITY_DEFAULT);
488   g_source_set_callback (socket_general_source,
489       (GSourceFunc) have_socket_data_cb, NULL, NULL);
490   g_source_attach (socket_general_source, NULL);
491
492   g_strfreev (probed_ifaces);
493 }
494
495 static void
496 drop_privileges (void)
497 {
498 #ifdef HAVE_PTP_HELPER_SETUID
499   /* Switch to the given user/group */
500 #ifdef HAVE_PTP_HELPER_SETUID_GROUP
501   {
502     struct group *grp;
503
504     grp = getgrnam (HAVE_PTP_HELPER_SETUID_GROUP);
505     if (!grp)
506       g_error ("Failed to get group information '%s': %s",
507           HAVE_PTP_HELPER_SETUID_GROUP, g_strerror (errno));
508
509     if (setgid (grp->gr_gid) != 0)
510       g_error ("Failed to change to group '%s': %s",
511           HAVE_PTP_HELPER_SETUID_GROUP, g_strerror (errno));
512   }
513 #endif
514
515 #ifdef HAVE_PTP_HELPER_SETUID_USER
516   {
517     struct passwd *pwd;
518
519     pwd = getpwnam (HAVE_PTP_HELPER_SETUID_USER);
520     if (!pwd)
521       g_error ("Failed to get user information '%s': %s",
522           HAVE_PTP_HELPER_SETUID_USER, g_strerror (errno));
523
524 #ifndef HAVE_PTP_HELPER_SETUID_GROUP
525     if (setgid (pwd->pw_gid) != 0)
526       g_error ("Failed to change to user group '%s': %s",
527           HAVE_PTP_HELPER_SETUID_USER, g_strerror (errno));
528 #endif
529
530     if (setuid (pwd->pw_uid) != 0)
531       g_error ("Failed to change to user '%s': %s", HAVE_PTP_HELPER_SETUID_USER,
532           g_strerror (errno));
533   }
534 #endif
535 #endif
536 #ifdef HAVE_PTP_HELPER_CAPABILITIES
537   /* Drop all capabilities */
538   {
539     cap_t caps;
540
541     caps = cap_get_proc ();
542     if (caps == 0)
543       g_error ("Failed to get process caps: %s", g_strerror (errno));
544     if (cap_clear (caps) != 0)
545       g_error ("Failed to clear caps: %s", g_strerror (errno));
546     if (cap_set_proc (caps) != 0)
547       g_error ("Failed to set process caps: %s", g_strerror (errno));
548   }
549 #endif
550 }
551
552 static void
553 setup_stdio_channels (void)
554 {
555   GSource *stdin_source;
556
557   /* Create stdin source */
558   stdin_channel = g_io_channel_unix_new (STDIN_FILENO);
559   if (g_io_channel_set_encoding (stdin_channel, NULL,
560           NULL) == G_IO_STATUS_ERROR)
561     g_error ("Failed to set stdin to binary encoding");
562   g_io_channel_set_buffered (stdin_channel, FALSE);
563   stdin_source =
564       g_io_create_watch (stdin_channel, G_IO_IN | G_IO_PRI | G_IO_HUP);
565   g_source_set_priority (stdin_source, G_PRIORITY_DEFAULT);
566   g_source_set_callback (stdin_source, (GSourceFunc) have_stdin_data_cb, NULL,
567       NULL);
568   g_source_attach (stdin_source, NULL);
569
570   /* Create stdout channel */
571   stdout_channel = g_io_channel_unix_new (STDOUT_FILENO);
572   if (g_io_channel_set_encoding (stdout_channel, NULL,
573           NULL) == G_IO_STATUS_ERROR)
574     g_error ("Failed to set stdout to binary encoding");
575   g_io_channel_set_buffered (stdout_channel, FALSE);
576 }
577
578 static void
579 write_clock_id (void)
580 {
581   GError *err = NULL;
582   GIOStatus status;
583   StdIOHeader header = { 0, };
584   gsize written;
585
586   /* Write clock id to stdout */
587
588   header.type = TYPE_CLOCK_ID;
589   header.size = 8;
590   status =
591       g_io_channel_write_chars (stdout_channel, (gchar *) & header,
592       sizeof (header), &written, &err);
593   if (status == G_IO_STATUS_ERROR) {
594     g_error ("Failed to write to stdout: %s", err->message);
595   } else if (status == G_IO_STATUS_EOF) {
596     g_message ("EOF on stdout");
597     exit (0);
598   } else if (status != G_IO_STATUS_NORMAL) {
599     g_error ("Unexpected stdout write status: %d", status);
600   } else if (written != sizeof (header)) {
601     g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written);
602   }
603
604   status =
605       g_io_channel_write_chars (stdout_channel,
606       (const gchar *) clock_id_array, sizeof (clock_id_array), &written, &err);
607   if (status == G_IO_STATUS_ERROR) {
608     g_error ("Failed to write to stdout: %s", err->message);
609   } else if (status == G_IO_STATUS_EOF) {
610     g_message ("EOF on stdout");
611     exit (0);
612   } else if (status != G_IO_STATUS_NORMAL) {
613     g_error ("Unexpected stdout write status: %d", status);
614   } else if (written != sizeof (clock_id_array)) {
615     g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written);
616   }
617 }
618
619 #ifdef __APPLE__
620 static gint
621 dummy_poll (GPollFD * fds, guint nfds, gint timeout)
622 {
623   return g_poll (fds, nfds, timeout);
624 }
625 #endif
626
627 gint
628 main (gint argc, gchar ** argv)
629 {
630   GOptionContext *opt_ctx;
631   GMainLoop *loop;
632   GError *err = NULL;
633
634   /* FIXME: Work around some side effects of the changes from
635    * https://bugzilla.gnome.org/show_bug.cgi?id=741054
636    *
637    * The modified poll function somehow calls setugid(), which
638    * then abort()s the application. Make sure that we use g_poll()
639    * here!
640    */
641 #ifdef __APPLE__
642   {
643     GMainContext *context = g_main_context_default ();
644     g_main_context_set_poll_func (context, dummy_poll);
645   }
646 #endif
647
648 #ifdef HAVE_PTP_HELPER_SETUID
649   if (setuid (0) < 0)
650     g_error ("not running with superuser privileges");
651 #endif
652
653   opt_ctx = g_option_context_new ("- GStreamer PTP helper process");
654   g_option_context_add_main_entries (opt_ctx, opt_entries, NULL);
655   if (!g_option_context_parse (opt_ctx, &argc, &argv, &err))
656     g_error ("Error parsing options: %s", err->message);
657   g_option_context_free (opt_ctx);
658
659   setup_sockets ();
660   drop_privileges ();
661   setup_stdio_channels ();
662   write_clock_id ();
663
664   /* Get running */
665   loop = g_main_loop_new (NULL, FALSE);
666   g_main_loop_run (loop);
667
668   /* We never exit cleanly, so don't do cleanup */
669   g_assert_not_reached ();
670
671   return 0;
672 }