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