2 * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com>
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.
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.
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.
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.
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
37 #include <sys/types.h>
39 #include <sys/ioctl.h>
41 #include <netinet/in.h>
44 #ifdef HAVE_PTP_HELPER_SETUID
49 #ifdef HAVE_PTP_HELPER_CAPABILITIES
50 #include <sys/capability.h>
57 #include <gst/net/gstptp_private.h>
59 #define PTP_MULTICAST_GROUP "224.0.1.129"
60 #define PTP_EVENT_PORT 319
61 #define PTP_GENERAL_PORT 320
63 static gchar **ifaces = NULL;
64 static gboolean verbose = FALSE;
65 static guint64 clock_id = (guint64) - 1;
66 static guint8 clock_id_array[8];
68 static GOptionEntry opt_entries[] = {
69 {"interface", 'i', 0, G_OPTION_ARG_STRING_ARRAY, &ifaces,
70 "Interface to listen on", NULL},
71 {"clock-id", 'c', 0, G_OPTION_ARG_INT64, &clock_id,
72 "PTP clock id", NULL},
73 {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
78 static GSocketAddress *event_saddr, *general_saddr;
79 static GSocket *socket_event, *socket_general;
80 static GIOChannel *stdin_channel, *stdout_channel;
83 have_socket_data_cb (GSocket * socket, GIOCondition condition,
91 StdIOHeader header = { 0, };
93 read = g_socket_receive (socket, buffer, sizeof (buffer), NULL, &err);
95 g_error ("Failed to read from socket: %s", err->message);
98 g_message ("Received %" G_GSSIZE_FORMAT " bytes from %s socket", read,
99 (socket == socket_event ? "event" : "general"));
102 header.type = (socket == socket_event) ? TYPE_EVENT : TYPE_GENERAL;
105 g_io_channel_write_chars (stdout_channel, (gchar *) & header,
106 sizeof (header), &written, &err);
107 if (status == G_IO_STATUS_ERROR) {
108 g_error ("Failed to write to stdout: %s", err->message);
109 } else if (status == G_IO_STATUS_EOF) {
110 g_message ("EOF on stdout");
112 } else if (status != G_IO_STATUS_NORMAL) {
113 g_error ("Unexpected stdout write status: %d", status);
114 } else if (written != sizeof (header)) {
115 g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written);
119 g_io_channel_write_chars (stdout_channel, buffer, read, &written, &err);
120 if (status == G_IO_STATUS_ERROR) {
121 g_error ("Failed to write to stdout: %s", err->message);
122 } else if (status == G_IO_STATUS_EOF) {
123 g_message ("EOF on stdout");
125 } else if (status != G_IO_STATUS_NORMAL) {
126 g_error ("Unexpected stdout write status: %d", status);
127 } else if (written != read) {
128 g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written);
131 return G_SOURCE_CONTINUE;
135 have_stdin_data_cb (GIOChannel * channel, GIOCondition condition,
139 StdIOHeader header = { 0, };
145 if ((condition & G_IO_STATUS_EOF)) {
146 g_message ("EOF on stdin");
151 g_io_channel_read_chars (channel, (gchar *) & header, sizeof (header),
153 if (status == G_IO_STATUS_ERROR) {
154 g_error ("Failed to read from stdin: %s", err->message);
155 } else if (status == G_IO_STATUS_EOF) {
156 g_message ("EOF on stdin");
158 } else if (status != G_IO_STATUS_NORMAL) {
159 g_error ("Unexpected stdin read status: %d", status);
160 } else if (read != sizeof (header)) {
161 g_error ("Unexpected read size: %" G_GSIZE_FORMAT, read);
162 } else if (header.size > 8192) {
163 g_error ("Unexpected size: %u", header.size);
166 status = g_io_channel_read_chars (channel, buffer, header.size, &read, &err);
167 if (status == G_IO_STATUS_ERROR) {
168 g_error ("Failed to read from stdin: %s", err->message);
169 } else if (status == G_IO_STATUS_EOF) {
170 g_message ("EOF on stdin");
172 } else if (status != G_IO_STATUS_NORMAL) {
173 g_error ("Unexpected stdin read status: %d", status);
174 } else if (read != header.size) {
175 g_error ("Unexpected read size: %" G_GSIZE_FORMAT, read);
178 switch (header.type) {
182 g_socket_send_to (header.type ==
183 TYPE_EVENT ? socket_event : socket_general,
184 (header.type == TYPE_EVENT ? event_saddr : general_saddr), buffer,
185 header.size, NULL, &err);
187 g_error ("Failed to write to socket: %s", err->message);
188 else if (written != header.size)
189 g_error ("Unexpected write size: %" G_GSSIZE_FORMAT, written);
192 g_message ("Sent %" G_GSSIZE_FORMAT " bytes to %s socket", read,
193 (header.type == TYPE_EVENT ? "event" : "general"));
199 return G_SOURCE_CONTINUE;
205 GInetAddress *bind_addr, *mcast_addr;
206 GSocketAddress *bind_saddr;
207 GSource *socket_event_source, *socket_general_source;
208 gchar **probed_ifaces = NULL;
213 g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
214 G_SOCKET_PROTOCOL_UDP, &err);
216 g_error ("Couldn't create event socket: %s", err->message);
219 g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
220 G_SOCKET_PROTOCOL_UDP, &err);
222 g_error ("Couldn't create general socket: %s", err->message);
225 bind_addr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
226 bind_saddr = g_inet_socket_address_new (bind_addr, PTP_EVENT_PORT);
227 if (!g_socket_bind (socket_event, bind_saddr, TRUE, &err))
228 g_error ("Couldn't bind event socket: %s", err->message);
229 g_object_unref (bind_saddr);
230 bind_saddr = g_inet_socket_address_new (bind_addr, PTP_GENERAL_PORT);
231 if (!g_socket_bind (socket_general, bind_saddr, TRUE, &err))
232 g_error ("Couldn't bind general socket: %s", err->message);
233 g_object_unref (bind_saddr);
234 g_object_unref (bind_addr);
236 /* Probe all non-loopback interfaces */
242 ifc.ifc_len = sizeof (buf);
244 if (ioctl (g_socket_get_fd (socket_event), SIOCGIFCONF, &ifc) != -1) {
245 struct ifreq *it = ifc.ifc_req;
246 const struct ifreq *const end =
247 it + (ifc.ifc_len / sizeof (struct ifreq));
250 probed_ifaces = g_new0 (gchar *, ifc.ifc_len + 1);
252 for (; it != end; ++it) {
253 strcpy (ifr.ifr_name, it->ifr_name);
254 if (ioctl (g_socket_get_fd (socket_event), SIOCGIFFLAGS, &ifr) == 0) {
255 if ((ifr.ifr_flags & IFF_LOOPBACK))
257 probed_ifaces[idx] = g_strdup (it->ifr_name);
260 g_warning ("can't get flags of interface '%s'", it->ifr_name);
261 probed_ifaces[idx] = g_strdup (it->ifr_name);
267 ifaces = probed_ifaces;
271 /* Get a clock id from the MAC address if none was given */
272 if (clock_id == (guint64) - 1) {
274 gboolean success = FALSE;
277 gchar **ptr = ifaces;
280 strcpy (ifr.ifr_name, *ptr);
281 if (ioctl (g_socket_get_fd (socket_event), SIOCGIFHWADDR, &ifr) == 0) {
282 clock_id_array[0] = ifr.ifr_hwaddr.sa_data[0];
283 clock_id_array[1] = ifr.ifr_hwaddr.sa_data[1];
284 clock_id_array[2] = ifr.ifr_hwaddr.sa_data[2];
285 clock_id_array[3] = 0xff;
286 clock_id_array[4] = 0xfe;
287 clock_id_array[5] = ifr.ifr_hwaddr.sa_data[3];
288 clock_id_array[6] = ifr.ifr_hwaddr.sa_data[4];
289 clock_id_array[7] = ifr.ifr_hwaddr.sa_data[5];
300 ifc.ifc_len = sizeof (buf);
302 if (ioctl (g_socket_get_fd (socket_event), SIOCGIFCONF, &ifc) != -1) {
303 struct ifreq *it = ifc.ifc_req;
304 const struct ifreq *const end =
305 it + (ifc.ifc_len / sizeof (struct ifreq));
307 for (; it != end; ++it) {
308 strcpy (ifr.ifr_name, it->ifr_name);
309 if (ioctl (g_socket_get_fd (socket_event), SIOCGIFFLAGS, &ifr) == 0) {
310 if ((ifr.ifr_flags & IFF_LOOPBACK))
313 if (ioctl (g_socket_get_fd (socket_event), SIOCGIFHWADDR,
315 clock_id_array[0] = ifr.ifr_hwaddr.sa_data[0];
316 clock_id_array[1] = ifr.ifr_hwaddr.sa_data[1];
317 clock_id_array[2] = ifr.ifr_hwaddr.sa_data[2];
318 clock_id_array[3] = 0xff;
319 clock_id_array[4] = 0xfe;
320 clock_id_array[5] = ifr.ifr_hwaddr.sa_data[3];
321 clock_id_array[6] = ifr.ifr_hwaddr.sa_data[4];
322 clock_id_array[7] = ifr.ifr_hwaddr.sa_data[5];
327 g_warning ("can't get flags of interface '%s'", it->ifr_name);
334 g_warning ("can't get any MAC address, using random clock id");
335 clock_id = (((guint64) g_random_int ()) << 32) | (g_random_int ());
336 GST_WRITE_UINT64_BE (clock_id_array, clock_id);
337 clock_id_array[3] = 0xff;
338 clock_id_array[4] = 0xfe;
341 GST_WRITE_UINT64_BE (clock_id_array, clock_id);
344 /* Join multicast groups */
345 mcast_addr = g_inet_address_new_from_string (PTP_MULTICAST_GROUP);
347 gchar **ptr = ifaces;
348 gboolean success = FALSE;
352 if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, *ptr,
354 g_warning ("Couldn't join multicast group on interface '%s': %s",
359 if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE,
361 g_warning ("Couldn't join multicast group on interface '%s': %s",
372 /* Join multicast group without any interface */
373 if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, NULL,
375 g_error ("Couldn't join multicast group: %s", err->message);
376 if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE,
378 g_error ("Couldn't join multicast group: %s", err->message);
381 /* Join multicast group without any interface */
382 if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, NULL,
384 g_error ("Couldn't join multicast group: %s", err->message);
385 if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE, NULL,
387 g_error ("Couldn't join multicast group: %s", err->message);
390 event_saddr = g_inet_socket_address_new (mcast_addr, PTP_EVENT_PORT);
391 general_saddr = g_inet_socket_address_new (mcast_addr, PTP_GENERAL_PORT);
393 /* Create socket sources */
394 socket_event_source =
395 g_socket_create_source (socket_event, G_IO_IN | G_IO_PRI, NULL);
396 g_source_set_priority (socket_event_source, G_PRIORITY_HIGH);
397 g_source_set_callback (socket_event_source, (GSourceFunc) have_socket_data_cb,
399 g_source_attach (socket_event_source, NULL);
400 socket_general_source =
401 g_socket_create_source (socket_general, G_IO_IN | G_IO_PRI, NULL);
402 g_source_set_priority (socket_general_source, G_PRIORITY_DEFAULT);
403 g_source_set_callback (socket_general_source,
404 (GSourceFunc) have_socket_data_cb, NULL, NULL);
405 g_source_attach (socket_general_source, NULL);
407 g_strfreev (probed_ifaces);
411 drop_privileges (void)
413 #ifdef HAVE_PTP_HELPER_SETUID
414 /* Switch to the given user/group */
415 #ifdef HAVE_PTP_HELPER_SETUID_GROUP
419 grp = getgrnam (HAVE_PTP_HELPER_SETUID_GROUP);
421 g_error ("Failed to get group information '%s': %s",
422 HAVE_PTP_HELPER_SETUID_GROUP, g_strerror (errno));
424 if (setgid (grp->gr_gid) != 0)
425 g_error ("Failed to change to group '%s': %s",
426 HAVE_PTP_HELPER_SETUID_GROUP, g_strerror (errno));
430 #ifdef HAVE_PTP_HELPER_SETUID_USER
434 pwd = getpwnam (HAVE_PTP_HELPER_SETUID_USER);
436 g_error ("Failed to get user information '%s': %s",
437 HAVE_PTP_HELPER_SETUID_USER, g_strerror (errno));
439 #ifndef HAVE_PTP_HELPER_SETUID_GROUP
440 if (setgid (pwd->pw_gid) != 0)
441 g_error ("Failed to change to user group '%s': %s",
442 HAVE_PTP_HELPER_SETUID_USER, g_strerror (errno));
445 if (setuid (pwd->pw_uid) != 0)
446 g_error ("Failed to change to user '%s': %s", HAVE_PTP_HELPER_SETUID_USER,
451 #ifdef HAVE_PTP_HELPER_CAPABILITIES
452 /* Drop all capabilities */
456 caps = cap_get_proc ();
458 g_error ("Failed to get process caps: %s", g_strerror (errno));
459 if (cap_clear (caps) != 0)
460 g_error ("Failed to clear caps: %s", g_strerror (errno));
461 if (cap_set_proc (caps) != 0)
462 g_error ("Failed to set process caps: %s", g_strerror (errno));
468 setup_stdio_channels (void)
470 GSource *stdin_source;
472 /* Create stdin source */
473 stdin_channel = g_io_channel_unix_new (STDIN_FILENO);
474 if (g_io_channel_set_encoding (stdin_channel, NULL,
475 NULL) == G_IO_STATUS_ERROR)
476 g_error ("Failed to set stdin to binary encoding");
477 g_io_channel_set_buffered (stdin_channel, FALSE);
479 g_io_create_watch (stdin_channel, G_IO_IN | G_IO_PRI | G_IO_HUP);
480 g_source_set_priority (stdin_source, G_PRIORITY_DEFAULT);
481 g_source_set_callback (stdin_source, (GSourceFunc) have_stdin_data_cb, NULL,
483 g_source_attach (stdin_source, NULL);
485 /* Create stdout channel */
486 stdout_channel = g_io_channel_unix_new (STDOUT_FILENO);
487 if (g_io_channel_set_encoding (stdout_channel, NULL,
488 NULL) == G_IO_STATUS_ERROR)
489 g_error ("Failed to set stdout to binary encoding");
490 g_io_channel_set_buffered (stdout_channel, FALSE);
494 write_clock_id (void)
498 StdIOHeader header = { 0, };
501 /* Write clock id to stdout */
503 header.type = TYPE_CLOCK_ID;
506 g_io_channel_write_chars (stdout_channel, (gchar *) & header,
507 sizeof (header), &written, &err);
508 if (status == G_IO_STATUS_ERROR) {
509 g_error ("Failed to write to stdout: %s", err->message);
510 } else if (status == G_IO_STATUS_EOF) {
511 g_message ("EOF on stdout");
513 } else if (status != G_IO_STATUS_NORMAL) {
514 g_error ("Unexpected stdout write status: %d", status);
515 } else if (written != sizeof (header)) {
516 g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written);
520 g_io_channel_write_chars (stdout_channel,
521 (const gchar *) clock_id_array, sizeof (clock_id_array), &written, &err);
522 if (status == G_IO_STATUS_ERROR) {
523 g_error ("Failed to write to stdout: %s", err->message);
524 } else if (status == G_IO_STATUS_EOF) {
525 g_message ("EOF on stdout");
527 } else if (status != G_IO_STATUS_NORMAL) {
528 g_error ("Unexpected stdout write status: %d", status);
529 } else if (written != sizeof (clock_id_array)) {
530 g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written);
535 main (gint argc, gchar ** argv)
537 GOptionContext *opt_ctx;
541 opt_ctx = g_option_context_new ("- GStreamer PTP helper process");
542 g_option_context_add_main_entries (opt_ctx, opt_entries, NULL);
543 if (!g_option_context_parse (opt_ctx, &argc, &argv, &err))
544 g_error ("Error parsing options: %s", err->message);
545 g_option_context_free (opt_ctx);
549 setup_stdio_channels ();
553 loop = g_main_loop_new (NULL, FALSE);
554 g_main_loop_run (loop);
556 /* We never exit cleanly, so don't do cleanup */
557 g_assert_not_reached ();