mbgtools-lx  4.2.8
mbgsetsystime.c
Go to the documentation of this file.
1 
2 /**************************************************************************
3  *
4  * $Id: mbgsetsystime.c 1.12 2018/11/15 12:12:35 martin TRASH $
5  *
6  * Description:
7  * Main file for mbgsetsystime program which reads the current date
8  * and time from a Meinberg device and sets the system time accordingly.
9  * The program returns 0 if successfull, otherwise a value greater
10  * than 0.
11  *
12  * Be careful if using this program while (x)ntpd is running because
13  * (x)ntpd does not like time steps and may react unexpectedly.
14  *
15  * -----------------------------------------------------------------------
16  * $Log: mbgsetsystime.c $
17  * Revision 1.12 2018/11/15 12:12:35 martin
18  * Individual MBG_MICRO_VERSION codes are now obsolete.
19  * Revision 1.11 2018/10/30 14:49:55 martin
20  * Account for renamed library function in Windows-specific code.
21  * Revision 1.10 2018/01/15 18:21:49Z martin
22  * Changed return type of snprintf_timespec() to int.
23  * Account for renamed library symbol NSEC_PER_SEC.
24  * Revision 1.9 2017/07/05 18:24:44 martin
25  * New way to maintain version information.
26  * Support build under Windows.
27  * Use high resolution time if the device supports it.
28  * Use codes and inline functions from mbgerror.h.
29  * Use functions from new module timeutil.
30  * Account for frac_sec_from_bin() obsoleted by bin_frac_32_to_dec_frac().
31  * Proper return codes and exit codes.
32  * Revision 1.8 2009/09/29 15:02:15 martin
33  * Updated version number to 3.4.0.
34  * Revision 1.7 2009/07/24 09:50:09 martin
35  * Updated version number to 3.3.0.
36  * Revision 1.6 2009/06/19 12:38:52 martin
37  * Updated version number to 3.2.0.
38  * Revision 1.5 2009/03/19 17:04:26 martin
39  * Updated version number to 3.1.0.
40  * Updated copyright year to include 2009.
41  * Revision 1.4 2008/12/22 12:43:31 martin
42  * Updated description, copyright, revision number and string.
43  * Use unified functions from toolutil module.
44  * Accept device name(s) on the command line.
45  * Don't use printf() without format, which migth produce warnings
46  * with newer gcc versions.
47  * Revision 1.3 2007/07/24 09:33:01 martin
48  * Updated copyright to include 2007.
49  * Revision 1.2 2003/04/25 10:28:05 martin
50  * Use new functions from mbgdevio library.
51  * New program version v2.1.
52  * Revision 1.1 2001/09/17 15:08:46 martin
53  *
54  **************************************************************************/
55 
56 // include Meinberg headers
57 #include <mbgdevio.h>
58 #include <pcpsmktm.h>
59 #include <pcpsutil.h>
60 #include <toolutil.h>
61 #include <timeutil.h>
62 #include <str_util.h>
63 #include <mbgerror.h>
64 
65 // include system headers
66 #include <stdio.h>
67 #include <stdlib.h>
68 
69 #include <sys/types.h>
70 
71 
72 #define MBG_FIRST_COPYRIGHT_YEAR 2001
73 #define MBG_LAST_COPYRIGHT_YEAR 0 // use default
74 
75 static const char *pname = "mbgsetsystime";
76 
77 
78 
79 static /*HDR*/
80 int sn_printf_timespec( char *s, size_t max_len, const struct timespec *p_ts )
81 {
82  struct tm tm = { 0 };
83  int rc = mbg_gmtime( &tm, &p_ts->tv_sec );
84 
85  if ( mbg_rc_is_error( rc ) ) // conversion failed
86  return sn_cpy_str_safe( s, max_len, "(invalid time)" );
87 
88  return snprintf_safe( s, max_len, "%04i-%02i-%02i %02i:%02i:%02i.%09li UTC",
89  tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
90  tm.tm_hour, tm.tm_min, tm.tm_sec,
91  (long) p_ts->tv_nsec );
92 
93 } // sn_printf_timespec
94 
95 
96 
97 static /*HDR*/
98 int set_system_time( const struct timespec *p_ts )
99 {
100  char ws[100];
101 
102 #if defined( MBG_TGT_WIN32 )
103  #define clock_settime mbg_clock_settime //### TODO cleanup
104 #endif
105 
106  int rc = clock_settime( CLOCK_REALTIME, p_ts );
107 
108  sn_printf_timespec( ws, sizeof( ws ), p_ts );
109 
110  if ( rc < 0 ) // usually 0 on success, -1 on error
111  {
112  rc = mbg_get_last_error( NULL );
113 
114  fprintf( stderr, "Failed to set system time to %s: %s\n", ws, mbg_strerror( rc ) );
115  return rc;
116  }
117 
118  printf( "Date/time set to %s\n", ws );
119 
120  return MBG_SUCCESS;
121 
122 } // set_system_time
123 
124 
125 
126 static /*HDR*/
128 {
129  struct tm tm = { 0 };
130  time_t t_rev;
131  time_t t;
132  int rc;
133 
134  t = pcps_mktime( p_t );
135 
136  if ( t == (time_t) -1 ) // error
137  {
138  fprintf( stderr, "Failed to convert %02u.%02u.%02u %02u:%02u:%02u.%02u (UTC%+02ih) to system time\n",
139  p_t->mday, p_t->month, p_t->year,
140  p_t->hour, p_t->min, p_t->sec, p_t->sec100,
141  p_t->offs_utc
142  );
143  return MBG_ERR_OVERFLOW;
144  }
145 
146  t_rev = t + p_t->offs_utc * SECS_PER_HOUR;
147 
148  rc = mbg_gmtime( &tm, &t_rev );
149 
150  if ( mbg_rc_is_error( rc ) )
151  return rc;
152 
153  if ( ( tm.tm_year % 100 != p_t->year ) ||
154  ( tm.tm_mon + 1 != p_t->month ) ||
155  ( tm.tm_mday != p_t->mday ) ||
156  ( tm.tm_hour != p_t->hour ) ||
157  ( tm.tm_min != p_t->min ) ||
158  ( tm.tm_sec != p_t->sec ) )
159  {
160  fprintf( stderr, "reversely computed date/time differs from original\n" );
161  return MBG_ERR_RANGE;
162  }
163 
164  #if defined( MBG_TGT_WIN32 )
165  {
166  SYSTEMTIME st;
167 
168  union
169  {
170  FILETIME ft;
171  ULONGLONG ull;
172  } u;
173 
174  // Convert seconds and fractions to 100 ns units.
175  u.ull = FILETIME_1970 +
176  (ULONGLONG) t * 10 * 1000 * 1000 +
177  (ULONGLONG) (ulong) p_t->sec100 * 100000;
178 
179  if ( !FileTimeToSystemTime( &u.ft, &st ) )
180  {
181  int err = mbg_win32_sys_err_to_mbg( GetLastError(), NULL );
182 
183  fprintf( stderr, "Failed to convert FILETIME to system time: %s\n",
184  mbg_strerror( err ) );
185  return err;
186  }
187 
188  if ( !SetSystemTime( &st ) )
189  {
190  #if 0
191  LPVOID lpMsgBuf;
192  DWORD last_error = GetLastError();
193 
194  FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
195  FORMAT_MESSAGE_FROM_SYSTEM |
196  FORMAT_MESSAGE_IGNORE_INSERTS,
197  NULL,
198  last_error,
199  MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language
200  (LPTSTR) &lpMsgBuf,
201  0,
202  NULL
203  );
204 
205  fprintf( stderr, "Failed to set system time: %s (code 0x%08lX)\n",
206  lpMsgBuf, (ulong) last_error );
207 
208  // Free the buffer.
209  LocalFree( lpMsgBuf );
210  #else
211  int err = mbg_win32_sys_err_to_mbg( GetLastError(), NULL );
212 
213  fprintf( stderr, "Failed to set system time: err %i\n", err );
214  #endif
215  return err;
216  }
217 
218  return MBG_SUCCESS;
219  }
220  #else // assuming POSIX
221  {
222  struct timespec ts;
223 
224  ts.tv_sec = t;
225  ts.tv_nsec = (long) p_t->sec100 * ( NSEC_PER_SEC / 100 ); // convert to nanoseconds
226 
227  return set_system_time( &ts );
228  }
229  #endif
230 
231 } // set_system_time_from_pcps_time
232 
233 
234 
235 static /*HDR*/
237 {
238  PCPS_TIME t;
239 
240  int rc = mbg_get_time( dh, &t );
241 
242  if ( mbg_cond_err_msg( rc, "mbg_get_time" ) )
243  return rc;
244 
245  if ( t.status & PCPS_INVT )
246  {
247  // This may happen if the radio clock's battery
248  // has been discharged or disconnected.
249  printf( "Radio clock has no valid date/time.\n" );
250  return MBG_ERR_INV_TIME;
251  }
252 
254 
255  return rc;
256 
257 } // do_set_system_time_from_pcps_time
258 
259 
260 
261 static /*HDR*/
263 {
264  PCPS_HR_TIME ht;
265  struct timespec ts = { 0 };
266 
267  int rc = mbg_get_hr_time( dh, &ht );
268 
269  if ( mbg_cond_err_msg( rc, "mbg_get_hr_time" ) )
270  return rc;
271 
272  if ( ht.status & PCPS_INVT )
273  {
274  // This may happen if the radio clock's battery
275  // has been discharged or disconnected.
276  printf( "Radio clock has no valid date/time.\n" );
277  return MBG_ERR_INV_TIME;
278  }
279 
280  ts.tv_sec = ht.tstamp.sec;
281  ts.tv_nsec = bin_frac_32_to_dec_frac( ht.tstamp.frac, NSEC_PER_SEC );
282 
283  return set_system_time( &ts );
284 
285 } // do_set_system_time_from_pcps_hr_time
286 
287 
288 
289 static /*HDR*/
290 int do_mbgsetsystime( MBG_DEV_HANDLE dh, const PCPS_DEV *p_dev )
291 {
292  static int system_time_has_been_set;
293 
294  int rc = MBG_SUCCESS;
295 
296  if ( system_time_has_been_set )
297  goto done;
298 
299  rc = mbg_chk_dev_has_hr_time( dh );
300 
301  if ( mbg_rc_is_success( rc ) )
303  else
305 
306  if ( mbg_rc_is_success( rc ) )
307  system_time_has_been_set = 1;
308 
309  puts( "" );
310 
311 done:
312  return rc;
313 
314 } // do_mbgsetsystime
315 
317 
318 
319 
320 static /*HDR*/
321 void usage( void )
322 {
324  "This program can be used to set the system time to the card's time.\n"
325  "This should be done only at boot time, before the NTP daemon is started.\n"
326  "Please *don't* run this program while ntpd is already active."
327  );
330  puts( "" );
331 
332 } // usage
333 
334 
335 
336 int main( int argc, char *argv[] )
337 {
338  int c;
339  int rc;
340 
342 
343  // check command line parameters
344  while ( ( c = getopt( argc, argv, "h?" ) ) != -1 )
345  {
346  switch ( c )
347  {
348  case 'h':
349  case '?':
350  default:
351  must_print_usage = 1;
352  }
353  }
354 
355  if ( must_print_usage )
356  {
357  usage();
358  return MBG_EXIT_CODE_USAGE;
359  }
360 
361  // Handle each of the specified devices.
362  rc = mbg_handle_devices( argc, argv, optind, do_mbgsetsystime, 0 );
363 
364  // determine the exit code based on the return code
365 
366  if ( mbg_rc_is_success( rc ) )
367  return MBG_EXIT_CODE_SUCCESS; // success
368 
369  if ( rc == MBG_ERR_INV_TIME )
370  return MBG_EXIT_CODE_INV_TIME; // device has no valid time to set the system time with
371 
372  return MBG_EXIT_CODE_FAIL; // any error occurred
373 }
static int set_system_time(const struct timespec *p_ts)
Definition: mbgsetsystime.c:98
invalid time because battery had been disconnected, or absolute time can&#39;t be decoded safely ...
Definition: pcpsdefs.h:1261
uint8_t year
year of the century, 0..99
Definition: pcpsdefs.h:1138
int mbg_handle_devices(int argc, char *argv[], int optind, MBG_DEV_HANDLER_FNC *fnc, int chk_dev_flags)
Main action handler that can be called by utility programs.
Definition: toolutil.c:655
int must_print_usage
uint8_t mday
day of month, 0..31
Definition: pcpsdefs.h:1135
uint8_t sec100
hundredths of seconds, 0..99, 10 ms resolution
Definition: pcpsdefs.h:1130
Requested action completed successfully.
Definition: mbgerror.h:631
static int do_set_system_time_from_pcps_hr_time(MBG_DEV_HANDLE dh)
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
PCPS_SECONDS sec
seconds since 1970, usually UTC scale
Definition: pcpsdefs.h:974
_MBG_API_ATTR int _MBG_API mbg_get_time(MBG_DEV_HANDLE dh, PCPS_TIME *p)
Read a PCPS_TIME structure returning the current date/time/status.
Definition: mbgdevio.c:4491
Unable to handle requested action, usage printed.
Definition: mbgerror.h:632
int sn_cpy_str_safe(char *dst, size_t max_len, const char *src)
A function to copy a string safely, returning the number of characters copied.
Definition: str_util.c:276
_MBG_API_ATTR int _MBG_API mbg_get_hr_time(MBG_DEV_HANDLE dh, PCPS_HR_TIME *p)
Read the card&#39;s current time with high resolution, including status.
Definition: mbgdevio.c:4625
#define mbg_rc_is_success(_rc)
Definition: mbgerror.h:618
#define MBG_ERR_RANGE
input parameter was out of range
Definition: mbgerror.h:347
MBG_CHK_SUPP_FNC mbg_chk_dev_has_hr_time
Check if a device supports the mbg_get_hr_time... functions.
Definition: mbgdevio.c:1360
#define MBG_FIRST_COPYRIGHT_YEAR
Definition: mbgsetsystime.c:72
void mbg_print_device_options(void)
Print common info on how to specify devices on the command line.
Definition: toolutil.c:270
void mbg_print_usage_intro(const char *pname, const char *info)
Print usage intro to console.
Definition: toolutil.c:217
PCPS_TIME_STATUS_X status
status bits, see PCPS_TIME_STATUS_FLAGS
Definition: pcpsdefs.h:1089
#define SECS_PER_HOUR
Definition: mbgtime.h:242
static int set_system_time_from_pcps_time(const PCPS_TIME *p_t)
static MBG_DEV_HANDLER_FNC do_mbgsetsystime
static void usage(void)
PCPS_FRAC_32 frac
binary fractions of second, see PCPS_FRAC_32
Definition: pcpsdefs.h:975
Device has no valid time to set the system time with.
Definition: mbgerror.h:635
PCPS_TIME_STAMP tstamp
High resolution time stamp (UTC)
Definition: pcpsdefs.h:1087
int main(int argc, char *argv[])
#define FILETIME
Definition: mbgdevio.h:456
int8_t offs_utc
[hours], 0 if not _pcps_has_utc_offs
Definition: pcpsdefs.h:1142
#define MBG_ERR_INV_TIME
The device has no valid time.
Definition: mbgerror.h:275
int MBG_DEV_HANDLER_FNC(MBG_DEV_HANDLE, const PCPS_DEV *)
The type of functions to called to handle a device in a specific way.
Definition: toolutil.h:144
#define MBG_SUCCESS
Error codes used with Meinberg devices and drivers.
Definition: mbgerror.h:259
static __mbg_inline int mbg_gmtime(struct tm *p_tm, const time_t *p_time)
Definition: timeutil.h:118
uint8_t hour
hours, 0..23
Definition: pcpsdefs.h:1133
uint32_t DWORD
Definition: mbgerror.h:222
Action failed for specified device.
Definition: mbgerror.h:634
int mbg_win32_sys_err_to_mbg(DWORD win32_sys_rc, const char *info)
Translate a Windows non-socket API return code to one of the MBG_RETURN_CODES.
static const char * pname
Definition: mbgsetsystime.c:75
#define MBG_LAST_COPYRIGHT_YEAR
Definition: mbgsetsystime.c:73
static __mbg_inline uint32_t bin_frac_32_to_dec_frac(uint32_t bin, uint32_t scale)
Convert a 32 bit binary fraction to a scaled decimal.
Definition: mbgtime.h:439
High resolution time including status and local time offset.
Definition: pcpsdefs.h:1085
void mbg_print_help_options(void)
Print info on common program help arguments.
Definition: toolutil.c:257
#define NSEC_PER_SEC
Definition: mbgtime.h:264
Local calendar date and time, plus sync status.
Definition: pcpsdefs.h:1128
static int do_set_system_time_from_pcps_time(MBG_DEV_HANDLE dh)
int snprintf_safe(char *s, size_t max_len, const char *fmt,...)
A portable, safe implementation of snprintf()
Definition: str_util.c:156
#define MBG_ERR_OVERFLOW
range or buffer overflow
Definition: mbgerror.h:333
#define mbg_rc_is_error(_rc)
Definition: mbgerror.h:617
uint8_t min
minutes, 0..59
Definition: pcpsdefs.h:1132
void mbg_print_program_info(const char *pname, int first_year, int last_year)
Print program info to console.
Definition: toolutil.c:193
const char * mbg_strerror(int mbg_errno)
Return an error string associated with the MBG_ERROR_CODES.
Definition: mbgerror.c:685
unsigned long ulong
Definition: words.h:292
static int sn_printf_timespec(char *s, size_t max_len, const struct timespec *p_ts)
Definition: mbgsetsystime.c:80
int mbg_get_last_error(const char *info)
Get and translate last error after non-socket function call.
Definition: mbgerror.c:993
uint8_t month
month, 1..12
Definition: pcpsdefs.h:1137
Device info structure.
Definition: pcpsdev.h:1043
PCPS_TIME_STATUS status
status bits, see PCPS_TIME_STATUS_FLAGS_COMMON
Definition: pcpsdefs.h:1140
time_t pcps_mktime(const PCPS_TIME *tp)
Compute a linear time_t value from PCPS_TIME.
Definition: pcpsmktm.c:49
uint8_t sec
seconds, 0..59, or 60 if leap second
Definition: pcpsdefs.h:1131