ptp: Initial implementation of a PTP clock
[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_PTP_HELPER_SETUID
45 #include <grp.h>
46 #include <pwd.h>
47 #endif
48
49 #ifdef HAVE_PTP_HELPER_CAPABILITIES
50 #include <sys/capability.h>
51 #endif
52
53 #include <glib.h>
54 #include <gio/gio.h>
55
56 #include <gst/gst.h>
57 #include <gst/net/gstptp_private.h>
58
59 #define PTP_MULTICAST_GROUP "224.0.1.129"
60 #define PTP_EVENT_PORT   319
61 #define PTP_GENERAL_PORT 320
62
63 static gchar **ifaces = NULL;
64 static gboolean verbose = FALSE;
65 static guint64 clock_id = (guint64) - 1;
66 static guint8 clock_id_array[8];
67
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,
74       "Be verbose", NULL},
75   {NULL}
76 };
77
78 static GSocketAddress *event_saddr, *general_saddr;
79 static GSocket *socket_event, *socket_general;
80 static GIOChannel *stdin_channel, *stdout_channel;
81
82 static gboolean
83 have_socket_data_cb (GSocket * socket, GIOCondition condition,
84     gpointer user_data)
85 {
86   gchar buffer[8192];
87   gssize read;
88   gsize written;
89   GError *err = NULL;
90   GIOStatus status;
91   StdIOHeader header = { 0, };
92
93   read = g_socket_receive (socket, buffer, sizeof (buffer), NULL, &err);
94   if (read == -1)
95     g_error ("Failed to read from socket: %s", err->message);
96
97   if (verbose)
98     g_message ("Received %" G_GSSIZE_FORMAT " bytes from %s socket", read,
99         (socket == socket_event ? "event" : "general"));
100
101   header.size = read;
102   header.type = (socket == socket_event) ? TYPE_EVENT : TYPE_GENERAL;
103
104   status =
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");
111     exit (0);
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);
116   }
117
118   status =
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");
124     exit (0);
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);
129   }
130
131   return G_SOURCE_CONTINUE;
132 }
133
134 static gboolean
135 have_stdin_data_cb (GIOChannel * channel, GIOCondition condition,
136     gpointer user_data)
137 {
138   GIOStatus status;
139   StdIOHeader header = { 0, };
140   gchar buffer[8192];
141   GError *err = NULL;
142   gsize read;
143   gssize written;
144
145   if ((condition & G_IO_STATUS_EOF)) {
146     g_message ("EOF on stdin");
147     exit (0);
148   }
149
150   status =
151       g_io_channel_read_chars (channel, (gchar *) & header, sizeof (header),
152       &read, &err);
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");
157     exit (0);
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);
164   }
165
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");
171     exit (0);
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);
176   }
177
178   switch (header.type) {
179     case TYPE_EVENT:
180     case TYPE_GENERAL:
181       written =
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);
186       if (written == -1)
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);
190
191       if (verbose)
192         g_message ("Sent %" G_GSSIZE_FORMAT " bytes to %s socket", read,
193             (header.type == TYPE_EVENT ? "event" : "general"));
194       break;
195     default:
196       break;
197   }
198
199   return G_SOURCE_CONTINUE;
200 }
201
202 static void
203 setup_sockets (void)
204 {
205   GInetAddress *bind_addr, *mcast_addr;
206   GSocketAddress *bind_saddr;
207   GSource *socket_event_source, *socket_general_source;
208   gchar **probed_ifaces = NULL;
209   GError *err = NULL;
210
211   /* Create sockets */
212   socket_event =
213       g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
214       G_SOCKET_PROTOCOL_UDP, &err);
215   if (!socket_event)
216     g_error ("Couldn't create event socket: %s", err->message);
217
218   socket_general =
219       g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
220       G_SOCKET_PROTOCOL_UDP, &err);
221   if (!socket_general)
222     g_error ("Couldn't create general socket: %s", err->message);
223
224   /* Bind sockets */
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);
235
236   /* Probe all non-loopback interfaces */
237   if (!ifaces) {
238     struct ifreq ifr;
239     struct ifconf ifc;
240     gchar buf[8192];
241
242     ifc.ifc_len = sizeof (buf);
243     ifc.ifc_buf = 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));
248       guint idx = 0;
249
250       probed_ifaces = g_new0 (gchar *, ifc.ifc_len + 1);
251
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))
256             continue;
257           probed_ifaces[idx] = g_strdup (it->ifr_name);
258           idx++;
259         } else {
260           g_warning ("can't get flags of interface '%s'", it->ifr_name);
261           probed_ifaces[idx] = g_strdup (it->ifr_name);
262           idx++;
263         }
264       }
265
266       if (idx != 0)
267         ifaces = probed_ifaces;
268     }
269   }
270
271   /* Get a clock id from the MAC address if none was given */
272   if (clock_id == (guint64) - 1) {
273     struct ifreq ifr;
274     gboolean success = FALSE;
275
276     if (ifaces) {
277       gchar **ptr = ifaces;
278
279       while (*ptr) {
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];
290           success = TRUE;
291           break;
292         }
293       }
294
295       ptr++;
296     } else {
297       struct ifconf ifc;
298       gchar buf[8192];
299
300       ifc.ifc_len = sizeof (buf);
301       ifc.ifc_buf = 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));
306
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))
311               continue;
312
313             if (ioctl (g_socket_get_fd (socket_event), SIOCGIFHWADDR,
314                     &ifr) == 0) {
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];
323               success = TRUE;
324               break;
325             }
326           } else {
327             g_warning ("can't get flags of interface '%s'", it->ifr_name);
328           }
329         }
330       }
331     }
332
333     if (!success) {
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;
339     }
340   } else {
341     GST_WRITE_UINT64_BE (clock_id_array, clock_id);
342   }
343
344   /* Join multicast groups */
345   mcast_addr = g_inet_address_new_from_string (PTP_MULTICAST_GROUP);
346   if (ifaces) {
347     gchar **ptr = ifaces;
348     gboolean success = FALSE;
349
350     while (*ptr) {
351       gint c = 0;
352       if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, *ptr,
353               &err))
354         g_warning ("Couldn't join multicast group on interface '%s': %s",
355             *ptr, err->message);
356       else
357         c++;
358
359       if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE,
360               *ptr, &err))
361         g_warning ("Couldn't join multicast group on interface '%s': %s",
362             *ptr, err->message);
363       else
364         c++;
365
366       if (c == 2)
367         success = TRUE;
368       ptr++;
369     }
370
371     if (!success) {
372       /* Join multicast group without any interface */
373       if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, NULL,
374               &err))
375         g_error ("Couldn't join multicast group: %s", err->message);
376       if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE,
377               NULL, &err))
378         g_error ("Couldn't join multicast group: %s", err->message);
379     }
380   } else {
381     /* Join multicast group without any interface */
382     if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, NULL,
383             &err))
384       g_error ("Couldn't join multicast group: %s", err->message);
385     if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE, NULL,
386             &err))
387       g_error ("Couldn't join multicast group: %s", err->message);
388   }
389
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);
392
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,
398       NULL, NULL);
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);
406
407   g_strfreev (probed_ifaces);
408 }
409
410 static void
411 drop_privileges (void)
412 {
413 #ifdef HAVE_PTP_HELPER_SETUID
414   /* Switch to the given user/group */
415 #ifdef HAVE_PTP_HELPER_SETUID_GROUP
416   {
417     struct group *grp;
418
419     grp = getgrnam (HAVE_PTP_HELPER_SETUID_GROUP);
420     if (!grp)
421       g_error ("Failed to get group information '%s': %s",
422           HAVE_PTP_HELPER_SETUID_GROUP, g_strerror (errno));
423
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));
427   }
428 #endif
429
430 #ifdef HAVE_PTP_HELPER_SETUID_USER
431   {
432     struct passwd *pwd;
433
434     pwd = getpwnam (HAVE_PTP_HELPER_SETUID_USER);
435     if (!pwd)
436       g_error ("Failed to get user information '%s': %s",
437           HAVE_PTP_HELPER_SETUID_USER, g_strerror (errno));
438
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));
443 #endif
444
445     if (setuid (pwd->pw_uid) != 0)
446       g_error ("Failed to change to user '%s': %s", HAVE_PTP_HELPER_SETUID_USER,
447           g_strerror (errno));
448   }
449 #endif
450 #endif
451 #ifdef HAVE_PTP_HELPER_CAPABILITIES
452   /* Drop all capabilities */
453   {
454     cap_t caps;
455
456     caps = cap_get_proc ();
457     if (caps == 0)
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));
463   }
464 #endif
465 }
466
467 static void
468 setup_stdio_channels (void)
469 {
470   GSource *stdin_source;
471
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);
478   stdin_source =
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,
482       NULL);
483   g_source_attach (stdin_source, NULL);
484
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);
491 }
492
493 static void
494 write_clock_id (void)
495 {
496   GError *err = NULL;
497   GIOStatus status;
498   StdIOHeader header = { 0, };
499   gsize written;
500
501   /* Write clock id to stdout */
502
503   header.type = TYPE_CLOCK_ID;
504   header.size = 8;
505   status =
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");
512     exit (0);
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);
517   }
518
519   status =
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");
526     exit (0);
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);
531   }
532 }
533
534 gint
535 main (gint argc, gchar ** argv)
536 {
537   GOptionContext *opt_ctx;
538   GMainLoop *loop;
539   GError *err = NULL;
540
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);
546
547   setup_sockets ();
548   drop_privileges ();
549   setup_stdio_channels ();
550   write_clock_id ();
551
552   /* Get running */
553   loop = g_main_loop_new (NULL, FALSE);
554   g_main_loop_run (loop);
555
556   /* We never exit cleanly, so don't do cleanup */
557   g_assert_not_reached ();
558
559   return 0;
560 }