From a2c2a595f2588a53b7c804e688f459e8a1c5efaf Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 25 Mar 2011 15:01:49 -0400 Subject: First cut at shared-memory export. Daemon-side only. Not configured in by default yet because it produces a strange regression failure. --- Makefile.am | 1 + configure.ac | 40 ++++++++++++++++++----------- gps.h | 4 +++ gpsd.c | 18 +++++++++++++ gpsd.h-tail | 17 +++++++++++++ libgpsd_core.c | 3 +++ shmexport.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 148 insertions(+), 14 deletions(-) create mode 100644 shmexport.c diff --git a/Makefile.am b/Makefile.am index 9f5fa08d..2484c1e2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -182,6 +182,7 @@ libgpsd_c_sources = \ packet.c \ pseudonmea.c \ serial.c \ + shmexport.c \ srecord.c \ subframe.c \ drivers.c \ diff --git a/configure.ac b/configure.ac index 7c540922..9ba3705c 100644 --- a/configure.ac +++ b/configure.ac @@ -570,19 +570,6 @@ fi AM_CONDITIONAL([CLIENTDEBUG_ENABLE], [test x"$ac_clientdebug" = x"yes"]) -dnl check for socket export support -AC_ARG_ENABLE(socket_export, - AC_HELP_STRING([--disable-socket-export], - [disable data export over sockets]), - [ac_socket_export=$enableval], [ac_socket_export=yes]) -AC_MSG_CHECKING([for socket-export support]) -if test x"$ac_socket_export" = "xyes"; then - AC_MSG_RESULT([yes]) - AC_DEFINE([SOCKET_EXPORT_ENABLE], 1, [socket-export support]) -else - AC_MSG_RESULT([no]) -fi - dnl check for support for oldstyle protocol AC_ARG_ENABLE(oldstyle, AC_HELP_STRING([--disable-oldstyle], @@ -710,7 +697,18 @@ else AC_MSG_RESULT([no]) fi - +dnl check for socket export support +AC_ARG_ENABLE(socket-export, + AC_HELP_STRING([--disable-socket-export], + [disable data export over sockets]), + [ac_socket_export=$enableval], [ac_socket_export=yes]) +AC_MSG_CHECKING([for socket-export support]) +if test x"$ac_socket_export" = "xyes"; then + AC_MSG_RESULT([yes]) + AC_DEFINE([SOCKET_EXPORT_ENABLE], 1, [socket-export support]) +else + AC_MSG_RESULT([no]) +fi dnl Manually configure DBUS until we figure out a dnl distro-independent was to check for both libraries and headers @@ -736,6 +734,19 @@ else AC_MSG_RESULT([no]) fi +dnl check for shm export support +AC_ARG_ENABLE(shm-export, + AC_HELP_STRING([--enable-shm-export], + [enable export via shared memory]), + [ac_shm_export=$enableval], [ac_shm_export=no]) +AC_MSG_CHECKING([for shared-memory-export support]) +if test x"$ac_shm_export" == "xyes"; then + AC_MSG_RESULT([yes]) + AC_DEFINE([SHM_EXPORT_ENABLE], 1, [shared-memory-export support]) +else + AC_MSG_RESULT([no]) +fi + dnl check for limited maximum clients AC_ARG_ENABLE(max-clients, AC_HELP_STRING([--enable-max-clients=nnn], @@ -1004,6 +1015,7 @@ dnl Below this line are non-protocol switches echo " Daemon Features" echo " ---------------" echo "Socket export : $ac_socket_export" +echo "Shared-memory export : $ac_shm_export" echo "DBUS export : $ac_dbus_export" echo "Timing support : $ac_timing" echo "NTP SHM : $ac_ntpshm" diff --git a/gps.h b/gps.h index 34a24e4a..7f31e787 100644 --- a/gps.h +++ b/gps.h @@ -1437,6 +1437,10 @@ extern int gps_stream(struct gps_data_t *, unsigned int, /*@null@*/void *); extern const char /*@observer@*/ *gps_data(struct gps_data_t *); extern const char /*@observer@*/ *gps_errstr(const int); +extern int gps_shm_open(/*@out@*/struct gps_data_t *); +extern int gps_shm_read(/*@out@*/struct gps_data_t *); +extern int gps_shm_close(struct gps_data_t *); + /* this only needs to be visible for the unit tests */ extern int gps_unpack(char *, struct gps_data_t *); diff --git a/gpsd.c b/gpsd.c index f494dee9..c9cb79b6 100644 --- a/gpsd.c +++ b/gpsd.c @@ -1507,6 +1507,12 @@ static void consume_packets(struct gps_device_t *device) #endif /* DBUS_EXPORT_ENABLE */ } +#ifdef SHM_EXPORT_ENABLE + if ((changed & (REPORT_IS|NOISE_IS|SATELLITE_IS|SUBFRAME_IS| + ATT_IS|RTCM2_IS|RTCM3_IS|AIS_IS)) != 0) + shm_update(&context, &device->gpsdata); +#endif /* DBUS_EXPORT_ENABLE */ + #ifdef SOCKET_EXPORT_ENABLE /* update all subscribers associated with this device */ for (sub = subscribers; sub < subscribers + MAXSUBSCRIBERS; sub++) { @@ -1854,6 +1860,14 @@ int main(int argc, char *argv[]) "successfully connected to the DBUS system bus\n"); #endif /* DBUS_EXPORT_ENABLE */ +#ifdef SHM_EXPORT_ENABLE + /* create the shared segment as root so readers can't mess with it */ + if (!shm_acquire(&context)) { + gpsd_report(LOG_ERROR, "shared-segment creation failed,\n"); + } else + gpsd_report(LOG_PROG, "shared-segment creation succeeded,\n"); +#endif /* DBUS_EXPORT_ENABLE */ + if (getuid() == 0 && go_background) { struct passwd *pw; struct stat stb; @@ -2223,6 +2237,10 @@ int main(int argc, char *argv[]) } #endif /* SOCKET_EXPORT_ENABLE */ +#ifdef SHM_EXPORT_ENABLE + shm_release(&context); +#endif /* DBUS_EXPORT_ENABLE */ + if (control_socket) (void)unlink(control_socket); if (pid_file) diff --git a/gpsd.h-tail b/gpsd.h-tail index 026bc617..f8f46b19 100644 --- a/gpsd.h-tail +++ b/gpsd.h-tail @@ -207,6 +207,11 @@ struct gps_context_t { bool shmTimePPS; # endif /* PPS_ENABLE */ #endif /* NTPSHM_ENABLE */ +#ifdef SHM_EXPORT_ENABLE + /* we don't want the compiler to treat writes to shmexport as dead code, + * and we don't want them reordered either */ + /*@reldef@*/volatile char *shmexport; +#endif }; struct aivdm_context_t { @@ -705,6 +710,18 @@ extern void ecef_to_wgs84fix(/*@out@*/struct gps_fix_t *, double, double, double); extern void clear_dop(/*@out@*/struct dop_t *); +/* shmexport.c */ +#define GPSD_KEY 0x47505344 /* "GPSD" */ +struct shmexport_t +{ + int bookend1; + struct gps_data_t gpsdata; + int bookend2; +}; +extern bool shm_acquire(struct gps_context_t *); +extern void shm_release(struct gps_context_t *); +extern void shm_update(struct gps_context_t *, struct gps_data_t *); + /* srecord.c */ extern void hexdump(size_t, unsigned char *, unsigned char *); extern unsigned char sr_sum(unsigned int, unsigned int, unsigned char *); diff --git a/libgpsd_core.c b/libgpsd_core.c index 05757b17..a4bc9815 100644 --- a/libgpsd_core.c +++ b/libgpsd_core.c @@ -105,6 +105,9 @@ void gps_context_init(struct gps_context_t *context) .shmTimePPS = false, # endif /* PPS_ENABLE */ #endif /* NTPSHM_ENABLE */ +#ifdef SHM_EXPORT_ENABLE + .shmexport = NULL, +#endif /* SHM_EXPORT_ENABLE */ }; /*@ +initallelements +nullassign +nullderef @*/ /* *INDENT-ON* */ diff --git a/shmexport.c b/shmexport.c new file mode 100644 index 00000000..b9c330c4 --- /dev/null +++ b/shmexport.c @@ -0,0 +1,79 @@ +/**************************************************************************** + +NAME + shmexport.c - shared-memory exports from the daemon and how to read them + +DESCRIPTION + + +PERMISSIONS + This file is Copyright (c) 2010 by the GPSD project + BSD terms apply: see the file COPYING in the distribution root for details. + +***************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include "gpsd.h" + +#ifdef SHM_EXPORT_ENABLE +bool shm_acquire(struct gps_context_t *context) +/* initialize the shared-memory segment to be used for export */ +{ + int shmid; + + shmid = shmget((key_t)GPSD_KEY, sizeof(struct gps_data_t), (int)(IPC_CREAT|0666)); + if (shmid == -1) { + gpsd_report(LOG_ERROR, "shmget(%ld, %zd, 0666) failed: %s\n", + (long int)GPSD_KEY, + sizeof(struct gps_data_t), + strerror(errno)); + return false; + } + context->shmexport = (char *)shmat(shmid, 0, 0); + /*@ -mustfreefresh */ + if ((int)(long)context->shmexport == -1) { + gpsd_report(LOG_ERROR, "shmat failed: %s\n", strerror(errno)); + context->shmexport = NULL; + return false; + } + gpsd_report(LOG_PROG, "shmat() succeeded, segment %d\n", shmid); + return true; +} + +void shm_release(struct gps_context_t *context) +/* release the shared-memory segment used for export */ +{ + if (context->shmexport != NULL) + (void)shmdt((const void *)context->shmexport); +} + +void shm_update(struct gps_context_t *context, struct gps_data_t *gpsdata) +/* export an update to all listeners */ +{ + if (context->shmexport != NULL) + { + static int tick; + + ++tick; + /* + * Following block of instructions must not be reordered, otherwise + * havoc will ensue. asm volatile("sfence") is a GCCism intended + * to prevent reordering. + */ + ((struct shmexport_t *)context)->bookend2 = tick; + asm volatile("sfence"); + memcpy((void *)(context->shmexport + offsetof(struct shmexport_t, gpsdata)), + (void *)gpsdata, + sizeof(struct gps_data_t)); + asm volatile("sfence"); + ((struct shmexport_t *)context)->bookend1 = tick; + } +} +#endif /* SHM_EXPORT_ENABLE */ + +/* end */ -- cgit v1.2.1