mbgtools-lx  4.2.8
mbgsvcd.c
Go to the documentation of this file.
1 
2 /**************************************************************************
3  *
4  * $Id: mbgsvcd.c 1.6 2019/02/11 09:57:20 martin TRASH $
5  *
6  * Description:
7  * Main file for mbgsvcd which compares the system time to a PCI card's
8  * time and transfers this data pair to the SHM driver of the ntpd.
9  *
10  * -----------------------------------------------------------------------
11  * $Log: mbgsvcd.c $
12  * Revision 1.6 2019/02/11 09:57:20 martin
13  * Account for renamed library symbols.
14  * Use an individual filter for each device.
15  * Revision 1.5 2018/11/15 12:12:33 martin
16  * Individual MBG_MICRO_VERSION codes are now obsolete.
17  * Revision 1.4 2017/07/05 18:45:29 martin
18  * New way to maintain version information.
19  * Print PC cycles counter frequency at program start.
20  * Use /var/run as directory for the lockfile.
21  * Using generic MBG_SYS_TIME with nanosecond resolution.
22  * Workaround in case cycle frequency can not be determined.
23  * Compute execution time limit in cycles instead of us so this can also
24  * be done if the cycle counter clock rate can not be determined.
25  * Skip devices which don't support HR time immediately at startup.
26  * Log reasons for error if function calls fail.
27  * Combined printf() and syslog() to mbg_log().
28  * Added leap second support.
29  * Moved some code to some extra modules which can be shared.
30  * Use seconds for the trust time.
31  * Support options -q and -r.
32  * Use more functions from common library modules.
33  * Use codes and inline functions from mbgerror.h.
34  * Patch submitted by <juergen.perlinger@t-online.de>:
35  * Support variable number of SHM units and number
36  * of the first unit to use.
37  * Proper return codes and exit codes.
38  * Revision 1.3 2010/03/03 14:59:36 martin
39  * Support -p parameter to pretend sync.
40  * Revision 1.2 2010/02/03 16:15:09 daniel
41  * Revision 1.1 2010/02/03 16:07:18 daniel
42  *
43  **************************************************************************/
44 
45 // include Meinberg headers
46 #include <mbgdevio.h>
47 #include <pcpsutil.h>
48 #include <toolutil.h> // common utility functions
49 #include <mbgerror.h>
50 #include <pcpsmktm.h>
51 #include <chk_time_info.h>
52 #include <ntp_shm.h>
53 
54 // include system headers
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <pthread.h>
59 #include <arpa/inet.h>
60 #include <sys/socket.h>
61 #include <sys/types.h>
62 #include <sys/time.h>
63 #include <sys/stat.h>
64 #include <unistd.h>
65 #include <errno.h>
66 #include <fcntl.h>
67 #include <signal.h>
68 #include <syslog.h>
69 #include <stdarg.h>
70 #include <time.h>
71 #include <sys/time.h>
72 #include <sys/ipc.h>
73 #include <sys/shm.h>
74 
75 #define RUNNING_DIR "/var/run"
76 #define LOCK_FILE "mbgsvcd.pid"
77 
78 #define MBG_FIRST_COPYRIGHT_YEAR 2010
79 #define MBG_LAST_COPYRIGHT_YEAR 0 // use default
80 
81 static const char *pname = "mbgsvcd";
82 
83 static int foreground; // -f
84 static int quiet; // -q
85 static int pretend_sync; // -p
86 static int print_raw; // -r
87 static int sleep_intv = 1; // -s
88 static unsigned long trust_time_seconds; // -t
89 static int n_unit0 = 0; // -o
90 static int n_units = MAX_SHM_REFCLOCKS; // -n
91 static int frac_digits = 9;
92 
94 
95 
98 
100 static long int ref_trust_time_start[MAX_SHM_REFCLOCKS] = { 0 };
101 static long int ref_trust_time_expire[MAX_SHM_REFCLOCKS] = { 0 };
102 
103 
104 
105 /*HDR*/
106 void mbg_log( int lvl, const char *fmt, ... )
107 {
108  char ws[256];
109  va_list ap;
110 
111  va_start( ap, fmt );
112  vsnprintf( ws, sizeof( ws ), fmt, ap );
113  va_end( ap );
114 
115  syslog( lvl, "%s", ws );
116  fprintf( stdout, "%s\n", ws );
117 
118 } // mbg_log
119 
120 
121 
122 static /*HDR*/
123 int do_mbgsvctasks( void )
124 {
125  char ws[256];
126  int rc = 0;
127  int n_devices_found;
128  int n_devices;
131  int i;
132 
133  n_devices_found = mbg_find_devices();
134 
135  for ( i = 0, n_devices = 0; i < n_devices_found; i++ )
136  {
138  PCPS_DEV dev_info;
139 
140  rc = mbg_get_device_info( dh, &dev_info );
141 
142  if ( rc < 0 )
143  {
144  mbg_log( LOG_WARNING, "Failed to read device info from device #%i.", i );
145  mbg_close_device( &dh );
146  continue;
147  }
148 
149  if ( !_pcps_has_hr_time( &dev_info ) )
150  {
151  mbg_log( LOG_WARNING, "Device %s does not support HR time stamps.",
152  _pcps_type_name( &dev_info ) );
153  mbg_close_device( &dh );
154  continue;
155  }
156 
157  dhs[n_devices] = dh;
158  devs[n_devices] = dev_info;
159 
160  if ( ++n_devices >= n_units )
161  break;
162  }
163 
164  if ( n_devices == 0 )
165  {
166  mbg_log( LOG_WARNING, "No usable device found!" );
167  goto done;
168  }
169 
170 
171  // Search for devices up to the maximum of supported number of NTP SHM refclocks
172  if ( n_devices > MAX_SHM_REFCLOCKS )
173  n_devices = MAX_SHM_REFCLOCKS;
174 
175  mbg_log( LOG_INFO, "Found %d devices usable for the NTP daemon", n_devices ); //##++++
176 
178 
179  if ( mbg_cond_err_msg( rc, "mbg_get_default_cycles_frequency_from_dev" ) )
180  goto done;
181 
182 
183  mbg_log( LOG_INFO, "%sPC cycles counter clock frequency: %Lu Hz",
184  ( cyc_freq == 0 ) ? "*** Warning: " : "",
185  (unsigned long long) cyc_freq );
186 
187  // Initialize NTP shared memory area
188  ntpshm_init( shmTime, n_devices, n_unit0 );
189 
190  for (;;)
191  {
192  for ( i = 0; i < n_devices; i++ )
193  {
194  MBG_CHK_TIME_INFO cti;
195  const char *cp;
196  int leap;
197 
198  rc = mbg_chk_time_info( dhs[i], &cti, &filter[i], 0 );
199 
200  if ( mbg_cond_err_msg( rc, "mbg_chk_time_info" ) )
201  continue;
202 
203  cp = "";
204  leap = 0;
205 
206  if ( ( has_synced_after_reset[i] == 1 ) && ( ( ( cti.hrti.ref_hr_time_cycles.t.status & PCPS_FREER ) == 1 )
207  || ( ( cti.hrti.ref_hr_time_cycles.t.status & PCPS_SYNCD ) == 0 ) ))
208  {
209  struct timespec ts;
210 
211  clock_gettime(CLOCK_MONOTONIC, &ts);
212 
213  if (ref_trust_time_start[i] == 0 )
214  {
215  mbg_log( LOG_WARNING, "Device #%i, entering holdover mode", i );
216  ref_trust_time_start[i] = ts.tv_sec;
217  ref_trust_time_expire[i] = ts.tv_sec + trust_time_seconds;
218  }
219 
220  if ( ts.tv_sec > ref_trust_time_expire[i] )
221  {
222  mbg_log( LOG_WARNING, "Device #%i, trust time expired", i );
223  has_synced_after_reset[i] = 0;
224  ref_trust_time_start[i] = 0;
225  ref_trust_time_expire[i] = 0;
226  }
227  }
228 
229  // check if refclock is sync and if exec time of the system time call was fast enough
230  if ( ( cti.exec_cyc <= cti.exec_cyc_limit ) && ( pretend_sync || has_synced_after_reset[i] || (
231  ( ( cti.hrti.ref_hr_time_cycles.t.status & PCPS_FREER ) == 0 ) &&
232  ( ( cti.hrti.ref_hr_time_cycles.t.status & PCPS_SYNCD ) != 0 ) ) ) )
233  {
234  struct shmTime *p = shmTime[i];
235 
236  has_synced_after_reset[i] = 1;
237 
238  if ( p )
239  {
240  MBG_SYS_TIME_CYCLES *p_sys_tic = &cti.hrti.sys_time_cycles;
241  cp = " *";
242 
243  // fill SHM structure
244  p->count++;
245  p->clockTimeStampSec = (time_t) cti.d_ref_comp;
246  p->clockTimeStampUSec = (int) ( ( cti.d_ref_comp - p->clockTimeStampSec ) * 1e6 ); // get microseconds from d_ref
247  p->receiveTimeStampSec = (time_t) p_sys_tic->sys_time.secs;
248  p->receiveTimeStampUSec = (int) ( p_sys_tic->sys_time.nano_secs / 1000 );
249 
250  // These fields are only supported by newer versions of ntpd //##++++++++++++++++++ which versions ?
251  p->clockTimeStampNSec = (int) ( ( cti.d_ref_comp - p->clockTimeStampSec ) * 1e9 ); // get nanoseconds from d_ref
252  p->receiveTimeStampNSec = (int) ( p_sys_tic->sys_time.nano_secs );
253 
254  // patch precision value according to the ref time accuracy
255  if ( _pcps_is_lwr( &devs[i] ) )
256  p->precision = -8;
257  else
258  {
259  if ( _pcps_is_irig_rx( &devs[i] ) )
260  {
261  if ( _pcps_is_usb( &devs[i] ) )
262  p->precision = -10;
263  else
264  p->precision = -18;
265  }
266  else
267  p->precision = -20;
268  }
269 
271  p->leap = LEAP_DELSECOND;
272  else
274  p->leap = LEAP_ADDSECOND;
275  else
276  p->leap = LEAP_NOWARNING;
277 
278  leap = p->leap; //##+++
279 
280  p->count++;
281  p->valid = 1;
282  }
283  }
284 
285  if ( !quiet )
286  {
287  snprint_chk_time_info( ws, sizeof( ws ), &cti, &devs[i], frac_digits, print_raw );
288  printf( "%s, leap: %02X%s\n", ws, leap, cp );
289  }
290 
291  usleep( 10 );
292  }
293 
294  if ( n_devices > 1 )
295  printf( "\n" );
296 
297  if ( sleep_intv )
298  sleep( sleep_intv );
299  }
300 
301 done:
302  for ( i = 0; i < n_devices; i++ )
303  mbg_close_device( &dhs[i] );
304 
305  return rc;
306 
307 } // do_mbgsvctasks
308 
309 
310 
311 static /*HDR*/
312 void usage( void )
313 {
315  "This program periodically reads a reference time stamp and an associated\n"
316  "system time stamp from every mbgclock device, and feeds the time stamp pairs\n"
317  "to the NTP daemon's shared memory refclock driver.\n"
318  "It usually runs as daemon but can also be run in the foreground to monitor the\n"
319  "time stamps and offsets.\n"
320  "This works only for cards supporting high resolution time stamps.\n"
321  );
323  mbg_print_opt_info( "-f", "run program in foreground" );
324  mbg_print_opt_info( "-q", "quiet, don't print time differences on stdout" );
325  mbg_print_opt_info( "-r", "print raw time stamps when printing on stdout" );
326  mbg_print_opt_info( "-s num", "sleep num seconds between calls" );
327  mbg_print_opt_info( "-p", "pretend device is always synchronized" );
328  mbg_print_opt_info( "-t num", "set num seconds for refclock trust time, default 4 days (345600 seconds)" );
329  mbg_print_opt_info( "-n num", "number of SHM segments to use" );
330  mbg_print_opt_info( "-o num", "SHM segment number offset (default 0)" );
332  puts( "" );
333 
334 } // usage
335 
336 
337 
338 static /*HDR*/
339 void startup_daemon( void )
340 {
341  int i;
342  int lfp;
343  int rc;
344  char str[1024];
345 
346  if ( getppid() == 1 )
347  return; /* already a daemon */
348 
349  mbg_log( LOG_INFO, "Daemon mode, backgrounding" );
350  i = fork();
351 
352  if ( i < 0 )
353  exit( 1 ); /* fork error */
354 
355  if ( i > 0 )
356  exit( 0 ); /* parent exits */
357 
358 
359  /* child (daemon) continues */
360  setsid(); /* obtain a new process group */
361 
362  for ( i = getdtablesize(); i >= 0; --i )
363  close( i ); /* close all descriptors */
364 
365  /* handle standard I/O */
366  i = open( "/dev/null", O_RDWR );
367  rc = dup( i );
368  rc = dup( i );
369 
370  umask( 027 ); /* set newly created file permissions */
371  rc = chdir( RUNNING_DIR ); /* change running directory */
372 
373  lfp = open( LOCK_FILE, O_RDWR | O_CREAT, 0640 );
374 
375  if ( lfp < 0 )
376  exit( 1 ); /* unable to open lock file */
377 
378  if ( lockf( lfp, F_TLOCK, 0 ) < 0 )
379  {
380  mbg_log( LOG_ERR, "Lock file already exists, another instance of this daemon seems to be running" );
381  closelog();
382  exit( 0 ); /* can not lock */
383  }
384 
385  /* first instance continues */
386  snprintf( str, sizeof( str ), "%d\n", getpid() );
387  rc = write( lfp, str, strlen( str ) ); /* record pid to lockfile */
388 
389  (void) rc; // avoid warning "set but not used"
390 
391  signal( SIGCHLD, SIG_IGN ); /* ignore child */
392  signal( SIGTSTP, SIG_IGN ); /* ignore tty signals */
393  signal( SIGTTOU, SIG_IGN );
394  signal( SIGTTIN, SIG_IGN );
395 
396 } // startup_daemon
397 
398 
399 
400 int main( int argc, char *argv[] )
401 {
402  int rc;
403  int c;
404 
406 
407  // check command line parameters
408  while ( ( c = getopt( argc, argv, "fpqrst:n:o:h?" ) ) != -1 )
409  {
410  switch ( c )
411  {
412  case 'f':
413  foreground = 1;
414  break;
415 
416  case 'p':
417  pretend_sync = 1;
418  break;
419 
420  case 'q':
421  quiet++;
422  break;
423 
424  case 'r':
425  print_raw = 1;
426  break;
427 
428  case 's':
429  sleep_intv = atoi( optarg );
430  break;
431 
432  case 't':
433  {
434  long tt = atol( optarg );
435 
436  if ( tt > 0 )
437  trust_time_seconds = tt;
438 
439  break;
440  }
441 
442  case 'n':
443  n_units = atoi( optarg );
444 
445  if ( n_units < 0 || n_units > MAX_SHM_REFCLOCKS )
446  {
447  mbg_log( LOG_WARNING, "Configured number of SHM units %i out of range, truncating to %i",
448  n_units, MAX_SHM_REFCLOCKS );
450  }
451 
452  break;
453 
454  case 'o':
455  n_unit0 = atoi( optarg );
456 
457  if ( n_unit0 < 0 || n_unit0 >= MAX_SHM_UNIT_OFFSET )
458  {
459  mbg_log( LOG_WARNING, "Configured SHM unit offset %i out of range %i to %i, truncating to %i",
460  n_unit0, 0, MAX_SHM_UNIT_OFFSET, 0 );
461  n_unit0 = 0;
462  }
463 
464  break;
465 
466  case 'h':
467  case '?':
468  default:
469  must_print_usage = 1;
470  }
471  }
472 
473  if ( must_print_usage )
474  {
475  usage();
476  return MBG_EXIT_CODE_USAGE;
477  }
478 
479  if ( foreground == 0 )
480  {
481  char ws[256];
482 
485 
486  mbg_log( LOG_INFO, "Starting Meinberg Service Daemon %s", ws );
487 
488  startup_daemon();
489  }
490 
491  if ( trust_time_seconds )
492  mbg_log( LOG_INFO, "refclock trust time: %ul seconds", trust_time_seconds );
493 
494  rc = do_mbgsvctasks();
495 
497 }
#define LEAP_NOWARNING
Definition: ntp_shm.h:95
_MBG_API_ATTR int _MBG_API mbg_find_devices(void)
Get the number of devices installed on the computer.
Definition: mbgdevio.c:3503
MBG_PC_CYCLES exec_cyc
Computed execution time of the call, in [cycles].
Definition: chk_time_info.h:88
MBG_SYS_TIME_CYCLES sys_time_cycles
system time stamp plus associated cycles
Definition: pcpsdev.h:1421
MBG_PC_CYCLES_FREQUENCY cyc_freq
Must be set up tby the application.
Definition: mbgsvcd.c:93
int must_print_usage
PCPS_HR_TIME t
Definition: pcpsdev.h:1395
#define _pcps_has_hr_time(_d)
Definition: pcpsdev.h:1149
Requested action completed successfully.
Definition: mbgerror.h:631
System time plus associated cycles counter values.
Definition: pcpsdev.h:386
#define LOCK_FILE
Definition: mbgsvcd.c:76
static int n_units
Definition: mbgsvcd.c:90
unsigned clockTimeStampNSec
Definition: ntp_shm.h:83
static int n_unit0
Definition: mbgsvcd.c:89
bool mbg_cond_err_msg(int rc, const char *what)
Check if a value is an error code and print an associated error message.
Definition: mbgerror.c:714
int precision
Definition: ntp_shm.h:80
static long int ref_trust_time_start[MAX_SHM_REFCLOCKS]
Definition: mbgsvcd.c:100
announced leap second is negative
Definition: pcpsdefs.h:1274
Unable to handle requested action, usage printed.
Definition: mbgerror.h:632
#define mbg_rc_is_success(_rc)
Definition: mbgerror.h:618
int main(int argc, char *argv[])
Definition: mbgsvcd.c:400
double d_ref_comp
Compensated reference timestamp in floating point format, [s].
Definition: chk_time_info.h:97
#define RUNNING_DIR
Definition: mbgsvcd.c:75
time_t receiveTimeStampSec
Definition: ntp_shm.h:77
static int do_mbgsvctasks(void)
Definition: mbgsvcd.c:123
int ntpshm_init(struct shmTime **shmTime, int n_units, int n_unit0)
Definition: ntp_shm.c:62
static int sleep_intv
Definition: mbgsvcd.c:87
void mbg_print_device_options(void)
Print common info on how to specify devices on the command line.
Definition: toolutil.c:270
MBG_TIME_INFO_HRT hrti
System time, device time, and cycles read from the driver.
Definition: chk_time_info.h:85
void mbg_print_usage_intro(const char *pname, const char *info)
Print usage intro to console.
Definition: toolutil.c:217
int receiveTimeStampUSec
Definition: ntp_shm.h:78
unsigned receiveTimeStampNSec
Definition: ntp_shm.h:84
A structure to store the results of the mbg_chk_time_info routine.
Definition: chk_time_info.h:83
PCPS_TIME_STATUS_X status
status bits, see PCPS_TIME_STATUS_FLAGS
Definition: pcpsdefs.h:1089
#define MAX_SHM_UNIT_OFFSET
Max. Number of SHM unit offset.
Definition: ntp_shm.h:112
int count
Definition: ntp_shm.h:74
static int quiet
Definition: mbgsvcd.c:84
_MBG_API_ATTR int _MBG_API mbg_get_default_cycles_frequency_from_dev(MBG_DEV_HANDLE dh, MBG_PC_CYCLES_FREQUENCY *p)
Retrieve the system&#39;s default cycles counter frequency from the kernel driver.
Definition: mbgdevio.c:9161
_MBG_API_ATTR int _MBG_API mbg_get_device_info(MBG_DEV_HANDLE dh, PCPS_DEV *p)
Read detailed device information.
Definition: mbgdevio.c:4242
_MBG_API_ATTR MBG_DEV_HANDLE _MBG_API mbg_open_device(int dev_idx)
Open a device by index number.
Definition: mbgdevio.c:3352
#define _pcps_is_lwr(_d)
Definition: pcpsdev.h:1083
MBG_PC_CYCLES exec_cyc_limit
Execution time limit, in [cycles], computed based on usual execution time.
Definition: chk_time_info.h:89
int mbg_program_info_str(char *s, size_t max_len, const char *pname, int first_year, int last_year)
Print some program info to a string buffer.
Definition: toolutil.c:160
int clockTimeStampUSec
Definition: ntp_shm.h:76
MBG_SYS_TIME sys_time
system time stamp
Definition: pcpsdev.h:390
#define MBG_LAST_COPYRIGHT_YEAR
Definition: mbgsvcd.c:79
long wave or time code receiver has sync&#39;ed at least once after pwr up, sat receiver is synchronized ...
Definition: pcpsdefs.h:1256
#define LEAP_DELSECOND
Definition: ntp_shm.h:97
int leap
see NTP Leap Bits
Definition: ntp_shm.h:79
#define _pcps_is_usb(_d)
Definition: pcpsdev.h:1089
static long int ref_trust_time_expire[MAX_SHM_REFCLOCKS]
Definition: mbgsvcd.c:101
static int frac_digits
Definition: mbgsvcd.c:91
PCPS_HR_TIME_CYCLES ref_hr_time_cycles
HR time read from the card, plus cycles.
Definition: pcpsdev.h:1420
_MBG_API_ATTR void _MBG_API mbg_close_device(MBG_DEV_HANDLE *dev_handle)
Close a device handle and set the handle value to MBG_INVALID_DEV_HANDLE.
Definition: mbgdevio.c:4190
Structure of NTP&#39;s shared memory segment.
Definition: ntp_shm.h:63
void mbg_log(int lvl, const char *fmt,...)
Definition: mbgsvcd.c:106
Action failed for specified device.
Definition: mbgerror.h:634
uint64_t MBG_PC_CYCLES_FREQUENCY
Definition: mbgpccyc.h:98
static int foreground
Definition: mbgsvcd.c:83
void mbg_print_help_options(void)
Print info on common program help arguments.
Definition: toolutil.c:257
int mbg_chk_time_info(MBG_DEV_HANDLE dh, MBG_CHK_TIME_INFO *p, CYCLES_FILTER_DATA *p_filter, int fast_ts_only)
Read and evaluate a system timestamp / reference timestamp pair.
static void usage(void)
Definition: mbgsvcd.c:312
static void startup_daemon(void)
Definition: mbgsvcd.c:339
#define _pcps_type_name(_d)
Definition: pcpsdev.h:1068
void mbg_print_opt_info(const char *opt_name, const char *opt_info)
Print info on a single program option / argument.
Definition: toolutil.c:236
static int has_synced_after_reset[MAX_SHM_REFCLOCKS]
Definition: mbgsvcd.c:99
static int print_raw
Definition: mbgsvcd.c:86
time_t clockTimeStampSec
Definition: ntp_shm.h:75
static const char * pname
Definition: mbgsvcd.c:81
int valid
Definition: ntp_shm.h:82
#define MBG_FIRST_COPYRIGHT_YEAR
Definition: mbgsvcd.c:78
void mbg_print_program_info(const char *pname, int first_year, int last_year)
Print program info to console.
Definition: toolutil.c:193
int64_t nano_secs
[nanoseconds]
Definition: words.h:628
int64_t secs
[seconds], usually since 1970-01-01 00:00:00
Definition: words.h:627
#define _pcps_is_irig_rx(_d)
Definition: pcpsdev.h:1078
int snprint_chk_time_info(char *s, size_t max_len, const MBG_CHK_TIME_INFO *p, const PCPS_DEV *p_dev, int frac_digits, int print_raw)
Print info from a MBG_CHK_TIME_INFO structure into a string buffer.
#define MAX_SHM_REFCLOCKS
Number of units (refclocks) supported by the SMH segment.
Definition: ntp_shm.h:106
A structure to keep filter data for cycles values.
Definition: chk_time_info.h:67
long wave or time code receiver running on xtal, satellite receiver has not verified its position ...
Definition: pcpsdefs.h:1254
static int pretend_sync
Definition: mbgsvcd.c:85
static CYCLES_FILTER_DATA filter[MAX_SHM_REFCLOCKS]
Definition: mbgsvcd.c:97
#define LEAP_ADDSECOND
Definition: ntp_shm.h:96
Device info structure.
Definition: pcpsdev.h:1043
static unsigned long trust_time_seconds
Definition: mbgsvcd.c:88
leap second announced, for very old clocks see REV_PCPS_LS_ANN_PC31PS31
Definition: pcpsdefs.h:1259