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