summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Burnicki <martin.burnicki@meinberg.de>2019-09-04 12:00:00 +0200
committerMartin Burnicki <martin.burnicki@meinberg.de>2019-09-04 12:00:00 +0200
commita859d75c21202b0237d6e9bffe0512656d435ff5 (patch)
tree9d5a9ffd36ecdbbf3f26851a90c4a67cbef2ef3d
parent54ad38deb28269fc8b74173a4f513adab13dd503 (diff)
downloadntptest-a859d75c21202b0237d6e9bffe0512656d435ff5.tar.gz
ntptest-a859d75c21202b0237d6e9bffe0512656d435ff5.zip
Specific exit codes, support openSSL v1.1.11.11
Return specific exit codes in case of error, and list the valid exit codes in the usage information. Support building against openSSL v1.1.1.
-rwxr-xr-xREADME40
-rwxr-xr-xmbglib/common/mbg_ntp_auth.c187
-rwxr-xr-xmbglib/common/mbg_ntp_auth.h6
-rwxr-xr-xmbglib/common/mbg_ntp_test_util.h99
-rwxr-xr-xmbglib/common/mbg_tgt.h160
-rwxr-xr-xmbglib/common/mbgerror.c355
-rwxr-xr-xmbglib/common/mbgerror.h303
-rwxr-xr-xmbglib/common/mbgtime.h187
-rwxr-xr-xmbglib/common/str_util.c63
-rwxr-xr-xmbglib/common/str_util.h115
-rwxr-xr-xmbglib/common/timeutil.c186
-rwxr-xr-xmbglib/common/timeutil.h252
-rwxr-xr-xmbglib/common/words.h22
-rwxr-xr-xntptest.c88
14 files changed, 1518 insertions, 545 deletions
diff --git a/README b/README
index edde188..a67f2b4 100755
--- a/README
+++ b/README
@@ -23,37 +23,37 @@ At least the hostname or IP address of a remote NTP server has to be
specified on the command line, e.g.:
------------------------------------------------------------------------------
-#ntptest> ./ntptest 172.16.100.110
+#ntptest> ./ntptest ntp1.py.meinberg.de
-ntptest v1.9, (c) Meinberg 2014-2018, contact: <martin.burnicki@meinberg.de>
+ntptest v1.11, (c) Meinberg 2014-2019, contact: <martin.burnicki@meinberg.de>
-Host 172.16.100.110
-Request packet:
- mode 3, version 4, leap 3, stratum 0, poll 6, prec -18 (3.81 us)
+Host ntp3.py.meinberg.de
+Request packet: mode 3 (client)
+ version 4, leap 3, stratum 0, poll 6, prec -18 (3.81 us)
root delay: 00000000 (0.000000 s)
root dispersion: 00000000 (0.000000 s/s)
reference id: 00000000 ("....")
Ref time: 00000000.00000000 1900-01-01 00:00:00.000000000
Org time (T1): 00000000.00000000 1900-01-01 00:00:00.000000000
Rcv time (T2): 00000000.00000000 1900-01-01 00:00:00.000000000
- Xmt time (T3): DE75990D.27C6F26D 2018-04-09 07:47:25.155379439
- Curr time (T4): DE75990D.27C6F26D 2018-04-09 07:47:25.155379439
+ Xmt time (T3): E119EA92.821A694D 2019-09-04 07:47:30.508215504
+ Curr time (T4): E119EA92.821A694D 2019-09-04 07:47:30.508215504
-Response packet:
- mode 4, version 4, leap 0, stratum 1, poll 6, prec -18 (3.81 us)
+Response packet: mode 4 (server)
+ version 4, leap 0, stratum 1, poll 6, prec -20 (954 ns)
root delay: 00000000 (0.000000 s)
- root dispersion: 00000048 (0.001099 s/s)
+ root dispersion: 0000000C (0.000183 s/s)
reference id: 0053524D ("MRS.")
- Ref time: DE759906.0961A292 2018-04-09 07:47:18.036646042
- Org time (T1): DE75990D.27C6F26D 2018-04-09 07:47:25.155379439
- Rcv time (T2): DE75990D.27C6366F 2018-04-09 07:47:25.155368234
- Xmt time (T3): DE75990D.27E9F68F 2018-04-09 07:47:25.155913743
- Curr time (T4): DE75990D.27FDB675 2018-04-09 07:47:25.156215098
-
-turnaround: 835.659 us (T4 - T1)
-server latency: 545.509 us (T3 - T2)
-computed delay: 290.150 us ((T4 - T1) - (T3 - T2))
-computed offset: -156.280 us (((T2 - T1) + (T3 - T4)) / 2)
+ Ref time: E119EA8D.096F0C83 2019-09-04 07:47:25.036850721
+ Org time (T1): E119EA92.821A694D 2019-09-04 07:47:30.508215504
+ Rcv time (T2): E119EA92.821FAF7B 2019-09-04 07:47:30.508295981
+ Xmt time (T3): E119EA92.82258D03 2019-09-04 07:47:30.508385480
+ Curr time (T4): E119EA92.822EE59F 2019-09-04 07:47:30.508528090
+
+turnaround: 312.586 us (T4 - T1)
+server latency: 89.498 us (T3 - T2)
+computed delay: 223.088 us ((T4 - T1) - (T3 - T2))
+computed offset: -31.067 us (((T2 - T1) + (T3 - T4)) / 2)
------------------------------------------------------------------------------
The output shows the contents of the request packet sent to the NTP server,
diff --git a/mbglib/common/mbg_ntp_auth.c b/mbglib/common/mbg_ntp_auth.c
index cc5dc69..d26069e 100755
--- a/mbglib/common/mbg_ntp_auth.c
+++ b/mbglib/common/mbg_ntp_auth.c
@@ -1,7 +1,7 @@
/**************************************************************************
*
- * $Id: mbg_ntp_auth.c 1.2 2014/09/24 08:55:12 martin REL_M $
+ * $Id: mbg_ntp_auth.c 1.4 2019/09/04 07:38:26 martin REL_M $
*
* Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
*
@@ -11,7 +11,17 @@
*
* -----------------------------------------------------------------------
* $Log: mbg_ntp_auth.c $
- * Revision 1.2 2014/09/24 08:55:12 martin
+ * Revision 1.4 2019/09/04 07:38:26 martin
+ * Openssl v1.1.1 support by Marius, but modified to avoid duplicate code.
+ * ereallocz() doesn't exit on error with an arbitrary exit code. Instead,
+ * it returns NULL, so the calling application can/has to handle the error,
+ * e.g. exit with an appropriate code.
+ * mbg_ntp_auth_init() now returns MBG_SUCCESS, or an appropriate
+ * Meinberg error code.
+ * Removed trailing spaces.
+ * Revision 1.3 2018/08/01 14:37:11 martin
+ * Fixed build on Windows.
+ * Revision 1.2 2014/09/24 08:55:12Z martin
* Full support of NTP symmetric key authentication.
* Export only Meinberg-specific functions which call
* the NTP functions internally.
@@ -27,24 +37,42 @@
#include <mbg_ntp_auth.h>
#undef _MBG_NTP_AUTH
+#include <mbgerror.h>
+
#include <stdlib.h>
#include <string.h>
-#include <arpa/inet.h>
#include <stddef.h> // offsetof()
#include <math.h> // log10()
-#include <syslog.h> // log levels
+
+#if !defined( MBG_TGT_WIN32 )
+ #include <syslog.h> // log levels
+ #include <arpa/inet.h>
+#else
+ #define LOG_ERR 0 // TODO
+ #define LOG_WARNING 1
+#endif
+
#include <ctype.h> // toupper()
+#if defined( MBG_TGT_WIN32 )
+ #define strdup _strdup
+#endif
+
#if NTP_COMPAT
#define TRUE 1
#define FALSE 0
- #define max(n1, n2) ( ( (n1) > (n2) ) ? (n1) : (n2) )
- #define min(n1, n2) ( ( (n1) < (n2) ) ? (n1) : (n2) )
+ #if defined( MBG_TGT_WIN32 )
+ #define zero_mem(p, s) memset(p, 0, s)
+ #else
+ #define zero_mem(p, s) bzero(p, s)
+ #define max(n1, n2) ( ( (n1) > (n2) ) ? (n1) : (n2) )
+ #define min(n1, n2) ( ( (n1) < (n2) ) ? (n1) : (n2) )
+ #endif
+
- #define zero_mem(p, s) bzero(p, s)
#define DEBUG_ENSURE( _v ) do {} while(0)
static u_long current_time; /* seconds since startup */
@@ -456,6 +484,27 @@ keytype_name(
}
+
+static /*HDR*/
+EVP_MD_CTX *get_md_ctx( void )
+{
+ EVP_MD_CTX *ctx;
+
+ #if OPENSSL_VERSION_NUMBER >= 0x010101000
+ ctx = EVP_MD_CTX_new();
+ #else
+ ctx = EVP_MD_CTX_create();
+ #endif
+
+ if ( ctx == NULL )
+ msyslog( LOG_ERR, "MAC encrypt: ctx init failed");
+
+ return ctx;
+
+} // get_md_ctx
+
+
+
/*
* keytype_from_text returns OpenSSL NID for digest by name, and
* optionally the associated digest length.
@@ -476,7 +525,7 @@ keytype_from_text(
u_char digest[EVP_MAX_MD_SIZE];
char * upcased;
char * pch;
- EVP_MD_CTX ctx;
+ EVP_MD_CTX * ctx;
/*
* OpenSSL digest short names are capitalized, so uppercase the
@@ -485,6 +534,12 @@ keytype_from_text(
* with past behavior.
*/
INIT_SSL();
+
+ ctx = get_md_ctx();
+
+ if ( ctx == NULL )
+ return 0;
+
#if 1 // NOTE: modification by martin
upcased = strdup( text );
if ( upcased )
@@ -515,8 +570,8 @@ keytype_from_text(
if (NULL != pdigest_len) {
#ifdef OPENSSL
- EVP_DigestInit(&ctx, EVP_get_digestbynid(key_type));
- EVP_DigestFinal(&ctx, digest, &digest_len);
+ EVP_DigestInit(ctx, EVP_get_digestbynid(key_type));
+ EVP_DigestFinal(ctx, digest, &digest_len);
if (digest_len > max_digest_len) {
fprintf(stderr,
"key type %s %u octet digests are too big, max %lu\n",
@@ -575,7 +630,7 @@ struct symkey_alloc_tag {
symkey_alloc * authallocs;
#endif /* DEBUG */
-static inline u_short auth_log2(double x);
+static __mbg_inline u_short auth_log2(double x);
static void auth_resize_hashtable(void);
static void allocsymkey(symkey **, keyid_t, u_short,
u_short, u_long, u_short, u_char *);
@@ -635,7 +690,7 @@ MD5authencrypt(
{
u_char digest[EVP_MAX_MD_SIZE];
u_int len;
- EVP_MD_CTX ctx;
+ EVP_MD_CTX * ctx;
/*
* Compute digest of key concatenated with packet. Note: the
@@ -643,18 +698,24 @@ MD5authencrypt(
* was creaded.
*/
INIT_SSL();
+
+ ctx = get_md_ctx();
+
+ if ( ctx == NULL )
+ return 0;
+
#if defined(OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x0090700fL
- if (!EVP_DigestInit(&ctx, EVP_get_digestbynid(type))) {
+ if (!EVP_DigestInit(ctx, EVP_get_digestbynid(type))) {
msyslog(LOG_ERR,
"MAC encrypt: digest init failed");
return (0);
}
#else
- EVP_DigestInit(&ctx, EVP_get_digestbynid(type));
+ EVP_DigestInit(ctx, EVP_get_digestbynid(type));
#endif
- EVP_DigestUpdate(&ctx, key, p_cache->secretsize);
- EVP_DigestUpdate(&ctx, (u_char *)pkt, (u_int)length);
- EVP_DigestFinal(&ctx, digest, &len);
+ EVP_DigestUpdate(ctx, key, p_cache->secretsize);
+ EVP_DigestUpdate(ctx, (u_char *)pkt, (u_int)length);
+ EVP_DigestFinal(ctx, digest, &len);
memmove((u_char *)pkt + length + 4, digest, len);
return (len + 4);
}
@@ -678,7 +739,7 @@ MD5authdecrypt(
{
u_char digest[EVP_MAX_MD_SIZE];
u_int len;
- EVP_MD_CTX ctx;
+ EVP_MD_CTX * ctx;
/*
* Compute digest of key concatenated with packet. Note: the
@@ -686,18 +747,24 @@ MD5authdecrypt(
* was created.
*/
INIT_SSL();
+
+ ctx = get_md_ctx();
+
+ if ( ctx == NULL )
+ return 0;
+
#if defined(OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x0090700fL
- if (!EVP_DigestInit(&ctx, EVP_get_digestbynid(type))) {
+ if (!EVP_DigestInit(ctx, EVP_get_digestbynid(type))) {
msyslog(LOG_ERR,
"MAC decrypt: digest init failed");
return (0);
}
#else
- EVP_DigestInit(&ctx, EVP_get_digestbynid(type));
+ EVP_DigestInit(ctx, EVP_get_digestbynid(type));
#endif
- EVP_DigestUpdate(&ctx, key, p_cache->secretsize);
- EVP_DigestUpdate(&ctx, (u_char *)pkt, (u_int)length);
- EVP_DigestFinal(&ctx, digest, &len);
+ EVP_DigestUpdate(ctx, key, p_cache->secretsize);
+ EVP_DigestUpdate(ctx, (u_char *)pkt, (u_int)length);
+ EVP_DigestFinal(ctx, digest, &len);
if ((u_int)size != len + 4) {
msyslog(LOG_ERR,
"MAC decrypt: MAC length error");
@@ -748,7 +815,7 @@ addr2refid(sockaddr_u *addr)
{
u_char digest[20];
u_int32 addr_refid;
- EVP_MD_CTX ctx;
+ EVP_MD_CTX * ctx;
u_int len;
if (IS_IPV4(addr))
@@ -756,24 +823,33 @@ addr2refid(sockaddr_u *addr)
INIT_SSL();
+ ctx = get_md_ctx();
+
+ if ctx == NULL
+ return 0;
+
+
#if defined(OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x0090700fL
- EVP_MD_CTX_init(&ctx);
-#ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
+ #ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
/* MD5 is not used as a crypto hash here. */
- EVP_MD_CTX_set_flags(&ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
-#endif
- if (!EVP_DigestInit_ex(&ctx, EVP_md5(), NULL)) {
+ EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
+ #endif
+ if (!EVP_DigestInit_ex(ctx, EVP_md5(), NULL)) {
msyslog(LOG_ERR,
"MD5 init failed");
+ // TODO We should not exit with an arbitrary code here.
+ // Instead, we should return an error to the caller
+ // and let the application determine an appropriate
+ // exit code.
exit(1);
}
#else
- EVP_DigestInit(&ctx, EVP_md5());
+ EVP_DigestInit(ctx, EVP_md5());
#endif
- EVP_DigestUpdate(&ctx, (u_char *)PSOCK_ADDR6(addr),
+ EVP_DigestUpdate(ctx, (u_char *)PSOCK_ADDR6(addr),
sizeof(struct in6_addr));
- EVP_DigestFinal(&ctx, digest, &len);
+ EVP_DigestFinal(ctx, digest, &len);
memcpy(&addr_refid, digest, sizeof(addr_refid));
return (addr_refid);
}
@@ -832,7 +908,7 @@ ereallocz(
"fatal out of memory %s line %d (%lu bytes)",
file, line, (u_long)newsz);
#endif
- exit(1);
+ return NULL;
}
if (zero_init && newsz > priorsz)
@@ -861,7 +937,7 @@ emalloc(size_t newsz)
* init_auth - initialize internal data
*/
static
-void
+int
init_auth(void)
{
size_t newalloc;
@@ -872,6 +948,13 @@ init_auth(void)
newalloc = authhashbuckets * sizeof(key_hash[0]);
key_hash = erealloc(key_hash, newalloc);
+
+ if ( key_hash == NULL )
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
memset(key_hash, '\0', newalloc);
INIT_DLIST(key_listhead, llink);
@@ -879,6 +962,8 @@ init_auth(void)
#ifdef DEBUG_KEYS
atexit(&free_auth_mem);
#endif
+
+ return 0; // success
}
@@ -971,7 +1056,7 @@ auth_prealloc_symkeys(
}
-static inline u_short
+static __mbg_inline u_short
auth_log2(double x)
{
return (u_short)(log10(x) / log10(2));
@@ -1229,7 +1314,7 @@ authtrust(
/*
* Key exists. If it is to be trusted, say so and
- * update its lifetime.
+ * update its lifetime.
*/
if (trust > 0) {
sk->flags |= KEY_TRUSTED;
@@ -1511,7 +1596,7 @@ nexttok(
*/
while (*cp == ' ' || *cp == '\t')
cp++;
-
+
/*
* Save this and space to end of token
*/
@@ -1519,19 +1604,19 @@ nexttok(
while (*cp != '\0' && *cp != '\n' && *cp != ' '
&& *cp != '\t' && *cp != '#')
cp++;
-
+
/*
* If token length is zero return an error, else set end of
* token to zero and return start.
*/
if (starttok == cp)
return NULL;
-
+
if (*cp == ' ' || *cp == '\t')
*cp++ = '\0';
else
*cp = '\0';
-
+
*str = cp;
return starttok;
}
@@ -1563,7 +1648,7 @@ authreadkeys(
if (fp == NULL) {
#if 1 // NOTE: modification by martin
mbglog( LOG_ERR, "Failed to read file %s: %s",
- file, strerror( errno ) );
+ file, strerror( errno ) );
#else
msyslog(LOG_ERR, "authreadkeys: file %s: %m",
file);
@@ -1584,7 +1669,7 @@ authreadkeys(
token = nexttok(&line);
if (token == NULL)
continue;
-
+
/*
* First is key number. See if it is okay.
*/
@@ -1613,7 +1698,7 @@ authreadkeys(
}
#ifdef OPENSSL
/*
- * The key type is the NID used by the message digest
+ * The key type is the NID used by the message digest
* algorithm. There are a number of inconsistencies in
* the OpenSSL database. We attempt to discover them
* here and prevent use of inconsistent data later.
@@ -1870,13 +1955,25 @@ int mbg_ntp_auth_trust_key( long key_id, int trust )
/*HDR*/
-void mbg_ntp_auth_init( void )
+/**
+ * @brief Initialize SSL and pre-allocate symmetric keys.
+ *
+ * @return ::MBG_SUCCESS on success, else one of the @ref MBG_ERROR_CODES
+ */
+ int mbg_ntp_auth_init( void )
{
+ int rc;
+
INIT_SSL();
- init_auth();
+ rc = init_auth();
+
+ if ( rc < 0 ) // error
+ return MBG_ERR_NO_MEM;
auth_prealloc_symkeys( 0 ); //##++++ TODO or the number of trusted keys we have determined before?
+ return MBG_SUCCESS;
+
} // mbg_ntp_auth_init
diff --git a/mbglib/common/mbg_ntp_auth.h b/mbglib/common/mbg_ntp_auth.h
index 5476df5..33cc405 100755
--- a/mbglib/common/mbg_ntp_auth.h
+++ b/mbglib/common/mbg_ntp_auth.h
@@ -1,7 +1,7 @@
/**************************************************************************
*
- * $Id: mbg_ntp_auth.h 1.3 2018/07/31 14:03:38 martin REL_M $
+ * $Id: mbg_ntp_auth.h 1.4 2019/09/04 07:23:02 martin REL_M $
*
* Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
*
@@ -10,6 +10,8 @@
*
* -----------------------------------------------------------------------
* $Log: mbg_ntp_auth.h $
+ * Revision 1.4 2019/09/04 07:23:02 martin
+ * mbg_ntp_auth_init() now provides a return code.
* Revision 1.3 2018/07/31 14:03:38 martin
* Fixed clang compiler warning.
* Revision 1.2 2014/09/24 08:56:27 martin
@@ -148,7 +150,7 @@ u_short cache_flags; /* flags that wave */
int mbg_ntp_read_conf_file( const char *fn ) ;
int mbg_ntp_auth_add_key( NTP_KEY_ID key_id, const char *key_type_str, const char *secret ) ;
int mbg_ntp_auth_trust_key( long key_id, int trust ) ;
- void mbg_ntp_auth_init( void ) ;
+ int mbg_ntp_auth_init( void ) ;
int mbg_ntp_auth_test( keyid_t key_id ) ;
/* ----- function prototypes end ----- */
diff --git a/mbglib/common/mbg_ntp_test_util.h b/mbglib/common/mbg_ntp_test_util.h
index 5f303d1..b7ae925 100755
--- a/mbglib/common/mbg_ntp_test_util.h
+++ b/mbglib/common/mbg_ntp_test_util.h
@@ -1,7 +1,7 @@
/**************************************************************************
*
- * $Id: mbg_ntp_test_util.h 1.6 2018/03/09 09:55:33 martin REL_M $
+ * $Id: mbg_ntp_test_util.h 1.7 2018/11/26 11:59:31 martin REL_M $
*
* Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
*
@@ -10,6 +10,8 @@
*
* -----------------------------------------------------------------------
* $Log: mbg_ntp_test_util.h $
+ * Revision 1.7 2018/11/26 11:59:31 martin
+ * Moved some inline functions to timeutil.h.
* Revision 1.6 2018/03/09 09:55:33 martin
* Updated function prototypes.
* Revision 1.5 2016/11/21 17:22:50 martin
@@ -160,101 +162,6 @@ void update_min_max_double( double curr, MIN_MAX_DOUBLE *p )
static __mbg_inline /*HDR*/
-double ntp_tstamp_to_double( const NTP_TSTAMP *t )
-{
- return (double) t->seconds + ( ( (double) t->fractions ) / NTP_FRAC_PER_SEC );
-
-} // ntp_tstamp_to_double
-
-
-
-static __mbg_inline /*HDR*/
-ulong ntp_frac_to_nsec( uint32_t frac )
-{
- unsigned long long tmp = ( (unsigned long long) frac * NSECS_PER_SEC ) / NTP_FRAC_PER_SEC;
-
- if ( tmp >= NSECS_PER_SEC )
- mbglog( LOG_WARNING, "Range overflow in ntp_frac_to_nsec: 0x%X -> %Lu",
- frac, tmp );
-
- return (ulong) tmp;
-
-} // ntp_frac_to_nsec
-
-
-
-static __mbg_inline /*HDR*/
-uint32_t nsec_to_ntp_frac( ulong nsec )
-{
- unsigned long long tmp;
-
- tmp = ( (unsigned long long) nsec * NTP_FRAC_PER_SEC ) / NSECS_PER_SEC;
-
- if ( tmp >= NTP_FRAC_PER_SEC )
- mbglog( LOG_WARNING, "Range overflow in nsec_to_ntp_frac: %lu -> 0x%LX",
- (ulong) nsec, tmp );
-
- return (uint32_t) tmp;
-
-} // nsec_to_ntp_frac
-
-
-
-static __mbg_inline /*HDR*/
-void timespec_to_ntp_tstamp( NTP_TSTAMP *t_ntp, const struct timespec *t_ts )
-{
- t_ntp->seconds = (uint32_t) t_ts->tv_sec + NTP_SEC_BIAS;
- t_ntp->fractions = nsec_to_ntp_frac( t_ts->tv_nsec );
-
-} // timespec_to_ntp_tstamp
-
-
-
-static __mbg_inline /*HDR*/
-void ntp_tstamp_to_timespec( struct timespec *t_ts, const NTP_TSTAMP *t_ntp )
-{
- t_ts->tv_sec = t_ntp->seconds - NTP_SEC_BIAS;
- t_ts->tv_nsec = ntp_frac_to_nsec( t_ntp->fractions );
-
-} // ntp_tstamp_to_timespec
-
-
-
-static __mbg_inline /*HDR*/
-int timespec_is_set( const struct timespec *p )
-{
- return p->tv_sec != 0 || p->tv_nsec != 0;
-
-} // timespec_is_set
-
-
-
-//
-// compute the difference of two timespec variables
-//
-static __mbg_inline /*HDR*/
-double delta_timespec_d_s( const struct timespec *ts,
- const struct timespec *ts_ref )
-{
- return ( (double) ts->tv_sec - (double) ts_ref->tv_sec )
- + ( (double) ts->tv_nsec - (double) ts_ref->tv_nsec ) / NSECS_PER_SEC;
-
-} // delta_timespec_d_s
-
-
-
-static __mbg_inline /*HDR*/
-long long delta_timespec_ll_ns( const struct timespec *ts,
- const struct timespec *ts_ref )
-{
- return ( ts->tv_sec - ts_ref->tv_sec ) * NSECS_PER_SEC
- + ( ts->tv_nsec - ts_ref->tv_nsec );
-
-} // delta_timespec_ll_ns
-
-
-
-static __mbg_inline /*HDR*/
int check_bounds( int opt_val, int min_val, int max_val, int default_val )
{
return ( ( opt_val < min_val ) || ( opt_val > max_val ) ) ? default_val : opt_val;
diff --git a/mbglib/common/mbg_tgt.h b/mbglib/common/mbg_tgt.h
index de8b50e..a6044a1 100755
--- a/mbglib/common/mbg_tgt.h
+++ b/mbglib/common/mbg_tgt.h
@@ -1,7 +1,7 @@
/**************************************************************************
*
- * $Id: mbg_tgt.h 1.40 2018/07/02 16:57:49 martin REL_M $
+ * $Id: mbg_tgt.h 1.46 2019/08/28 12:52:02 martin REL_M $
*
* Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
*
@@ -11,7 +11,24 @@
*
* -----------------------------------------------------------------------
* $Log: mbg_tgt.h $
- * Revision 1.40 2018/07/02 16:57:49 martin
+ * Revision 1.46 2019/08/28 12:52:02 martin
+ * Changes for mingw.
+ * Revision 1.45 2019/02/11 09:53:02Z martin
+ * Support the mingw build environment.
+ * Changed inclusion of winsock2.h/windows.h on Windows.
+ * Removed obsolete RCS log entries.
+ * Revision 1.44 2018/12/11 15:34:15 martin
+ * Older Visual Studio versions don't support _strtoi64().
+ * Revision 1.43 2018/11/22 11:28:01Z martin
+ * New preprocessor symbol MBG_TGT_USE_IOCTL.
+ * Provide 'uintptr_t' for old Linux kernels.
+ * Removed obsolete Windows driver stuff.
+ * Revision 1.42 2018/09/20 09:40:21 martin
+ * Define MBG_TGT_MISSTNG_STRTOLL and
+ * MBG_TGT_MISSING_LLDDIV_T, if appropriate.
+ * Revision 1.41 2018/08/23 10:49:56Z martin
+ * Enhanced handling of Windows DDK builds.
+ * Revision 1.40 2018/07/02 16:57:49Z martin
* Removed obsolete define MBG_USE_MM_IO_FOR_PCI.
* Revision 1.39 2018/06/25 10:52:00 martin
* Support MBG_TGT_NO_TGT.
@@ -59,54 +76,6 @@
* Fixed some spelling.
* Tmp workaround for 2.6.32-5-sparc64.
* Proper fix required.
- * Revision 1.34.1.26 2016/08/05 10:38:10 martin
- * Revision 1.34.1.25 2016/08/04 14:51:25Z martin
- * Moved some compatibility definitions from gpsserio.h to mbg_tgt.h.
- * Revision 1.34.1.24 2016/08/02 13:10:58 martin
- * Define ssize_t for Windows, if required.
- * Revision 1.34.1.23 2016/07/18 14:41:27Z martin
- * New symbol MBG_TGT_HAS_ABS64.
- * Revision 1.34.1.22 2016/07/14 09:00:58Z martin
- * Conditionally provided struct timespec for Windows.
- * Revision 1.34.1.21 2016/07/07 10:01:28Z martin
- * Modified inclusion of Windows header files.
- * Revision 1.34.1.20 2016/06/06 12:59:03 thomas-b
- * Include all necessary Windows headers in the needed sequence
- * Revision 1.34.1.19 2016/04/26 14:53:06 martin
- * Revision 1.34.1.18 2016/04/26 13:31:08Z martin
- * Added compatible 64 bit type print format specifiers.
- * Revision 1.34.1.17 2016/04/25 14:46:20Z martin
- * Include inttypes.h for all targets providing also stdint.h.
- * Revision 1.34.1.16 2016/03/02 12:26:15 martin
- * *** empty log message ***
- * Revision 1.34.1.15 2016/02/26 09:12:11 paul
- * Revision 1.34.1.14 2015/12/10 12:34:14Z martin
- * *** empty log message ***
- * Revision 1.34.1.13 2015/12/01 14:55:52 martin
- * Revision 1.34.1.12 2015/12/01 14:54:08Z martin
- * *** empty log message ***
- * Revision 1.34.1.11 2015/12/01 14:52:20 martin
- * *** empty log message ***
- * Revision 1.34.1.10 2015/12/01 14:43:44 martin
- * *** empty log message ***
- * Revision 1.34.1.9 2015/12/01 13:55:09 martin
- * Conditionally define a macro _DEPRECATED_BY which can be used to
- * tag functions as deprecated, so compilers can emit appropriate warnings.
- * Revision 1.34.1.8 2015/10/28 13:45:25 martin
- * Added some MSVC version code information.
- * Revision 1.34.1.7 2015/10/19 09:34:56 martin
- * Fixed some spelling.
- * Revision 1.34.1.6 2015/10/15 12:49:10 marvin
- * Revision 1.34.1.5 2015/10/08 08:55:16Z martin
- * Revision 1.34.1.4 2015/10/05 15:07:23Z marvin
- * Unicode support.
- * Revision 1.34.1.3 2015/09/21 08:58:27Z martin
- * *** empty log message ***
- * Revision 1.34.1.2 2015/09/18 14:53:25 martin
- * Fixes for FreeBSD.
- * Revision 1.34.1.1 2015/04/07 15:40:59 martin
- * Tmp workaround for 2.6.32-5-sparc64.
- * Proper fix required.
* Revision 1.34 2015/03/03 13:32:49 martin
* Provide __func__ for MS Visual Studio.
* Revision 1.33 2015/03/02 11:27:59Z martin
@@ -310,6 +279,7 @@ extern "C" {
// Watcom C/C++ for target OS/2
#define MBG_TGT_OS2
+ #define MBG_TGT_USE_IOCTL 1
#elif defined( __linux )
@@ -403,6 +373,7 @@ extern "C" {
#define MBG_TGT_HAS_DEV_FN 1
#define MBG_TGT_HAS_DEV_FN_BASE 0
+ #define MBG_TGT_USE_IOCTL 1
#endif
@@ -423,6 +394,23 @@ extern "C" {
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__ )
+ #if defined( __MINGW32__ )
+ #define MBG_TGT_MINGW
+ #endif
+
+ #if defined( __MINGW64__ )
+ #define MBG_TGT_MINGW
+ #endif
+
+ #if defined( MBG_TGT_MINGW ) && defined( __MINGW_EXTENSION )
+ // MSYS2 / MinGW defines __MINGW_EXTENSION
+ // and provides _abs64() and some other stuff,
+ // but the standard MinGW environment does not.
+ #if !defined( MBG_TGT_HAS_ABS64 )
+ #define MBG_TGT_HAS_ABS64 1
+ #endif
+ #endif
+
#if defined( __i386__ )
#define MBG_ARCH_I386
@@ -489,6 +477,13 @@ extern "C" {
#endif
+ // The 'uintptr_t' type has been introduced
+ // after v2.6.24-rc1 and before v2.6.24-rc2.
+ #if ( LINUX_VERSION_CODE < KERNEL_VERSION( 2, 6, 24 ) )
+ typedef unsigned long uintptr_t;
+ #define uintptr_t uintptr_t
+ #endif
+
// String formatter codes like "PRIi64" for int64_t types
// don't seem to be defined in Linux kernel mode, so we
// define the required "ll" or "l" modifier here as a hack.
@@ -517,6 +512,7 @@ extern "C" {
#define MBG_TGT_HAS_DEV_FN 1
#define MBG_TGT_HAS_DEV_FN_BASE 1
+ #define MBG_TGT_USE_IOCTL 1
#elif defined( MBG_TGT_BSD )
@@ -530,6 +526,7 @@ extern "C" {
#define MBG_TGT_HAS_DEV_FN 1
#define MBG_TGT_HAS_DEV_FN_BASE 1
+ #define MBG_TGT_USE_IOCTL 1
#if defined( MBG_TGT_FREEBSD ) && defined( MBG_TGT_KERNEL )
#if !defined( MBG_PRI_64_PREFIX )
@@ -671,12 +668,14 @@ extern "C" {
#endif
// The "deprecated" attribute should be supported since Visual Studio 2005,
- // but doesn't seem to be supported by the compiler shipped with the
+ // but doesn't seem to be supported e.g. by the compiler shipped with the
// "Windows Server 2003 SP1 DDK", which is used to build kernel drivers
// and defines the same _MSC_VER number as VS2005. For now we assume
- // that this is supported by compilers shipped with newer SDKs.
+ // that this is supported by compilers shipped with newer DDKs.
+ // The _DDK_BUILD_ symbol has to be explicitly defined in the "sources"
+ // file of the DDK project.
#if ( ( _MSC_VER >= 1500 ) || \
- ( ( _MSC_VER >= 1400 ) && !defined( _KDD_ ) ) )
+ ( ( _MSC_VER >= 1400 ) && !defined( _DDK_BUILD_ ) ) )
#define _DEPRECATED_BY( _s ) __declspec(deprecated("deprecated, use \"" _s "\""))
#endif
@@ -684,7 +683,10 @@ extern "C" {
#if ( _MSC_VER >= 1310 )
// This is supported at least since Visual Studio 2008
// and Windows Server 2003 SP1 DDK.
- #define MBG_TGT_HAS_ABS64 1
+
+ #if !defined( MBG_TGT_HAS_ABS64 )
+ #define MBG_TGT_HAS_ABS64 1
+ #endif
#endif
#if !defined ( HAVE_SSIZE_T )
@@ -705,6 +707,20 @@ extern "C" {
#endif // !defined ( HAVE_SSIZE_T )
+ #if ( _MSC_VER <= 1500 )
+ // At least MSVC++ 9.0 / Visual Studio 2008 and older
+ // don't provide lldiv_t and lldiv().
+ #define MBG_TGT_MISSING_LLDIV_T 1
+ #endif
+
+ #if ( _MSC_VER <= 1400 )
+ #define MBG_TGT_MISSING_STRTOLL 1
+ #elif ( _MSC_VER <= 1600 )
+ // At least MSVC++ 10.0 / Visual Studio 2010 and older
+ // don't provide strtoll(), but may provide _strtoi64 instead.
+ #define strtoll _strtoi64
+ #endif
+
#elif defined( _CVI_ )
// 1000 for CVI v10.0 (CVI 2010)
@@ -787,6 +803,12 @@ extern "C" {
#endif
#endif
+ #if ( __BORLANDC__ <= 0x0550 )
+ // At least CBuilder 5 and earlier.
+ #define MBG_TGT_MISSING_STRTOLL 1
+ #define MBG_TGT_MISSING_LLDIV_T 1
+ #endif
+
#define MBG_TGT_HAS_WCHAR_T defined( MBG_TGT_WIN32 )
#define MBG_TGT_MISSING_STRUCT_TIMESPEC 1
@@ -836,6 +858,11 @@ extern "C" {
#endif
+#if !defined( MBG_TGT_USE_IOCTL )
+ #define MBG_TGT_USE_IOCTL 0
+#endif
+
+
// If the build environment doesn't provide an inttypes.h file
// with print format specifiers for 64 bit fixed size types
// then MBG_PRI_64_PREFIX should be defined, which is used
@@ -878,11 +905,7 @@ extern "C" {
#include <ntddk.h>
#if defined( DBG ) && DBG
- #if defined( KDD_DEBUG_LVL )
- #define DEBUG KDD_DEBUG_LVL
- #else
- #define DEBUG 1
- #endif
+ #define DEBUG 1
#endif
#define _MBG_API
@@ -893,12 +916,9 @@ extern "C" {
#if !defined( WIN32_LEAN_AND_MEAN )
#define WIN32_LEAN_AND_MEAN 1
#endif
- #if !defined( _WINSOCKAPI_ )
- #define _WINSOCKAPI_
- #endif
- #include <windows.h>
#include <winsock2.h>
+ #include <windows.h>
#include <ws2tcpip.h>
typedef HANDLE MBG_HANDLE;
@@ -931,10 +951,12 @@ extern "C" {
#endif
- #if defined( MBG_LIB_EXPORT )
- #define _MBG_API_ATTR __declspec( dllexport )
- #else
- #define _MBG_API_ATTR __declspec( dllimport )
+ #if !defined( MBG_TGT_MINGW ) // not required for MinGW
+ #if defined( MBG_LIB_EXPORT )
+ #define _MBG_API_ATTR __declspec( dllexport )
+ #elif !defined( MBGSVCTL_STATIC_LIB )
+ #define _MBG_API_ATTR __declspec( dllimport )
+ #endif
#endif
#elif defined( MBG_TGT_POSIX )
@@ -961,7 +983,7 @@ extern "C" {
/**
* @brief A socket file descriptor type
*/
-#if defined( MBG_TGT_WIN32 )
+#if defined( MBG_TGT_WIN32 ) // && !defined( MBG_TGT_MINGW )
#if !defined( MBG_TGT_KERNEL ) // we don't need this in kernel space
// usually evaluates to UINT_PTR, which in turn evaluates
// to (unsigned int), or (unsigned __int64).
@@ -1026,7 +1048,7 @@ extern "C" {
#if defined( MBG_TGT_MISSING_STRUCT_TIMESPEC )
-#include <time.h>
+ #include <time.h>
struct timespec
{
diff --git a/mbglib/common/mbgerror.c b/mbglib/common/mbgerror.c
index 9f8dfaf..f4bbafa 100755
--- a/mbglib/common/mbgerror.c
+++ b/mbglib/common/mbgerror.c
@@ -1,7 +1,7 @@
/**************************************************************************
*
- * $Id: mbgerror.c 1.4 2018/06/25 14:21:09 martin REL_M $
+ * $Id: mbgerror.c 1.9 2019/08/26 15:15:10 martin REL_M $
*
* Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
*
@@ -10,7 +10,24 @@
*
* -----------------------------------------------------------------------
* $Log: mbgerror.c $
- * Revision 1.4 2018/06/25 14:21:09 martin
+ * Revision 1.9 2019/08/26 15:15:10 martin
+ * Translate POSIX ENODATA to MBG_ERR_NO_DATA.
+ * Revision 1.8 2019/08/20 08:22:51 martin
+ * Translate win32 ERROR_FILE_NOT_FOUND to MBG_ERR_NO_ENTITY.
+ * Revision 1.7 2019/04/03 12:53:55 martin
+ * Use new code MBG_ERR_BAD_ADDR.
+ * Revision 1.6 2018/11/22 14:47:15 martin
+ * Support QNX target.
+ * New function mbg_win32_ntstatus_to_mbg().
+ * Added and renamed static code conversion tables.
+ * Added and updated doxygen comments.
+ * Revision 1.5 2018/09/21 09:44:38 martin
+ * Renamed mbg_win32_last_err_to_mbg() to
+ * mbg_win32_sys_err_to_mbg().
+ * Translate win32 error codes ERROR_BUFFER_OVERFLOW
+ * and ERROR_INSUFFICIENT_BUFFER.
+ * Updated some comments.
+ * Revision 1.4 2018/06/25 14:21:09Z martin
* Cleaned up definition and usage of control macros.
* Distinguish between kernel mode and user mode on Windows.
* Support some new error codes.
@@ -50,6 +67,8 @@
#define _MBG_TGT_HAS_POSIX_ERRNO 1
#define _MBG_TGT_HAS_WIN32_ERR_API 1
#else
+ #include <mbgddmsg.h>
+
#define _MBG_TGT_OMIT_LAST_ERROR 1
#define _MBG_TGT_OMIT_ERR_MSG 1 // Not (yet) supported in Windows kernel mode
#endif
@@ -99,6 +118,16 @@
#define _MBG_TGT_OMIT_ERR_MSG 1
#endif
+#elif defined( MBG_TGT_QNX_NTO ) // QNX 6.x only, but not QNX 4.x
+
+ #include <stdio.h>
+ #include <errno.h>
+ #include <string.h>
+ #include <netdb.h>
+
+ #define _MBG_TGT_HAS_POSIX_ERRNO 1
+ #define _MBG_TGT_HAS_POSIX_H_ERRNO 1
+
#elif defined( MBG_TGT_DOS )
#include <stdio.h>
@@ -106,8 +135,8 @@
#include <stddef.h>
#include <errno.h>
- #define _MBG_TGT_HAS_POSIX_ERRNO 1
- #define _MBG_TGT_OMIT_SOCKET_ERRORS 1 // No network socket support
+ #define _MBG_TGT_HAS_POSIX_ERRNO 1
+ #define _MBG_TGT_OMIT_SOCKET_ERRORS 1 // No network socket support by OS
#endif
@@ -160,7 +189,7 @@ static ERRNO_CNV_ENTRY posix_errno_table[] =
{ EAGAIN, MBG_ERR_AGAIN }, // 11, Try again, resource temporarily unavailable (later calls may complete normally, maybe same as EWOULDBLOCK).
{ ENOMEM, MBG_ERR_NO_MEM }, // 12, Out of memory (can't allocate memory).
{ EACCES, MBG_ERR_ACCESS }, // 13, Permission denied (e.g. when trying to access a device without sufficient permissions).
- { EFAULT, MBG_ERR_INV_PARM }, // 14, Bad address (e.g. invalid address in a function argument).
+ { EFAULT, MBG_ERR_BAD_ADDRESS }, // 14, Bad address (e.g. invalid address in a function argument).
// { ENOTBLK, }, // 15, Block device required (eventually no POSIX error, but supported in Linux and *BSD).
{ EBUSY, MBG_ERR_BUSY }, // 16, Device or resource busy.
{ EEXIST, MBG_ERR_EXIST }, // 17, File exists.
@@ -181,6 +210,9 @@ static ERRNO_CNV_ENTRY posix_errno_table[] =
// { EPIPE, }, // 32, Broken pipe (writing to a socket, pipe, or FIFO which has no process to read the data).
// { EDOM, }, // 33, Math argument out of domain of function.
{ ERANGE, MBG_ERR_RANGE }, // 34, Math result too large or too small (not representable).
+#if defined( ENODATA )
+ { ENODATA, MBG_ERR_NO_DATA }, // 61, No (more) data.
+#endif
#if defined( ETIME )
{ ETIME, MBG_ERR_TIMER }, // 62, Timer expired (e.g. stream timeout on USB disconnect).
#endif
@@ -287,72 +319,214 @@ static ERRNO_CNV_ENTRY cvi_rs232_error_table[] =
#if defined( MBG_TGT_KERNEL )
-// FIXME TODO
-// Each of the predefined NTSTATUS codes STATUS_... used in Windows kernel drivers
-// is remapped to one of the positive ERROR_... codes defined in WinError.h when
-// passed up from kernel space to to user space.
-// For the mapping, see:
-// https://support.microsoft.com/en-us/help/113996/info-mapping-nt-status-error-codes-to-win32-error-codes
-//
-// Eventually, driver specific error codes can also be defined in kernel space,
-// e.g. by defining (NTSTATUS) (0x2000000 | 31). Look likes if these error codes
-// are passed up to user space, GetLastError() returns the negated value, e.g. -31.
-// This has to be tested, though, and could be used to pass direct Meinberg error
-// codes up to user space, without the need for win32_kernel_error_table.
-
-static ERRNO_CNV_ENTRY win32_kernel_error_table[] =
+/**
+ * @brief Mappings of some NTSTATUS codes to Meinberg error codes
+ *
+ * Kernel space Windows APIs use NTSTATUS codes as return values,
+ * which consist of several bit fields which also provide some severity
+ * and facility codes, similar to the message IDs used with text resources.
+ * The status codes and associated message texts are defined
+ * in the ntstatus.h file shipped with the various Windows DDKs.
+ *
+ * Please note that WIN32 user space API function return a different
+ * set of error codes on failure. See ::win32_sys_err_table.
+ *
+ * @see ::mbg_ioctl_to_ntstatus_table
+ * @see ::win32_kernel_status_table
+ * @see ::win32_sys_err_table
+ * @see ::win32_wsa_err_table
+ * @see @ref MBG_ERROR_CODES
+ */
+static ERRNO_CNV_ENTRY win32_kernel_status_table[] =
+{
+ { STATUS_INVALID_PARAMETER, MBG_ERR_INV_PARM, }, // 0xC000000DL, An invalid parameter was passed to a service or function.
+ { STATUS_INVALID_DEVICE_REQUEST, MBG_ERR_INV_DEV_REQUEST }, // 0xC0000010L, The specified request is not a valid operation for the target device.
+ { STATUS_DEVICE_BUSY, MBG_ERR_IRQ_UNSAFE }, // 0x80000011L, The device is currently busy.
+ { STATUS_NO_MEMORY, MBG_ERR_NO_MEM }, // 0xC0000017L, Not enough memory available to complete the specified operation.
+ { STATUS_NO_DATA_DETECTED, MBG_ERR_NOT_SUPP_BY_DEV }, // 0x80000022L, (normally used with tape access, but mis-used here)
+ { STATUS_ACCESS_DENIED, MBG_ERR_PERM }, // 0xC0000022L, A process has requested access to an object, but has not been granted those access rights.
+ { STATUS_IO_DEVICE_ERROR, MBG_ERR_IO }, // 0xC0000185L, The I/O device reported an I/O error.
+
+ { STATUS_BUFFER_TOO_SMALL, MBG_ERR_BUFFER_TOO_SMALL }, // 0xC0000023L, The buffer is too small to contain the entry. No information has been written to the buffer.
+ // { STATUS_CANCELLED, }, // 0xC0000120L, The I/O request was canceled.
+ // { STATUS_DELETE_PENDING, }, // 0xC0000056L, A non close operation has been requested of a file object with a delete pending.
+ // { STATUS_DEVICE_CONFIGURATION_ERROR, }, // 0xC0000182L, The I/O device is configured incorrectly or the configuration parameters to the driver are incorrect.
+ { STATUS_DEVICE_NOT_READY, MBG_ERR_NOT_READY }, // 0xC00000A3L, The device (drive) is not ready for use.
+ // { STATUS_INSUFFICIENT_RESOURCES, }, // 0xC000009AL, Insufficient system resources exist to complete the API.
+ // { STATUS_MORE_PROCESSING_REQUIRED, }, // 0xC0000016L, The specified IRP cannot be disposed of because the I/O operation is not complete.
+ // { STATUS_NOT_IMPLEMENTED, }, // 0xC0000002L, The requested operation is not implemented.
+ // { STATUS_NOT_SUPPORTED, }, // 0xC00000BBL, The network request is not supported.
+ // { STATUS_NO_SUCH_DEVICE, }, // 0xC000000EL, A device which does not exist was specified.
+ // { STATUS_OBJECT_NAME_NOT_FOUND, }, // 0xC0000034L, Object Name not found.
+ // { STATUS_PENDING, }, // 0x00000103L, The operation that was requested is pending completion.
+ { STATUS_TIMEOUT, MBG_ERR_TIMEOUT }, // 0x00000102L, STATUS_TIMEOUT
+ // { STATUS_UNSUCCESSFUL, }, // 0xC0000001L, The requested operation was unsuccessful.
+ { 0, 0 } // end-of-table identifier
+
+}; // win32_kernel_status_table
+
+
+
+#if !_USE_WIN32_PRIVATE_STATUS_CODES
+
+/**
+ * @brief Windows IOCTL error conversion table.
+ *
+ * The IOCTL handler of a Meinberg kernel driver actually calls
+ * some Meinberg mbglib functions to access a device, and those
+ * functions normally return one of the @ref MBG_RETURN_CODES.
+ *
+ * However, when the Windows kernel calls the driver's IOCTL handler
+ * it expects an NTSTATUS code to be returned, so this table can be
+ * used in an IOCTL handler to map a Meinberg return code to a
+ * predefined NTSTATUS code that can be returned.
+ *
+ * To make things worse, Windows then remaps the NTSTATUS code to one
+ * of the WIN32 error codes when it passes the IOCTL results up to the
+ * user space function that submitted the IOCTL request.
+ *
+ * The user space function can then convert the returned code back
+ * to one of the @ref MBG_RETURN_CODES.
+ *
+ * For the mapping of NTSTATUS codes to WIN32 error codes
+ * done by Windows when it passes an IOCTL return code from kernel
+ * space to user space, see:
+ * https://support.microsoft.com/en-us/help/113996/info-mapping-nt-status-error-codes-to-win32-error-codes
+ *
+ * So care should be taken that the table entries here are defined
+ * such that in spite of the mnumerous conversions the same error code
+ * that was returned by a driver function is finally returned by the
+ * Meinberg user space API call. For example:
+ *
+ * - Driver function returns ::MBG_ERR_INV_PARM error.
+ *
+ * - Table ::mbg_ioctl_to_ntstatus_table is used to convert this to
+ * STATUS_INVALID_PARAMETER, which is returned by the IOCTL handler
+ * in kernel space.
+ *
+ * - Windows converts this to ERROR_INVALID_PARAMETER when it passes
+ * the error code up to user space.
+ *
+ * - The user space function uses ::win32_sys_err_table to convert this
+ * to a Meinberg error code, which again yields ::MBG_ERR_INV_PARM.
+ *
+ * @note Eventually it would be possible to defined custom NTSTATUS codes
+ * in kernel space, e.g. ((NTSTATUS) (0xC0000000 | 0x20000000 | 31)).
+ * Apparently, such custom error status codes are passed up to user space,
+ * and GetLastError() after the ioctl() call then returns a negative number,
+ * i.e. -31 for the example here.
+ * This had to be tested, though, and could be used to pass Meinberg error
+ * codes directly up to user space, without the need for a multiple conversion.
+ * However, there's no guarantee that this always works, and would still
+ * work in future Windows versions.
+ *
+ * @see ::win32_kernel_status_table
+ * @see ::win32_sys_err_table
+ * @see ::win32_wsa_err_table
+ * @see @ref MBG_ERROR_CODES
+ */
+static ERRNO_CNV_ENTRY mbg_ioctl_to_ntstatus_table[] =
{
- { MBG_ERR_PERM, STATUS_ACCESS_DENIED }, // ERROR_ACCESS_DENIED
- { MBG_ERR_INV_DEV_REQUEST, STATUS_INVALID_DEVICE_REQUEST }, // ERROR_BAD_COMMAND
{ MBG_ERR_INV_PARM, STATUS_INVALID_PARAMETER }, // ERROR_INVALID_PARAMETER
- { MBG_ERR_NOT_SUPP_BY_DEV, STATUS_NO_DATA_DETECTED }, // ERROR_NO_DATA_DETECTED (mis-used here)
- { MBG_ERR_NO_MEM, STATUS_NO_MEMORY }, // ERROR_NOT_ENOUGH_MEMORY
+ { MBG_ERR_INV_DEV_REQUEST, STATUS_INVALID_DEVICE_REQUEST }, // ERROR_BAD_COMMAND
{ MBG_ERR_IRQ_UNSAFE, STATUS_DEVICE_BUSY }, // ERROR_BUSY
+ { MBG_ERR_NO_MEM, STATUS_NO_MEMORY }, // ERROR_NOT_ENOUGH_MEMORY
+ { MBG_ERR_NOT_SUPP_BY_DEV, STATUS_NO_DATA_DETECTED }, // ERROR_NO_DATA_DETECTED (mis-used here)
+ { MBG_ERR_PERM, STATUS_ACCESS_DENIED }, // ERROR_ACCESS_DENIED
{ MBG_ERR_IO, STATUS_IO_DEVICE_ERROR }, // ERROR_IO_DEVICE
{ 0, 0 } // end-of-table identifier
-}; // win32_kernel_error_table
+}; // mbg_ioctl_to_ntstatus_table
+
+#endif // !_USE_WIN32_PRIVATE_STATUS_CODES
+
#else // Windows user space
-static ERRNO_CNV_ENTRY win32_error_table[] =
+/**
+ * @brief Mappings of WIN32 error codes to Meinberg error codes
+ *
+ * If a WIN32 user space API function encounters an error,
+ * the Windows GetLastError() function has to be called in
+ * most cases to retrieve an associated error code, which is
+ * a positive integer number.
+ *
+ * Existing error codes and associated message texts
+ * are defined in the WinError.h file shipped with the
+ * various SDKs and IDEs.
+ *
+ * Please note that Windows socket functions return a different
+ * set of error codes on failure, and WSAGetLastError() has to
+ * be called to retrieve the associated error code.
+ * See ::win32_wsa_err_table.
+ *
+ * @see ::win32_wsa_err_table
+ * @see ::mbg_ioctl_to_ntstatus_table
+ * @see ::win32_kernel_status_table
+ * @see @ref MBG_ERROR_CODES
+ */
+static ERRNO_CNV_ENTRY win32_sys_err_table[] =
{
// Mappings for some Windows error codes defined in WinError.h
- { ERROR_PATH_NOT_FOUND, MBG_ERR_NO_ENTITY }, // 3L: The system cannot find the path specified.
- { ERROR_ACCESS_DENIED, MBG_ERR_ACCESS }, // 5L: Access is denied.
- { ERROR_INVALID_HANDLE, MBG_ERR_INV_HANDLE }, // 6L: The handle is invalid.
- { ERROR_NOT_ENOUGH_MEMORY, MBG_ERR_NO_MEM }, // 8L: Not enough storage is available to process this command.
- { ERROR_OUTOFMEMORY, MBG_ERR_NO_MEM }, // 14L: Not enough storage is available to complete this operation.
- // { ERROR_WRITE_PROTECT, }, // 19L: The media is write protected.
- // { ERROR_BAD_UNIT, }, // 20L: The system cannot find the device specified.
- { ERROR_NOT_READY, MBG_ERR_NOT_READY }, // 21L: The device is not ready.
- { ERROR_WRITE_FAULT, MBG_ERR_IO }, // 29L: The system cannot write to the specified device.
- { ERROR_READ_FAULT, MBG_ERR_IO }, // 30L: The system cannot read from the specified device.
- { ERROR_GEN_FAILURE, MBG_ERR_UNSPEC }, // 31L: A device attached to the system is not functioning.
- // { ERROR_SHARING_VIOLATION, }, // 32L: The process cannot access the file because it is being used by another process.
- // { ERROR_LOCK_VIOLATION, }, // 33L: The process cannot access the file because another process has locked a portion of the file.
- // { ERROR_NOT_SUPPORTED, }, // 50L: The request is not supported.
- // { ERROR_DUP_NAME, }, // 52L: A duplicate name exists on the network.
- // { ERROR_BAD_DEV_TYPE, }, // 66L: The network resource type is not correct.
- { ERROR_INVALID_PARAMETER, MBG_ERR_INV_PARM }, // 87L: The parameter is incorrect.
- // { ERROR_BUFFER_OVERFLOW, }, // 111L: The file name is too long.
- { ERROR_BUSY, MBG_ERR_BUSY }, // 170L: The requested resource is in use.
- // { ERROR_NOACCESS, }, // 998L: Invalid access to memory location.
- { ERROR_NO_DATA_DETECTED, MBG_ERR_NOT_SUPP_BY_DEV }, // 1104L: No more data is on the tape. (mis-used here)
- { ERROR_IO_DEVICE, MBG_ERR_IO }, // 1117L: The request could not be performed because of an I/O device error.
- { ERROR_PRIVILEGE_NOT_HELD, MBG_ERR_PERM }, // 1314L: A required privilege is not held by the client.
- { 0, 0 } // end-of-table identifier
-
-}; // win32_error_table
+ { ERROR_FILE_NOT_FOUND, MBG_ERR_NO_ENTITY }, // 2L: The system cannot find the file specified.
+ { ERROR_PATH_NOT_FOUND, MBG_ERR_NO_ENTITY }, // 3L: The system cannot find the path specified.
+ { ERROR_ACCESS_DENIED, MBG_ERR_ACCESS }, // 5L: Access is denied.
+ { ERROR_INVALID_HANDLE, MBG_ERR_INV_HANDLE }, // 6L: The handle is invalid.
+ { ERROR_NOT_ENOUGH_MEMORY, MBG_ERR_NO_MEM }, // 8L: Not enough storage is available to process this command.
+ { ERROR_OUTOFMEMORY, MBG_ERR_NO_MEM }, // 14L: Not enough storage is available to complete this operation.
+ // { ERROR_WRITE_PROTECT, }, // 19L: The media is write protected.
+ // { ERROR_BAD_UNIT, }, // 20L: The system cannot find the device specified.
+ { ERROR_NOT_READY, MBG_ERR_NOT_READY }, // 21L: The device is not ready.
+ { ERROR_WRITE_FAULT, MBG_ERR_IO }, // 29L: The system cannot write to the specified device.
+ { ERROR_READ_FAULT, MBG_ERR_IO }, // 30L: The system cannot read from the specified device.
+ { ERROR_GEN_FAILURE, MBG_ERR_UNSPEC }, // 31L: A device attached to the system is not functioning.
+ // { ERROR_SHARING_VIOLATION, }, // 32L: The process cannot access the file because it is being used by another process.
+ // { ERROR_LOCK_VIOLATION, }, // 33L: The process cannot access the file because another process has locked a portion of the file.
+ // { ERROR_NOT_SUPPORTED, }, // 50L: The request is not supported.
+ // { ERROR_DUP_NAME, }, // 52L: A duplicate name exists on the network.
+ // { ERROR_BAD_DEV_TYPE, }, // 66L: The network resource type is not correct.
+ { ERROR_INVALID_PARAMETER, MBG_ERR_INV_PARM }, // 87L: The parameter is incorrect.
+ { ERROR_BUFFER_OVERFLOW, MBG_ERR_OVERFLOW }, // 111L: The file name is too long.
+ { ERROR_INSUFFICIENT_BUFFER, MBG_ERR_BUFFER_TOO_SMALL }, // 122L: The data area passed to a system call is too small.
+ { ERROR_BUSY, MBG_ERR_BUSY }, // 170L: The requested resource is in use.
+ // { ERROR_NOACCESS, }, // 998L: Invalid access to memory location.
+ { ERROR_NO_DATA_DETECTED, MBG_ERR_NOT_SUPP_BY_DEV }, // 1104L: No more data is on the tape. (mis-used here)
+ { ERROR_IO_DEVICE, MBG_ERR_IO }, // 1117L: The request could not be performed because of an I/O device error.
+ { ERROR_PRIVILEGE_NOT_HELD, MBG_ERR_PERM }, // 1314L: A required privilege is not held by the client.
+ { 0, 0 } // end-of-table identifier
+
+}; // win32_sys_err_table
+/**
+ * @brief Mappings of Winsock error codes to Meinberg error codes
+ *
+ * If a Windows socket (Winsock) function encounters an error,
+ * the Windows WSAGetLastError() function has to be called in
+ * most cases to retrieve an associated error code, which is
+ * a positive integer number.
+ *
+ * Existing error codes and associated message texts are
+ * defined in the Winsock2.h file shipped with the various
+ * SDKs and IDEs.
+ *
+ * Please note that the standard WIN32 API functions return a
+ * different set of error codes on failure, and GetLastError()
+ * has to be called to retrieve the associated error code.
+ * See ::win32_err_table.
+ *
+ * @see ::win32_sys_err_table
+ * @see ::mbg_ioctl_to_ntstatus_table
+ * @see ::win32_kernel_status_table
+ * @see @ref MBG_ERROR_CODES
+ */
static ERRNO_CNV_ENTRY win32_wsa_err_table[] =
{
{ WSAEINTR, MBG_ERR_INTR }, // 10004L A blocking operation was interrupted by a call to WSACancelBlockingCall.
// { WSAEBADF // 10009L The file handle supplied is not valid.
// { WSAEACCES // 10013L An attempt was made to access a socket in a way forbidden by its access permissions.
- { WSAEFAULT, MBG_ERR_INV_PARM }, // 10014L The system detected an invalid pointer address in attempting to use a pointer argument in a call.
+ { WSAEFAULT, MBG_ERR_BAD_ADDRESS }, // 10014L The system detected an invalid pointer address in attempting to use a pointer argument in a call.
{ WSAEINVAL, MBG_ERR_INV_PARM }, // 10022L An invalid argument was supplied.
// { WSAEMFILE // 10024L Too many open sockets.
{ WSAEWOULDBLOCK, MBG_ERR_AGAIN }, // 10035L A non-blocking socket operation could not be completed immediately.
@@ -466,7 +640,7 @@ int mbg_errno_to_os( int err_no )
if ( err_no == MBG_SUCCESS )
return STATUS_SUCCESS;
- return lookup_tbl_errno( err_no, win32_kernel_error_table, STATUS_UNSUCCESSFUL );
+ return lookup_tbl_errno( err_no, mbg_ioctl_to_ntstatus_table, STATUS_UNSUCCESSFUL );
#elif defined( MBG_TGT_LINUX )
@@ -627,39 +801,60 @@ int mbg_cvi_rs232_error_to_mbg( int cvi_rc, const char *info )
#if defined( MBG_TGT_KERNEL )
-// FIXME TODO
+/*HDR*/
+/**
+ * @brief Translate a Windows NTSTATUS code to one of the @ref MBG_ERROR_CODES
+ *
+ * @param[in] st One of the NTSTATUS codes defined in ntstatus.h
+ * @param[in] info An optional informational text string, or NULL
+ *
+ * @return One of the @ref MBG_ERROR_CODES
+ */
+int mbg_win32_ntstatus_to_mbg( NTSTATUS st, const char *info )
+{
+ #if DEBUG
+ if ( info )
+ _mbgddmsg_2( DEBUG, MBG_LOG_INFO, "%s, ntstatus: 0x%08lX\n", info, (long) st );
+ #else
+ (void) info; // avoid compiler warning
+ #endif
+
+ return lookup_tbl_errno( st, win32_kernel_status_table, MBG_ERR_UNKNOWN );
+
+} // mbg_win32_ntstatus_to_mbg
+
#else // Windows user space
/*HDR*/
/**
- * @brief Translate a Windows non-socket API error code to one of the @ref MBG_ERROR_CODES
+ * @brief Translate a Windows non-socket API return code to one of the @ref MBG_RETURN_CODES
*
- * @param[in] last_err A Windows non-socket API error code as returned by GetLastError()
- * @param[in] info An optional informational text string, or NULL
+ * @param[in] win32_sys_rc A Windows non-socket API error code as returned by GetLastError(), or ERROR_SUCCESS.
+ * @param[in] info An optional informational text string, or NULL.
*
- * @return One of the @ref MBG_ERROR_CODES
+ * @return One of the @ref MBG_RETURN_CODES
*/
-int mbg_win32_last_err_to_mbg( DWORD last_err, const char *info )
+int mbg_win32_sys_err_to_mbg( DWORD win32_sys_rc, const char *info )
{
int rc = MBG_SUCCESS;
- if ( last_err == ERROR_SUCCESS )
+ if ( win32_sys_rc == ERROR_SUCCESS )
goto out;
- if ( last_err & 0x20000000L )
+ if ( win32_sys_rc & STATUS_CUSTOM_FLAG_MASK )
{
- // This is a user-defined error code, e.g. returned by an IOCTL call.
- // The lower 16 bits contain the (positive) error code while the upper
+ // This is a user-defined error code or message ID, e.g. returned by an IOCTL
+ // call. The lower 16 bits contain the (positive) error code while the upper
// 16 bits contain flags as specified for the Windows API in winerror.h.
// So we assume the error code is just the absolute value of one of the
// @ref MBG_ERROR_CODES, and we return the negated value.
- rc = - (int) ( last_err & 0xFFFF );
+ rc = - (int) ( win32_sys_rc & 0xFFFF );
goto out;
}
#if 0 // TODO FIXME
- rc = (int) last_err;
+ rc = (int) win32_sys_rc;
if ( rc < 0 )
{
@@ -670,13 +865,13 @@ int mbg_win32_last_err_to_mbg( DWORD last_err, const char *info )
}
#endif
- rc = lookup_tbl_errno( last_err, win32_error_table, MBG_ERR_UNKNOWN );
+ rc = lookup_tbl_errno( win32_sys_rc, win32_sys_err_table, MBG_ERR_UNKNOWN );
out:
#if DEBUG
if ( info )
- fprintf( stderr, "%s, last_err: 0x%08lX (%i) --> %i (%s)\n",
- info, (long) last_err, (int) last_err,
+ fprintf( stderr, "%s, win32_sys_rc: 0x%08lX (%i) --> %i (%s)\n",
+ info, (long) win32_sys_rc, (int) win32_sys_rc,
rc, mbg_strerror( rc ) );
#else
(void) info; // avoid compiler warning
@@ -684,7 +879,7 @@ out:
return rc;
-} // mbg_win32_last_err_to_mbg
+} // mbg_win32_sys_err_to_mbg
@@ -697,11 +892,11 @@ out:
*
* @return One of the @ref MBG_ERROR_CODES
*/
-int mbg_win32_wsa_err_to_mbg( DWORD wsa_err, const char *info )
+int mbg_win32_wsa_err_to_mbg( int wsa_err, const char *info )
{
#if DEBUG
if ( info )
- fprintf( stderr, "%s, wsa_err: 0x%08lX\n", info, (long) wsa_err );
+ fprintf( stderr, "%s, wsa_err: %i\n", info, wsa_err );
#else
(void) info; // avoid compiler warning
#endif
@@ -808,10 +1003,11 @@ int mbg_get_last_error( const char *info )
#if defined( MBG_TGT_WIN32 )
// Under Windows the "last error" code after a non-socket function
- // has to be retrieved by calling GetLastError(), whereas
- // the "last error" code from POSIX-like socket functions
- // is retrieved by calling WSAGetLastError().
- return mbg_win32_last_err_to_mbg( GetLastError(), info );
+ // ("Windows System Errors") has to be retrieved by calling GetLastError(),
+ // whereas the "last error" code from POSIX-like socket functions
+ // ("Windows Sockets Error Codes") has to be retrieved by calling
+ // WSAGetLastError().
+ return mbg_win32_sys_err_to_mbg( GetLastError(), info );
#elif defined( MBG_TGT_POSIX )
@@ -857,10 +1053,11 @@ int mbg_get_last_socket_error( const char *info )
#elif defined( MBG_TGT_WIN32 )
#if !defined( MBG_TGT_KERNEL )
- // Under Windows the "last error" code after a socket function
- // has to be retrieved by calling WSAGetLastError, whereas
- // the "last error" code from non-socket POSIX-like functions
- // is stored in errno as usual.
+ // Under Windows the "last error" code after a POSIX-like socket
+ // function ("Windows Sockets Error Code") has to be retrieved
+ // by calling WSAGetLastError(), whereas the "last error" code
+ // after a non-socket function ("Windows System Errors") has
+ // to be retrieved by calling GetLastError().
return mbg_win32_wsa_err_to_mbg( WSAGetLastError(), info );
#else
return MBG_ERR_GENERIC; // TODO should we only work with NTSTATUS codes in kernel mode?
diff --git a/mbglib/common/mbgerror.h b/mbglib/common/mbgerror.h
index 5694199..05a8999 100755
--- a/mbglib/common/mbgerror.h
+++ b/mbglib/common/mbgerror.h
@@ -1,17 +1,43 @@
/**************************************************************************
*
- * $Id: mbgerror.h 1.16 2018/06/25 14:26:38 martin REL_M $
+ * $Id: mbgerror.h 1.24 2019/08/28 08:02:56 philipp REL_M $
*
* Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
*
* Description:
- * Error codes used with Meinberg devices and drivers.
- * The codes can be translated into an OS dependent error code.
+ * Common error codes used with Meinberg API calls.
+ * OS-specific error codes can be translated into these codes.
*
* -----------------------------------------------------------------------
* $Log: mbgerror.h $
- * Revision 1.16 2018/06/25 14:26:38 martin
+ * Revision 1.24 2019/08/28 08:02:56 philipp
+ * Added error code MBG_ERR_IN_PROGRESS
+ * Revision 1.23 2019/08/26 15:14:34 martin
+ * Modified msg. text for MBG_ERR_NO_DATA.
+ * Revision 1.22 2019/08/20 08:20:04 martin
+ * Fixed build using mingw.
+ * Changed message text for MBG_ERR_ACCESS, which may also indicate that
+ * an exclusive resource is in use, not only that permissions are missing.
+ * Revision 1.21 2019/03/13 16:17:10 martin
+ * New code MBG_ERR_BAD_ADDRESS (for EFAULT).
+ * Revision 1.20 2019/03/13 09:44:30 martin
+ * Moved predefined program exit codes here.
+ * Revision 1.19 2019/02/08 10:42:56 martin
+ * Distinguish more detailed if an NTSTATUS replacement
+ * is required for a particular build environment.
+ * Revision 1.18 2018/11/22 11:25:56 martin
+ * New error code MBG_ERR_STR_SUBSTR merged from 1.15.1.x branch.
+ * Refactored surrogate definitions.
+ * Updated function prototypes.
+ * Revision 1.17 2018/09/21 09:49:57 martin
+ * Renamed MBG_ERR_FIFO to MBG_ERR_NO_DATA.
+ * Added strings for MBG_ERR_NO_DRIVER and MBG_ERR_DRV_VERSION.
+ * New code MBG_ERR_BUFFER_TOO_SMALL.
+ * MBG_ERR_OUTDATED error code added by philipp.
+ * Added some Windows stuff.
+ * Updated function prototypes.
+ * Revision 1.16 2018/06/25 14:26:38Z martin
* Some new error codes were introduced.
* New macro mbg_rc_is_success_or_err_perm().
* New control macro _USE_WIN32_PRIVATE_STATUS_CODES.
@@ -78,6 +104,57 @@
#include <mbg_tgt.h>
#include <words.h>
+// We may need surrogate declarations for target-specific
+// types that are used in common function prototypes.
+// Without such declarations we'd get compiler errors
+// on targets that don't have them.
+
+#if defined( MBG_TGT_WIN32 )
+
+ #if defined( MBG_TGT_KERNEL )
+
+ #define MBG_TGT_MISSING_DWORD 1 // missing even in kernel space.
+ #define MBG_TGT_MISSING_NTSTATUS 0 // available in kernel space
+
+ #else // Windows user space
+
+ #define MBG_TGT_MISSING_DWORD 0 // available in user space
+
+ // Some (but not all) Windows build environments provide a
+ // bcrypt.h file which has a definition for NTSTATUS, so we
+ // use.that if available to avoid duplicate / mismatching
+ // definitions in case an application uses bcrypt.h anyway.
+ #if !defined( MBG_TGT_HAS_BCRYPT_H ) // unless already defined elsewhere
+ #if defined( _MSC_VER ) && ( _MSC_VER >= 1500 ) // at least VS2008 has it
+ #define MBG_TGT_HAS_BCRYPT_H 1
+ #endif
+
+ #if defined( MBG_TGT_MINGW ) // older versions may not have it, though
+ #define MBG_TGT_HAS_BCRYPT_H 1
+ #endif
+
+ #if !defined( MBG_TGT_HAS_BCRYPT_H )
+ #define MBG_TGT_HAS_BCRYPT_H 0
+ #endif
+ #endif
+
+ #if MBG_TGT_HAS_BCRYPT_H
+ #include <bcrypt.h>
+ #define MBG_TGT_MISSING_NTSTATUS 0
+ #else
+ #define MBG_TGT_MISSING_NTSTATUS 1
+ #endif
+
+ #endif
+
+#else // non-Windows targets
+
+ #define MBG_TGT_MISSING_DWORD 1
+ #define MBG_TGT_MISSING_NTSTATUS 1
+
+#endif
+
+
#ifdef _MBGERROR
#define _ext
#define _DO_INIT
@@ -94,8 +171,6 @@ extern "C" {
#if defined( MBG_TGT_WIN32 )
- #define _USE_WIN32_PRIVATE_STATUS_CODES 0
-
#if defined( MBG_TGT_KERNEL )
#define MBG_SYS_RC_SUCCESS STATUS_SUCCESS
#else
@@ -117,10 +192,57 @@ extern "C" {
#define _USE_WIN32_PRIVATE_STATUS_CODES 0
#endif
-#if !defined( MBG_TGT_WIN32 ) || defined( MBG_TGT_KERNEL )
- // A dummy declaration for DWORD to avoid compiler errors.
- // Also reqired in Windows kernel mode.
- #define DWORD uint32_t
+
+#if defined( MBG_TGT_WIN32 )
+
+#define STATUS_SEVERITY_SUCCESS 0x0
+#define STATUS_SEVERITY_INFORMATIONAL 0x1
+#define STATUS_SEVERITY_WARNING 0x2
+#define STATUS_SEVERITY_ERROR 0x3
+
+#define STATUS_SEVERITY_SHIFT_BITS 30
+#define STATUS_SEVERITY_SHIFT_MASK 0x03
+
+#define STATUS_SEVERITY_SUCCESS_MASK ( STATUS_SEVERITY_SUCCESS << STATUS_SEVERITY_SHIFT_BITS )
+#define STATUS_SEVERITY_INFORMATIONAL_MASK ( STATUS_SEVERITY_INFORMATIONAL << STATUS_SEVERITY_SHIFT_BITS )
+#define STATUS_SEVERITY_WARNING_MASK ( STATUS_SEVERITY_WARNING << STATUS_SEVERITY_SHIFT_BITS )
+#define STATUS_SEVERITY_ERROR_MASK ( STATUS_SEVERITY_ERROR << STATUS_SEVERITY_SHIFT_BITS )
+
+#define _get_win_msg_severity( _st ) ( ( (_st) >> STATUS_SEVERITY_SHIFT_BITS ) & STATUS_SEVERITY_SHIFT_MASK )
+
+
+
+#define STATUS_CUSTOM_FLAG 0x1
+
+#define STATUS_CUSTOM_FLAG_SHIFT_BITS 29
+#define STATUS_CUSTOM_FLAG_SHIFT_MASK 0x01
+
+#define STATUS_CUSTOM_FLAG_MASK ( STATUS_CUSTOM_FLAG << STATUS_CUSTOM_FLAG_SHIFT_BITS )
+
+#define _get_win_msg_custom_flag( _st ) ( ( (_st) >> STATUS_CUSTOM_FLAG_SHIFT_BITS ) & STATUS_CUSTOM_FLAG_SHIFT_MASK )
+
+#define _win_msg_is_custom( _st ) ( _get_win_msg_custom_flag( _st ) != 0 )
+
+
+#define _mbg_msg_id_inf( num ) ((DWORD) ( STATUS_SEVERITY_INFORMATIONAL_MASK | STATUS_CUSTOM_FLAG_MASK | num ) )
+#define _mbg_msg_id_wrn( num ) ((DWORD) ( STATUS_SEVERITY_WARNING_MASK | STATUS_CUSTOM_FLAG_MASK | num ) )
+#define _mbg_msg_id_err( num ) ((DWORD) ( STATUS_SEVERITY_ERROR_MASK | STATUS_CUSTOM_FLAG_MASK | num ) )
+
+#endif // defined( MBG_TGT_WIN32 )
+
+
+
+#if MBG_TGT_MISSING_DWORD
+ typedef uint32_t DWORD;
+ #define DWORD DWORD
+#endif
+
+#if MBG_TGT_MISSING_NTSTATUS
+ // We intentionally define an uncommon type to
+ // enforce build errors in case NTSTATUS is really
+ // used on targets that should not use it.
+ typedef int *NTSTATUS;
+ #define NTSTATUS NTSTATUS
#endif
@@ -137,7 +259,7 @@ extern "C" {
/* ### TODO FIXME
* Under Windows, some message strings are provided as resources appended
* to the mbgctrl DLL, but the codes specified here have to be translated
- * to Windows-specific error codes before the appropriate resource string
+ * to Windows-specific message IDs before the appropriate resource string
* can be retrieved. Actually this is done by taking the absolute number
* of an error code and have it or'ed with 0xE0000000 afterwards, e.g.
* ::MBG_ERR_GENERIC (-19) will yield Windows code 0xE0000013.
@@ -164,69 +286,69 @@ extern "C" {
#define MBG_ERR_NBYTES -22 ///< The number of parameter bytes passed to the device did not
///< match the number of bytes expected by the device.
-#define MBG_ERR_INV_TIME -23 ///< The device doesn't have valid time.
-#define MBG_ERR_FIFO -24 ///< The data FIFO of a bus-level device is empty, though it shouldn't be.
+#define MBG_ERR_INV_TIME -23 ///< The device has no valid time.
+#define MBG_ERR_NO_DATA -24 ///< No (more) data to process, e.g. FIFO empty.
#define MBG_ERR_NOT_READY -25 ///< Bus-level device is temp. unable to respond e.g. during init. after RESET.
-#define MBG_ERR_INV_TYPE -26 ///< bus-level device didn't recognize data type
+#define MBG_ERR_INV_TYPE -26 ///< Bus-level device didn't recognize data type.
// Codes returned by the high level API functions
-#define MBG_ERR_NO_MEM -27 ///< failed to allocate memory
-#define MBG_ERR_CLAIM_RSRC -28 ///< failed to claim port or mem resource
-#define MBG_ERR_DEV_NOT_SUPP -29 ///< specified device type not supported by driver
-#define MBG_ERR_INV_DEV_REQUEST -30 ///< IOCTL call not supported by driver
-#define MBG_ERR_NOT_SUPP_BY_DEV -31 ///< cmd or feature not supported by device
+#define MBG_ERR_NO_MEM -27 ///< Failed to allocate memory.
+#define MBG_ERR_CLAIM_RSRC -28 ///< Failed to claim port or mem resource.
+#define MBG_ERR_DEV_NOT_SUPP -29 ///< Device type not supported by driver.
+#define MBG_ERR_INV_DEV_REQUEST -30 ///< IOCTL call not supported by driver.
+#define MBG_ERR_NOT_SUPP_BY_DEV -31 ///< Command or feature not supported by device.
// #define MBG_ERR_USB_ACCESS -32 ///< USB access failed (FIXME TODO this is B.S., we should return *why*)
-#define MBG_ERR_CYCLIC_TIMEOUT -33 ///< cyclic event (IRQ, etc.) didn't occur
-#define MBG_ERR_NOT_SUPP_ON_OS -34 ///< function is not supported under this operating system
-#define MBG_ERR_LIB_NOT_COMPATIBLE -35 ///< installed shared lib. version not compat. with version used at build time
-#define MBG_ERR_N_COM_EXCEEDS_SUPP -36 ///< num. COM ports of the device exceeds max. supp. by driver
-#define MBG_ERR_N_STR_EXCEEDS_SUPP -37 ///< num. string formats of the device exceeds max. supp. by driver
-#define MBG_ERR_IRQ_UNSAFE -38 ///< enabled IRQ of bus-level device is unsafe with this firmware/ASIC version
-#define MBG_ERR_N_POUT_EXCEEDS_SUPP -39 ///< num. prog. outputs of the device exceeds max. supp. by driver
+#define MBG_ERR_CYCLIC_TIMEOUT -33 ///< Cyclic event (IRQ, etc.) didn't occur in time.
+#define MBG_ERR_NOT_SUPP_ON_OS -34 ///< Function is not supported on this operating system.
+#define MBG_ERR_LIB_NOT_COMPATIBLE -35 ///< Installed shared library version not compatible with version used at build time.
+#define MBG_ERR_N_COM_EXCEEDS_SUPP -36 ///< Num. COM ports of the device exceeds max. supp. by driver.
+#define MBG_ERR_N_STR_EXCEEDS_SUPP -37 ///< Num. string formats of the device exceeds max. supp. by driver.
+#define MBG_ERR_IRQ_UNSAFE -38 ///< Enabled IRQ of bus-level device is unsafe with this firmware/ASIC version.
+#define MBG_ERR_N_POUT_EXCEEDS_SUPP -39 ///< Num. prog. outputs of the device exceeds max. supp. by driver.
// Legacy codes used with DOS TSRs only:
-#define MBG_ERR_INV_INTNO -40 ///< invalid interrupt number
-#define MBG_ERR_NO_DRIVER -41 ///< a driver could not be found
-#define MBG_ERR_DRV_VERSION -42 ///< the driver is too old
+#define MBG_ERR_INV_INTNO -40 ///< Invalid interrupt number.
+#define MBG_ERR_NO_DRIVER -41 ///< No driver could be found.
+#define MBG_ERR_DRV_VERSION -42 ///< The driver is too old.
-#define MBG_ERR_COPY_TO_USER -43 ///< kernel driver failed to copy data from kernel to user space
-#define MBG_ERR_COPY_FROM_USER -44 ///< kernel driver failed to copy data from use to kernel space
+#define MBG_ERR_COPY_TO_USER -43 ///< Kernel driver failed to copy data from kernel to user space.
+#define MBG_ERR_COPY_FROM_USER -44 ///< Kernel driver failed to copy data from use to kernel space.
// More codes returned by the driver's high level functions:
-#define MBG_ERR_N_UC_MSTR_EXCEEDS_SUPP -45 ///< num. PTP unicast masters of the device exceeds max. supp. by driver
-#define MBG_ERR_N_GNSS_EXCEEDS_SUPP -46 ///< num. of GNSS systems supp. by device exceeds max. supp. by driver
-#define MBG_ERR_N_GPIO_EXCEEDS_SUPP -47 ///< num. of GPIO ports supp. by device exceeds max. supp. by driver
-#define MBG_ERR_N_XMR_EXCEEDS_SUPP -48 ///< num. of XMR sources supp. by device exceeds max. supp. by driver
-
-#define MBG_ERR_UNSPEC -60 ///< unspecified error
-
-#define MBG_ERR_HDR_CSUM -61 ///< binary protocol header checksum error
-#define MBG_ERR_DATA_CSUM -62 ///< binary protocol data checksum error
-#define MBG_ERR_RCVD_NACK -63 ///< binary protocol received reply msg with a NACK code
-#define MBG_ERR_RCVD_NO_ACK -64 ///< binary protocol received reply msg without expected ACK code //### TODO
-#define MBG_ERR_CONN_TYPE -65 ///< binary protocol no valid/supported connection type specified
-#define MBG_ERR_BYTES_WRITTEN -66 ///< binary protocol failed to write all bytes
-#define MBG_ERR_AUTH -67 ///< binary protocol failed authentication
-
-#define MBG_ERR_SOCK_INIT -68 ///< socket interface not initialized, or failed to initialize
-#define MBG_ERR_INV_SOCK_FD -69 ///< invalid socket when tried to open network socket
-#define MBG_ERR_NOT_A_SOCKET -70 ///< socket descriptor is not a socket
-#define MBG_ERR_NBLOCK_WAIT_SLCT -71 ///< select timed out when waiting for non-blocking network port to become ready
-#define MBG_ERR_NBLOCK_WAIT_WR_FD -72 ///< write fd not set after select when waiting for non-blocking network port to become ready
-
-#define MBG_ERR_IO -73 ///< input/output error
-#define MBG_ERR_INV_PARM -74 ///< invalid parameter
-#define MBG_ERR_NO_DEV -75 ///< specified device not found ### No such device. Attempted an inappropriate function.
-#define MBG_ERR_NOT_FOUND -76 ///< specified item not found
+#define MBG_ERR_N_UC_MSTR_EXCEEDS_SUPP -45 ///< Num. PTP unicast masters of the device exceeds max. supp. by driver.
+#define MBG_ERR_N_GNSS_EXCEEDS_SUPP -46 ///< Num. of GNSS systems supp. by device exceeds max. supp. by driver.
+#define MBG_ERR_N_GPIO_EXCEEDS_SUPP -47 ///< Num. of GPIO ports supp. by device exceeds max. supp. by driver.
+#define MBG_ERR_N_XMR_EXCEEDS_SUPP -48 ///< Num. of XMR sources supp. by device exceeds max. supp. by driver.
+
+#define MBG_ERR_UNSPEC -60 ///< Unspecified error.
+
+#define MBG_ERR_HDR_CSUM -61 ///< Binary protocol header checksum error.
+#define MBG_ERR_DATA_CSUM -62 ///< Binary protocol data checksum error.
+#define MBG_ERR_RCVD_NACK -63 ///< Binary protocol received reply msg with a NACK code.
+#define MBG_ERR_RCVD_NO_ACK -64 ///< Binary protocol received reply msg without expected ACK code. //### TODO
+#define MBG_ERR_CONN_TYPE -65 ///< Binary protocol no valid/supported connection type specified.
+#define MBG_ERR_BYTES_WRITTEN -66 ///< Binary protocol failed to write all bytes.
+#define MBG_ERR_AUTH -67 ///< Binary protocol failed authentication.
+
+#define MBG_ERR_SOCK_INIT -68 ///< Socket interface not initialized, or failed to initialize.
+#define MBG_ERR_INV_SOCK_FD -69 ///< Invalid socket when tried to open network socket.
+#define MBG_ERR_NOT_A_SOCKET -70 ///< Socket descriptor is not a socket.
+#define MBG_ERR_NBLOCK_WAIT_SLCT -71 ///< Select timed out when waiting for non-blocking network port to become ready.
+#define MBG_ERR_NBLOCK_WAIT_WR_FD -72 ///< Write fd not set after select when waiting for non-blocking network port to become ready.
+
+#define MBG_ERR_IO -73 ///< Input/output error.
+#define MBG_ERR_INV_PARM -74 ///< Invalid parameter.
+#define MBG_ERR_NO_DEV -75 ///< No such device, or attempted an inappropriate function.
+#define MBG_ERR_NOT_FOUND -76 ///< Specified item not found.
#define MBG_ERR_OVERFLOW -77 ///< range or buffer overflow
#define MBG_ERR_PIPE -78 ///< pipe error
#define MBG_ERR_INTR -79 ///< interrupted function call
-#define MBG_ERR_ACCESS -80 ///< Access denied, e.g. when trying to access a file or device without sufficient permissions
-#define MBG_ERR_PERM -81 ///< Operation not permitted, e.g. when trying to set the system time without sufficient permissions
+#define MBG_ERR_ACCESS -80 ///< Access denied, e.g. to an object that is already in use, or in case of insufficient permissions.
+#define MBG_ERR_PERM -81 ///< Operation not permitted, e.g. when trying to set the system time without sufficient permissions.
#define MBG_ERR_BUSY -82 ///< Device or resource busy, can't be used
#define MBG_ERR_INV_HANDLE -83 ///< invalid file/device handle specified
@@ -272,6 +394,14 @@ extern "C" {
#define MBG_ERR_SN_VRFY -114 ///< Serial number could not be verified
#define MBG_ERR_RSRC_ITEM -115 ///< Too many resource items
+#define MBG_ERR_BUFFER_TOO_SMALL -116 ///< Buffer is too small.
+
+#define MBG_ERR_OUTDATED -117
+
+#define MBG_ERR_STR_SUBSTR -118 ///< Invalid substring in string
+#define MBG_ERR_BAD_ADDRESS -119 ///< Bad Address (like POSIX EFAULT)
+
+#define MBG_ERR_IN_PROGRESS -120 ///< Long lasting operation in progress
// NOTE: New codes have to be appended to this list, and the sequence of codes must not
// be changed. Whenever new codes have been defined, appropriate entries have to be added
@@ -310,8 +440,8 @@ extern "C" {
{ MBG_ERR_TIMEOUT, "Timeout" }, \
{ MBG_ERR_FW_ID, "Invalid firmware ID" }, \
{ MBG_ERR_NBYTES, "Unexpected number of data bytes for this API" }, \
- { MBG_ERR_INV_TIME, "Invalid time passed to device" }, \
- { MBG_ERR_FIFO, "FIFO unexpectedly empty" }, \
+ { MBG_ERR_INV_TIME, "The device has no valid time" }, \
+ { MBG_ERR_NO_DATA, "No (more) data to process" }, \
{ MBG_ERR_NOT_READY, "Device not ready" }, \
{ MBG_ERR_INV_TYPE, "Unsupported data type" }, \
{ MBG_ERR_NO_MEM, "Memory allocation error" }, \
@@ -327,6 +457,8 @@ extern "C" {
{ MBG_ERR_IRQ_UNSAFE, "Unsafe IRQ support" }, \
{ MBG_ERR_N_POUT_EXCEEDS_SUPP, "Num prog. outputs exceeds supported" }, \
{ MBG_ERR_INV_INTNO, "Invalid interrupt number" }, \
+ { MBG_ERR_NO_DRIVER, "Driver not found" }, \
+ { MBG_ERR_DRV_VERSION, "Driver too old" }, \
{ MBG_ERR_N_UC_MSTR_EXCEEDS_SUPP, "Num. PTP Unicast masters exceeds supported" }, \
{ MBG_ERR_N_GNSS_EXCEEDS_SUPP, "Num. GNSS systems exceeds supported" }, \
{ MBG_ERR_N_GPIO_EXCEEDS_SUPP, "Num. GPIO ports exceeds supported" }, \
@@ -340,7 +472,7 @@ extern "C" {
{ MBG_ERR_BYTES_WRITTEN, "Failed to write all bytes" }, \
{ MBG_ERR_IO, "Input/output error" }, \
{ MBG_ERR_INV_PARM, "Invalid parameter passed to function" }, \
- { MBG_ERR_NO_DEV, "X No such device, or attempted an inappropriate function." }, \
+ { MBG_ERR_NO_DEV, "No such device, or attempted an inappropriate function." }, \
{ MBG_ERR_NOT_FOUND, "Specified item not found" }, \
{ MBG_ERR_OVERFLOW, "Buffer overflow" }, \
{ MBG_ERR_BUSY, "Device busy" }, \
@@ -370,8 +502,11 @@ extern "C" {
{ MBG_ERR_SN_GCODE_UNKN, "Unknown device group code" }, \
{ MBG_ERR_SN_GCODE_WRONG, "Wrong device group code in S/N" }, \
{ MBG_ERR_SN_VRFY, "Serial number could not be verified" }, \
- { MBG_ERR_RSRC_ITEM, "Too many resource items" }
-
+ { MBG_ERR_RSRC_ITEM, "Too many resource items" }, \
+ { MBG_ERR_BUFFER_TOO_SMALL, "Data buffer too small" }, \
+ { MBG_ERR_OUTDATED, "Software/Module is too old/outdated. Please update!" }, \
+ { MBG_ERR_STR_SUBSTR, "Invalid substring in string" }, \
+ { MBG_ERR_IN_PROGRESS, "Long lasting operation in progress" }
/**
@@ -399,8 +534,6 @@ extern "C" {
#else
#define MBG_ERR_STR_TABLE_EXT_ENG \
- { MBG_ERR_NO_DRIVER, "Driver not found" }, \
- { MBG_ERR_DRV_VERSION, "Driver too old" }, \
{ MBG_ERR_COPY_TO_USER, "Error copying to user space" }, \
{ MBG_ERR_COPY_FROM_USER, "Error copying from user space" }, \
{ MBG_ERR_AUTH, "Authentication failed" }, \
@@ -411,7 +544,7 @@ extern "C" {
{ MBG_ERR_NBLOCK_WAIT_WR_FD, "Write file descriptor not ready after waiting for port ready" }, \
{ MBG_ERR_PIPE, "Pipe error" }, \
{ MBG_ERR_INTR, "Interrupted function call" }, \
- { MBG_ERR_ACCESS, "Access denied, insufficient permission" }, \
+ { MBG_ERR_ACCESS, "Access denied" }, \
{ MBG_ERR_PERM, "Operation not permitted, insufficient rights" }, \
{ MBG_ERR_EXIST, "File exists" }, \
{ MBG_ERR_NO_ENTITY, "No such file or directory" }, \
@@ -419,7 +552,8 @@ extern "C" {
{ MBG_ERR_CONN_RESET, "Connection reset by peer" }, \
{ MBG_ERR_NO_SPACE, "Insufficient disk space" }, \
{ MBG_ERR_PAM, "PAM authentication was not successful" }, \
- { MBG_ERR_TIMER, "Timer expired" } \
+ { MBG_ERR_TIMER, "Timer expired" }, \
+ { MBG_ERR_BAD_ADDRESS, "Bad Address" }
#endif
@@ -506,6 +640,21 @@ bool mbg_rc_is_success_or_err_perm( int rc )
+/**
+ * @brief Predefined exit codes returned by some tools.
+ */
+enum MBG_EXIT_CODES
+{
+ MBG_EXIT_CODE_SUCCESS, ///< Requested action completed successfully.
+ MBG_EXIT_CODE_USAGE, ///< Unable to handle requested action, usage printed.
+ MBG_EXIT_CODE_NOT_SUPP, ///< Requested action not supported on the running OS.
+ MBG_EXIT_CODE_FAIL, ///< Action failed for specified device.
+ MBG_EXIT_CODE_INV_TIME, ///< Device has no valid time to set the system time with.
+ N_MBG_EXIT_CODES
+};
+
+
+
/* ----- function prototypes begin ----- */
/* This section was generated automatically */
@@ -575,14 +724,24 @@ bool mbg_rc_is_success_or_err_perm( int rc )
int mbg_cvi_rs232_error_to_mbg( int cvi_rc, const char *info ) ;
/**
- * @brief Translate a Windows non-socket API error code to one of the @ref MBG_ERROR_CODES
+ * @brief Translate a Windows NTSTATUS code to one of the @ref MBG_ERROR_CODES
*
- * @param[in] last_err A Windows non-socket API error code as returned by GetLastError()
- * @param[in] info An optional informational text string, or NULL
+ * @param[in] st One of the NTSTATUS codes defined in ntstatus.h
+ * @param[in] info An optional informational text string, or NULL
*
* @return One of the @ref MBG_ERROR_CODES
*/
- int mbg_win32_last_err_to_mbg( DWORD last_err, const char *info ) ;
+ int mbg_win32_ntstatus_to_mbg( NTSTATUS st, const char *info ) ;
+
+ /**
+ * @brief Translate a Windows non-socket API return code to one of the @ref MBG_RETURN_CODES
+ *
+ * @param[in] win32_sys_rc A Windows non-socket API error code as returned by GetLastError(), or ERROR_SUCCESS.
+ * @param[in] info An optional informational text string, or NULL.
+ *
+ * @return One of the @ref MBG_RETURN_CODES
+ */
+ int mbg_win32_sys_err_to_mbg( DWORD win32_sys_rc, const char *info ) ;
/**
* @brief Translate a Windows socket API error code to one of the @ref MBG_ERROR_CODES
@@ -592,7 +751,7 @@ bool mbg_rc_is_success_or_err_perm( int rc )
*
* @return One of the @ref MBG_ERROR_CODES
*/
- int mbg_win32_wsa_err_to_mbg( DWORD wsa_err, const char *info ) ;
+ int mbg_win32_wsa_err_to_mbg( int wsa_err, const char *info ) ;
/**
* @brief Translate a POSIX errno error code to one of the @ref MBG_ERROR_CODES
diff --git a/mbglib/common/mbgtime.h b/mbglib/common/mbgtime.h
index 6ca8fd5..b6064a8 100755
--- a/mbglib/common/mbgtime.h
+++ b/mbglib/common/mbgtime.h
@@ -1,7 +1,7 @@
/**************************************************************************
*
- * $Id: mbgtime.h 1.29 2018/02/28 16:58:10 martin REL_M $
+ * $Id: mbgtime.h 1.37 2019/08/28 13:19:40 martin REL_M $
*
* Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
*
@@ -10,15 +10,33 @@
*
* -----------------------------------------------------------------------
* $Log: mbgtime.h $
+ * Revision 1.37 2019/08/28 13:19:40 martin
+ * New structures MBG_TZ_INFO and MBG_LS_INFO which
+ * are used by some new functions in new module mbgtimex.c.
+ * Revision 1.36 2019/08/02 07:51:30 martin
+ * New type MBG_TIME64_T.
+ * Revision 1.35 2019/07/23 07:27:37 martin
+ * Added definition GPS_INITIAL_DAY.
+ * Revision 1.34 2019/07/19 08:39:19 martin
+ * Don't mark frac_sec_from_bin() as deprecated anymore.
+ * Revision 1.33 2019/07/19 07:49:22Z martin
+ * Modified dec_frac_to_bin_frac_{16,32} so that no compiler
+ * warning is emitted if a large data type is passed as an argument.
+ * Revision 1.32 2019/06/26 10:03:02Z martin
+ * Updated function prototypes.
+ * Revision 1.31 2019/02/06 10:08:09 martin
+ * Added symbol HNS_PER_MIN.
+ * Revision 1.30 2018/11/26 12:04:44Z martin
+ * Moved definition NTP_FRAC_PER_SEC here.
* Revision 1.29 2018/02/28 16:58:10 martin
* Removed reference to frac_sec_from_bin().
* Revision 1.28 2018/01/15 18:18:49 martin
- * Renamed symbol NSECS_PER_SEC to NSEC_PER_SEC
+ * Renamed symbol NSECS_PER_SEC to NSEC_PER_SEC.
* according to namings of similar symbols.
* Revision 1.27 2017/11/29 11:14:57 gregoire
- * Added Multiplier MSEC_TO_NSEC_MULTIPLIER, MSEC_TO_USEC_MULTIPLIER
+ * Added Multiplier MSEC_TO_NSEC_MULTIPLIER, MSEC_TO_USEC_MULTIPLIER.
* Revision 1.26 2017/11/16 13:33:46 philipp
- * Added USEC_PER_SEC define
+ * Added USEC_PER_SEC define.
* Revision 1.25 2017/08/15 15:48:59 martin
* Define NSECS_PER_SEC only if it hasn't been defined before.
* Revision 1.24 2017/07/04 14:02:25 martin
@@ -112,6 +130,28 @@ extern "C" {
/* Start of header body */
+/**
+ * @brief A POSIX-like timestamp which is always 64 bits wide.
+ *
+ * Depending on the build and target environment, the original
+ * POSIX time_t may be only 32 bits wide, and thus will roll over
+ * in year 2038. Using this type for time conversion functions
+ * which also support this avoid the rollover on such systems.
+ */
+typedef int64_t MBG_TIME64_T;
+
+
+
+/**
+ * @brief The number of days from 0000-01-01 until GPS epoch
+ *
+ * The number of days as computed by ::n_days for the date
+ * of the GPS epoch, 1980-01-06.
+ *
+ * @see ::n_days
+ */
+#define GPS_INITIAL_DAY 722819L
+
/**
* @brief GPS epoch bias from ordinary time_t epoch
@@ -137,6 +177,10 @@ extern "C" {
*/
#define NTP_SEC_BIAS 2208988800UL
+#if !defined( MBG_TGT_MISSING_64_BIT_TYPES )
+ #define NTP_FRAC_PER_SEC (uint64_t) 4294967296.0
+#endif
+
// Modified Julian Day (MJD) numbers for some commonly used epochs.
@@ -264,6 +308,9 @@ typedef struct
#define HNS_PER_MS 10000L
#endif
+#if !defined( HNS_PER_MIN )
+ #define HNS_PER_MIN ( HNS_PER_SEC * SECS_PER_MIN )
+#endif
#define MSEC_TO_NSEC_MULTIPLIER ( NSEC_PER_SEC / MSEC_PER_SEC )
#define MSEC_TO_USEC_MULTIPLIER ( USEC_PER_SEC / MSEC_PER_SEC )
@@ -391,6 +438,76 @@ _ext DAYS_OF_MONTH_TABLE days_of_month
/**
+ * @brief DST on/off times pre-computed for a given year.
+ *
+ * Used like a cache to avoid redundant expensive computation
+ * of DST switching times for a given year inside an application.
+ * Not to be used for data exchange between devices.
+ *
+ * By default, switching times @a #t_on and @a #t_off are stored
+ * as local standard time (i.e. %UTC + @a #offs already applied)
+ * because this is most suitable for %UTC to local time conversions.
+ *
+ * However, for some cases (e.g. with PTP SMPTE) the switching
+ * times need to be compared to TAI times, so the function
+ * ::mbg_tz_info_to_tai can be used to convert the switching
+ * times to TAI. Care must be taken that the TAI switching times
+ * also need to be updated whenever a leap second event occurrs.
+ *
+ * @see ::mbg_set_tz_info_for_year
+ * @see ::mbg_set_tz_info_for_utc_time_t
+ * @see ::mbg_tz_info_to_tai
+ */
+typedef struct
+{
+ MBG_TIME64_T t_on; ///< 'DST on' time, local standard time (default), or TAI.
+ MBG_TIME64_T t_off; ///< 'DST off' time, local standard time (default), or TAI.
+ int offs; ///< Offset to be added to %UTC to yield local standard time [sec].
+ int offs_dl; ///< Additional offset to be added if daylight saving is in effect [sec].
+ int year; ///< The year number for which @a #t_on and @a #t_off have been computed.
+ int auto_flag; ///< A flag indicating that @a #t_on and @a #t_off were computed by automatic rules.
+ int valid; ///< A flag indicating that the information in this structure has been set up.
+
+} MBG_TZ_INFO;
+
+
+
+/**
+ * @brief Current %UTC/TAI Offset And Leap Second Information.
+ *
+ * The stored information can be retrieved e.g. from the
+ * ::UTC data set transmitted by the GPS satellites,
+ * or from an NTP leap second file.
+ *
+ * @see ::mbg_set_ls_info_from_gps_utc
+ */
+typedef struct
+{
+ /// @brief Time of the nearest leap second, if available, in %UTC time scale.
+ /// Should match %UTC midnight at the end of the last day in June or December of a given year.
+ MBG_TIME64_T t_ls_utc;
+
+ /// @brief Time of the nearest leap second, if available, in TAI time scale.
+ /// Should be ahead of @a #t_ls_utc by a number of leap seconds (~37 s in year 2019)
+ /// that have already been inserted in the past.
+ MBG_TIME64_T t_ls_tai;
+
+ /// @brief Number of seconds to be inserted into the %UTC time scale at the leap second transition.
+ /// This is 0 as long as no leap second announcement is currently available, +1 for a leap
+ /// second to be inserted, and -1 for a leap second to be deleted, which has yet never happened.
+ int ls_step;
+
+ int offs_gps_utc; ///< Number of seconds the GPS system time is ahead of %UTC after the leap second transition.
+
+ int offs_tai_utc; ///< Number of seconds TAI is ahead of %UTC after the leap second transition.
+
+ int valid; ///< Indicates that the structure has been set up.
+
+} MBG_LS_INFO;
+
+
+
+/**
* @brief Convert a 16 bit binary fraction to a scaled decimal
*
* @param[in] bin The binary fraction
@@ -444,17 +561,17 @@ uint32_t bin_frac_32_to_dec_frac( uint32_t bin, uint32_t scale )
// casting, at least for a partial expression.
static __mbg_inline
-uint16_t dec_frac_to_bin_frac_16( uint32_t dec, uint32_t scale )
+uint16_t dec_frac_to_bin_frac_16( MBG_FRAC32_CONVERSION_TYPE dec, uint32_t scale )
{
- return (uint16_t) ( ( ( (MBG_FRAC32_CONVERSION_TYPE) dec * 0x20000 / scale ) + 1 ) >> 1 );
+ return (uint16_t) ( ( ( dec * 0x20000 / scale ) + 1 ) >> 1 );
} // dec_frac_to_bin_frac_16
static __mbg_inline
-uint32_t dec_frac_to_bin_frac_32( uint32_t dec, uint32_t scale )
+uint32_t dec_frac_to_bin_frac_32( MBG_FRAC32_CONVERSION_TYPE dec, uint32_t scale )
{
- return (uint32_t) ( ( ( (MBG_FRAC32_CONVERSION_TYPE) dec * MBG_FRAC32_UNITS_PER_SEC * 2 / scale ) + 1 ) >> 1 );
+ return (uint32_t) ( ( ( dec * MBG_FRAC32_UNITS_PER_SEC * 2 / scale ) + 1 ) >> 1 );
} // dec_frac_to_bin_frac_32
@@ -482,10 +599,14 @@ uint32_t dec_frac_to_bin_frac_32( uint32_t dec, uint32_t scale )
/**
* @brief Convert a binary fraction to a scaled decimal
*
- * Convert a binary fraction (e.g. of a second, as in ::PCPS_TIME_STAMP::frac)
- * to a decimal fraction, using a specified scale factor.
+ * Convert a binary fraction (e.g. as in ::PCPS_TIME_STAMP::frac)
+ * to a decimal fraction, using a specified scale factor. Depending
+ * on the @p scale factor, the result can be milliseconds, microseconds,
+ * nanoseconds, or whatever.
*
- * @deprecated This function is deprecated, use ::bin_frac_32_to_dec_frac preferably.
+ * This function is actually just an alias for ::bin_frac_32_to_dec_frac,
+ * but has been introduced much erlier than the latter, and thus is kept
+ * for compatibility reasons.
*
* @param[in] b The binary fraction
* @param[in] scale The scale factor
@@ -495,7 +616,7 @@ uint32_t dec_frac_to_bin_frac_32( uint32_t dec, uint32_t scale )
* @see ::bin_frac_32_to_dec_frac
*/
static __mbg_inline
-uint32_t _DEPRECATED_BY( "bin_frac_32_to_dec_frac" ) frac_sec_from_bin( uint32_t b, uint32_t scale )
+uint32_t frac_sec_from_bin( uint32_t b, uint32_t scale )
{
return bin_frac_32_to_dec_frac( b, scale );
@@ -584,12 +705,18 @@ double dfrac_sec_from_bin( uint32_t b )
/**
* @brief Convert second-of-week to day-of-week and time-of-day
*
- * @param[in] wsec The second-of-week number to be converted
- * @param[out] tm Address of a ::TM_GPS structure which takes the computed results
+ * @param[in] wsec The second-of-week number to be converted.
+ * Must not be negative.
+ * @param[out] tm Address of a ::TM_GPS structure which takes
+ * the computed results. Updates the fields
+ * ::TM_GPS::hour, ::TM_GPS::min, ::TM_GPS::sec,
+ * and ::TM_GPS::wday in the range 0..6, with
+ * 0 = Sunday.
*
* @return Pointer to the ::TM_GPS structure that has been passed
*
* @see ::tm_to_wsec
+ * @see ::day_of_week_sun06
*/
TM_GPS *wsec_to_tm( long wsec, TM_GPS *tm ) ;
@@ -636,21 +763,43 @@ double dfrac_sec_from_bin( uint32_t b )
void date_of_year ( int year, int day_num, TM_GPS *tm ) ;
/**
- * @brief Compute day-of-week from a given date
+ * @brief Compute day-of-week for a given date.
*
- * @todo Specify range of returned day-of-week. Should we just call n_days()?
+ * ATTENTION: The computed day-of-week is in the range 0..6,
+ * with 0 = Monday (!).
*
- * @param[in] day The day-of-month
- * @param[in] month The month
+ * In most cases the function ::day_of_week_sun06 is
+ * more suitable for applications.
+ *
+ * @param[in] day The day-of-month, 0..31
+ * @param[in] month The month, 1..12
* @param[in] year The full year number
*
- * @return The computed day-of-week
+ * @return The computed day-of-week, 0..6, 0 = Monday (!)
*
+ * @see ::day_of_week_sun06
* @see ::n_days
*/
int day_of_week( int day, int month, int year ) ;
/**
+ * @brief Compute day-of-week for a given date.
+ *
+ * The computed day-of-week is in the range 0..6,
+ * with 0 = Sunday, as expected by most applications.
+ *
+ * @param[in] day The day-of-month, 0..31
+ * @param[in] month The month, 1..12
+ * @param[in] year The full year number
+ *
+ * @return The computed day-of-week, 0..6, with 0 = Sunday.
+ *
+ * @see ::n_days
+ * @see ::wsec_to_tm
+ */
+ int day_of_week_sun06( int day, int month, int year ) ;
+
+ /**
* @brief Update a year number by a number of days, accounting for leap years
*
* @param[in] day_num The number of days to evaluate
diff --git a/mbglib/common/str_util.c b/mbglib/common/str_util.c
index c851036..859b5e3 100755
--- a/mbglib/common/str_util.c
+++ b/mbglib/common/str_util.c
@@ -1,7 +1,7 @@
/**************************************************************************
*
- * $Id: str_util.c 1.4 2018/06/25 13:22:42 martin REL_M $
+ * $Id: str_util.c 1.6 2019/07/31 15:42:38 martin REL_M $
*
* Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
*
@@ -10,6 +10,13 @@
*
* -----------------------------------------------------------------------
* $Log: str_util.c $
+ * Revision 1.6 2019/07/31 15:42:38 martin
+ * Doxygen changes.
+ * Revision 1.5 2018/08/23 13:07:16 martin
+ * Moved the snprintf() safety checks to new inline functions that
+ * can also be used called from specific kernel mode functions.
+ * Unified variable naming.
+ * More common __attribute__ syntax.
* Revision 1.4 2018/06/25 13:22:42 martin
* Many functios return int rather than size_t, like standard
* library functions.
@@ -46,14 +53,14 @@ static /*HDR*/
// Under DOS we use the Borland C/C++ v3.1 compiler by default, which
// doesn't provide a vsnprintf() function, so we use a simple replacement
// here. Since we share most of the source code between several target
-// systems we assume that if it our code works properly for other targets
+// systems we assume that if our code works properly for other targets
// which really provide a vsnprintf() function then it also works properly
// under DOS. ;-)
-int vsnprintf( char *s, size_t max_len, const char *fmt, va_list arg_list )
+int vsnprintf( char *s, size_t max_len, const char *fmt, va_list args )
{
(void) max_len; // quiet compiler warning "not used"
- return vsprintf( s, fmt, arg_list );
+ return vsprintf( s, fmt, args );
} // vsnprintf
@@ -71,12 +78,12 @@ int vsnprintf( char *s, size_t max_len, const char *fmt, va_list arg_list )
*
* If the output exceeds the buffer size and thus is truncated then:<br>
*
- * - Under Windows a negative value is returned and eventually *no*
+ * - Under Windows a negative value is returned and eventually ***no***
* terminating 0 is written to the output buffer, so the output string
* may not be terminated properly.
*
- * - Some versions of glibc return the number of bytes that *would*
- * have been written to the buffer *if* the buffer would have been
+ * - Some versions of glibc return the number of bytes that ***would***
+ * have been written to the buffer ***if*** the buffer would have been
* large enough, instead of the true number of characters that have
* been written to the buffer.
*
@@ -90,7 +97,7 @@ int vsnprintf( char *s, size_t max_len, const char *fmt, va_list arg_list )
* This wrapper function takes care that strings are always terminated
* properly, and that the returned value always matches the number of
* characters really written to the string buffer, excluding the
- * terminating 0
+ * terminating 0.
*
* @note The "size_t" type parameter used to specify the buffer size
* can be larger (e.g. "unsigned long") than the "int" type returned
@@ -103,7 +110,7 @@ int vsnprintf( char *s, size_t max_len, const char *fmt, va_list arg_list )
* @param[out] s The string buffer to be filled
* @param[in] max_len Size of the output buffer for 0-terminated string
* @param[in] fmt Format string according to subsequent parameters
- * @param[in] ap Variable argument list in va_list format
+ * @param[in] args Variable argument list in va_list format
*
* @return Number of characters written to the output buffer, except the terminating 0
*
@@ -112,26 +119,18 @@ int vsnprintf( char *s, size_t max_len, const char *fmt, va_list arg_list )
* @see ::sn_cpy_str_safe
* @see ::sn_cpy_char_safe
*/
-int __attribute__( ( format( printf, 3, 0 ) ) )
-vsnprintf_safe( char *s, size_t max_len, const char *fmt, va_list ap )
+__attribute__( ( format( printf, 3, 0 ) ) )
+int vsnprintf_safe( char *s, size_t max_len, const char *fmt, va_list args )
{
size_t n;
- if ( s == NULL || max_len == 0 )
+ if ( !mbg_buffer_specs_valid( s, max_len ) )
return 0; // nothing to do anyway
+ n = mbg_vsnprintf( s, max_len, fmt, args );
- mbg_vsnprintf( s, max_len, fmt, ap );
-
- // Force proper worst-case termination of the output string.
- s[max_len - 1] = 0;
-
- // The return type of strlen() is usually size_t, so
- // we can safely return the true length of the string
- // written to the buffer.
- n = strlen( s );
-
- return _int_from_size_t( n );
+ // Do some common checks to avoid subsequent buffer overflows, etc.
+ return mbg_chk_snprint_results( n, s, max_len );
} // vsnprintf_safe
@@ -155,17 +154,15 @@ vsnprintf_safe( char *s, size_t max_len, const char *fmt, va_list ap )
* @see ::sn_cpy_str_safe
* @see ::sn_cpy_char_safe
*/
-int __attribute__( ( format( printf, 3, 4 ) ) )
-snprintf_safe( char *s, size_t max_len, const char * fmt, ... )
+__attribute__( ( format( printf, 3, 4 ) ) )
+int snprintf_safe( char *s, size_t max_len, const char * fmt, ... )
{
- va_list ap;
+ va_list args;
int len;
- va_start( ap, fmt );
-
- len = vsnprintf_safe( s, max_len, fmt, ap );
-
- va_end( ap );
+ va_start( args, fmt );
+ len = vsnprintf_safe( s, max_len, fmt, args );
+ va_end( args);
return len;
@@ -180,7 +177,7 @@ static __mbg_inline
* This is the basic function used to implemment ::strncpy_safe and
* ::sn_cpy_safe. This function takes care that the copied string
* is always terminated by 0, but any remaining buffer space
- * is *not* filled up with '0' characters.
+ * is ***not*** filled up with '0' characters.
*
* @param[out] dst Pointer to the output buffer
* @param[in] src Pointer to the input buffer
@@ -235,7 +232,7 @@ size_t do_str_copy_safe( char *dst, const char *src, size_t n )
* buffer length then the string in the output buffer is not 0-terminated.
*
* Our implementation always forces a proper termination by 0, but unlike
- * the original implementation of strncpy() it does *not* fill the whole
+ * the original implementation of strncpy() it does ***not*** fill the whole
* remaining buffer space with '0' characters.
*
* @param[out] dst Pointer to the output buffer
diff --git a/mbglib/common/str_util.h b/mbglib/common/str_util.h
index e4d7338..cdc0be6 100755
--- a/mbglib/common/str_util.h
+++ b/mbglib/common/str_util.h
@@ -1,7 +1,7 @@
/**************************************************************************
*
- * $Id: str_util.h 1.5 2018/06/25 13:24:15 martin REL_M $
+ * $Id: str_util.h 1.7 2019/07/31 15:42:39 martin REL_M $
*
* Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
*
@@ -10,6 +10,13 @@
*
* -----------------------------------------------------------------------
* $Log: str_util.h $
+ * Revision 1.7 2019/07/31 15:42:39 martin
+ * Doxygen changes.
+ * Revision 1.6 2018/08/23 13:10:26 martin
+ * New inline functions mbg_buffer_specs_valid() and
+ * mbg_chk_snprint_results() that can also be called
+ * from code used in kernel mode.
+ * Updated function prototypes.
* Revision 1.5 2018/06/25 13:24:15 martin
* Updated function prototypes.
* Revision 1.4 2017/05/10 15:26:10 martin
@@ -33,8 +40,12 @@
#include <words.h> // implicitly includes mbg_tgt.h for non-firmware projects
-#include <stdlib.h>
-#include <stdarg.h>
+#if defined( MBG_TGT_KERNEL )
+ #include <mbgddmsg.h>
+#else
+ #include <stdlib.h>
+ #include <stdarg.h>
+#endif
#ifdef _STR_UTIL
@@ -58,6 +69,88 @@ _ext const char *str_not_avail
#endif
;
+
+
+static __mbg_inline /*HDR*/
+/**
+ * @brief Check if the buffer plus size parameters passed to a function are valid.
+ *
+ * This function can be used to check parameters that have been
+ * passed to another function to specify an output buffer to be filled.
+ *
+ * If no buffer has been specified, or the size of the buffer which
+ * eventually remains after a previous operation is 0 or even less than 0
+ * then no data can be placed in the buffer.
+ *
+ * @param[in] s The address of the specified buffer
+ * @param[in] max_len The size of the specified buffer.
+ *
+ * @return true if the buffer address is not NULL and the size is > 0, else false.
+ */
+bool mbg_buffer_specs_valid( char *s, size_t max_len )
+{
+ return s != NULL && max_len > 0;
+
+} // mbg_buffer_specs_valid
+
+
+
+static __mbg_inline /*HDR*/
+/**
+ * @brief Check the results of an snprintf()-like function.
+ *
+ * Implementations of snprintf()-like functions may behave differently
+ * and badly if the specified output buffer is too small.
+ * The exact behavior depends on the runtime library shipped with a
+ * specific build environment for a specific operating system,
+ * the version of that runtime library, and may even differ depending
+ * on whether kernel mode or user mode code is compiled.
+ *
+ * This function can be called after any snprintf()-like function
+ * to make sure that a valid buffer is always 0-terminated, and the
+ * number returned to indicate how many bytes have been written to the
+ * buffer is never less than 0, and never exceeds the real size
+ * of the buffer.
+ *
+ * @param[in] n The return code from an snprintf()-like function that has been called before.
+ * @param[in] s The address of the buffer that had been passed to the snprintf()-like function.
+ * @param[in] max_len The size of the specified buffer that had been passed to the snprintf()-like function.
+ *
+ * @return The real number of bytes that had been written to the buffer.
+ *
+ * @see ::vsnprintf_safe
+ * @see ::mbg_kdd_vsnprintf
+ * @see ::mbg_buffer_specs_valid
+ */
+int mbg_chk_snprint_results( size_t n, char *s, size_t max_len )
+{
+ if ( !mbg_buffer_specs_valid( s, max_len ) )
+ return 0; // Buffer parameters are not valid.
+
+
+ // Force proper worst-case termination of the output string.
+ s[max_len - 1] = 0;
+
+ // If n is > 0, but less than the specified buffer size we
+ // assume the value is correct.
+ if ( n > 0 && n < max_len )
+ goto out;
+
+ // Determine the real string length, but don't just call strlen()
+ // since that function may not be available in kernel mode.
+ for ( n = 0; s[n]; n++ );
+
+out:
+ // Most snprintf()-like functions take a "size_t" to specify the buffer size,
+ // but just return an "int", which may be a smaller data type than "size_t",
+ // so we do a conversion here, if required, and try to do the conversion
+ // in a safe way.
+ return _int_from_size_t( n );
+
+} // mbg_chk_snprint_results
+
+
+
#define _sn_cpy_str_safe( _dst, _src ) sn_cpy_str_safe( _dst, sizeof( _dst ), _src )
@@ -75,12 +168,12 @@ _ext const char *str_not_avail
*
* If the output exceeds the buffer size and thus is truncated then:<br>
*
- * - Under Windows a negative value is returned and eventually *no*
+ * - Under Windows a negative value is returned and eventually ***no***
* terminating 0 is written to the output buffer, so the output string
* may not be terminated properly.
*
- * - Some versions of glibc return the number of bytes that *would*
- * have been written to the buffer *if* the buffer would have been
+ * - Some versions of glibc return the number of bytes that ***would***
+ * have been written to the buffer ***if*** the buffer would have been
* large enough, instead of the true number of characters that have
* been written to the buffer.
*
@@ -94,7 +187,7 @@ _ext const char *str_not_avail
* This wrapper function takes care that strings are always terminated
* properly, and that the returned value always matches the number of
* characters really written to the string buffer, excluding the
- * terminating 0
+ * terminating 0.
*
* @note The "size_t" type parameter used to specify the buffer size
* can be larger (e.g. "unsigned long") than the "int" type returned
@@ -107,7 +200,7 @@ _ext const char *str_not_avail
* @param[out] s The string buffer to be filled
* @param[in] max_len Size of the output buffer for 0-terminated string
* @param[in] fmt Format string according to subsequent parameters
- * @param[in] ap Variable argument list in va_list format
+ * @param[in] args Variable argument list in va_list format
*
* @return Number of characters written to the output buffer, except the terminating 0
*
@@ -116,7 +209,7 @@ _ext const char *str_not_avail
* @see ::sn_cpy_str_safe
* @see ::sn_cpy_char_safe
*/
- int __attribute__( ( format( printf, 3, 0 ) ) ) vsnprintf_safe( char *s, size_t max_len, const char *fmt, va_list ap ) ;
+ __attribute__( ( format( printf, 3, 0 ) ) ) int vsnprintf_safe( char *s, size_t max_len, const char *fmt, va_list args ) ;
/**
* @brief A portable, safe implementation of snprintf()
@@ -135,7 +228,7 @@ _ext const char *str_not_avail
* @see ::sn_cpy_str_safe
* @see ::sn_cpy_char_safe
*/
- int __attribute__( ( format( printf, 3, 4 ) ) ) snprintf_safe( char *s, size_t max_len, const char * fmt, ... ) ;
+ __attribute__( ( format( printf, 3, 4 ) ) ) int snprintf_safe( char *s, size_t max_len, const char * fmt, ... ) ;
/**
* @brief A portable, safe implementation of strncpy()
@@ -145,7 +238,7 @@ _ext const char *str_not_avail
* buffer length then the string in the output buffer is not 0-terminated.
*
* Our implementation always forces a proper termination by 0, but unlike
- * the original implementation of strncpy() it does *not* fill the whole
+ * the original implementation of strncpy() it does ***not*** fill the whole
* remaining buffer space with '0' characters.
*
* @param[out] dst Pointer to the output buffer
diff --git a/mbglib/common/timeutil.c b/mbglib/common/timeutil.c
index ec42d02..0c525f9 100755
--- a/mbglib/common/timeutil.c
+++ b/mbglib/common/timeutil.c
@@ -1,7 +1,7 @@
/**************************************************************************
*
- * $Id: timeutil.c 1.7 2018/01/30 09:51:35 martin REL_M $
+ * $Id: timeutil.c 1.11 2019/07/19 14:20:52 martin REL_M $
*
* Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
*
@@ -10,7 +10,17 @@
*
* -----------------------------------------------------------------------
* $Log: timeutil.c $
- * Revision 1.7 2018/01/30 09:51:35 martin
+ * Revision 1.11 2019/07/19 14:20:52 martin
+ * Cleaned up mbg_clock_gettime() and mbg_clock_settime(),
+ * and let them set an appropriate POSIX error code on error.
+ * Revision 1.10 2019/02/08 10:51:44 martin
+ * Removed some definitions that are also in the header file.
+ * Fixed a compiler warning.
+ * Revision 1.9 2018/12/18 11:00:48 martin
+ * Implemented setting time on Windows.
+ * Revision 1.8 2018/12/11 15:02:57Z martin
+ * Cast to avoid build error on Windows.
+ * Revision 1.7 2018/01/30 09:51:35Z martin
* Let snprint_gmtime_error() return an int instead of size_t.
* Updated mbg_clock_gettime():
* Revision 1.6 2018/01/15 18:31:08Z martin
@@ -40,6 +50,7 @@
#if defined( MBG_TGT_WIN32 )
#include <stdio.h>
+ #include <errno.h>
#endif
@@ -69,88 +80,131 @@ int snprint_gmtime_error( char *s, size_t max_len, int mbg_errno, time_t t, cons
#if defined( MBG_TGT_WIN32 )
-typedef int clockid_t;
-#define clockid_t clockid_t
-
-#define CLOCK_REALTIME ( (clockid_t) 0 )
-
/*HDR*/
+/**
+ * @brief A Windows implementation for POSIX clock_gettime().
+ *
+ * @param[in] clock_id Identifier of a specific clock, e.g.
+ * CLOCK_REALTIME, CLOCK_MONOTONIC, etc.
+ * @param[out] tp Address of a struct timespec to take up
+ * the current time.
+ *
+ * @return 0 on success, -1 on error, just like the POSIX function.
+ * In case of an error the POSIX errno variable is set
+ * appropriately.
+ */
int mbg_clock_gettime( clockid_t clock_id, struct timespec *tp )
{
- // FIXME TODO Should we return a POSIX-compatible return code
- // with 0 for success and -1 indicating an error?
- //
- // If we'd do we had to set "errno" or the value returned
- // by Windows' GetLastError() to some specific error code
- // that can be retrieved by the application. However, there's
- // a problem when timespec_get() is used to implement this.
-
- // On success, timespec_get() returns the value of the "base"
- // parameter that has been passed to timespec_get(), e.g.
- // TIME_UTC. On error, it returns 0, but the usual docs for
- // timespec_get() don't mention if there's a way to determine
- // the reason for the failure, e.g. by retrieving a value
- // from"errno" or so.
- //
- // So for now we return 0 on success as usual, and -1
- // on error, but the caller is unable to determine the
- // reason for the fauilure.
-
- if ( clock_id == CLOCK_REALTIME )
+ switch ( clock_id )
{
- #if defined( TIME_UTC ) // C11 / VS2015+
- int rc = timespec_get( tp, TIME_UTC );
- return ( rc == 0 ) ? -1 : 0;
- #else
- #define EPOCH_HNS 116444736000000000i64
- FILETIME ft;
- unsigned __int64 tmp;
- gstaft_fnc( &ft );
- tmp = ( (__int64) ft.dwHighDateTime << 32 ) | ft.dwLowDateTime;
- tmp -= EPOCH_HNS; // convert to Unix epoch
- tmp *= 100; // convert to nanoseconds
- tp->tv_sec = ( tmp / NSEC_PER_SEC );
- tp->tv_nsec = (long) ( tmp % NSEC_PER_SEC );
- return 0;
- #endif
+ case CLOCK_REALTIME:
+ case CLOCK_MONOTONIC:
+ // FIXME TODO CLOCK_MONOTONIC should be implemented separately,
+ // e.g. using QPC,
+ {
+ #if defined( TIME_UTC ) // C11 / VS2015+
+ if ( timespec_get( tp, TIME_UTC ) == 0 )
+ goto fail; // error
+ #else
+ FILETIME ft;
+ uint64_t tmp;
+ gstaft_fnc( &ft );
+ // Always succeeds.
+ tmp = ( (uint64_t) ft.dwHighDateTime << 32 ) | ft.dwLowDateTime;
+ tmp -= FILETIME_1970; // convert to Unix epoch
+ tmp *= 100; // convert to nanoseconds
+ tp->tv_sec = (time_t) ( tmp / NSEC_PER_SEC );
+ tp->tv_nsec = (long) ( tmp % NSEC_PER_SEC );
+ #endif
+ } break;
+
+ default:
+ goto fail;
}
- else
- return -1; // TODO this is e.g. in case of CLOCK_MONOTONIC, we could use QPC then.
+
+ // Just like POSIX clock_gettime(), we return 0 on success.
+ return 0;
+
+fail:
+ // The specified clock_id is not supported.
+ // Set the POSIX errno variable appropriately
+ // and return -1 just like clock_gettime()
+ // does.in case of error.
+ errno = EINVAL;
+ return -1;
} // mbg_clock_gettime
/*HDR*/
+/**
+ * @brief A Windows implementation for POSIX clock_settime().
+ *
+ * @param[in] clock_id Identifier of a specific clock, i.e.
+ * CLOCK_REALTIME which is the only clock
+ * supported by this call.
+ * @param[in] tp Pointer to a struct timespec providing
+ * the time to be set.
+ *
+ * @return 0 on success, -1 on error, just like the POSIX function.
+ * In case of an error the POSIX errno variable is set
+ * appropriately.
+ */
int mbg_clock_settime( clockid_t clock_id, const struct timespec *tp )
{
+ DWORD dw;
+
if ( clock_id == CLOCK_REALTIME )
{
-#if 0 // ### TODO FIXME This needs to be implemented.
- #if defined( TIME_UTC ) // C11 / VS2015+
- int rc = timespec_get( res, TIME_UTC ); // TODO Check this code
- return ( rc == 0 ) ? -1 : 0 // rc == 0 means error
- #else
- #define EPOCH_HNS 116444736000000000i64
+ SYSTEMTIME st;
+ union
+ {
FILETIME ft;
- unsigned __int64 tmp;
- gstaft_fnc( &ft );
- tmp = ( (__int64) ft.dwHighDateTime << 32 ) | ft.dwLowDateTime;
- tmp -= EPOCH_HNS; // convert to Unix epoch
- tmp *= 100; // convert to nanoseconds
- res->tv_sec = ( tmp / NSEC_PER_SEC );
- res->tv_nsec = ( tmp % NSEC_PER_SEC );
- return 0;
- #endif
-#endif
+ ULONGLONG ull;
+ } t;
+
+ t.ull = FILETIME_1970 +
+ (ULONGLONG) tp->tv_sec * HNS_PER_SEC +
+ (ULONGLONG) tp->tv_nsec / 100;
+
+ // According to the MS API docs the FILETIME to be converted
+ // must be less than 0x8000000000000000. Otherwise, the function
+ // fails, and apparently sets error code ERROR_INVALID_PARAMETER.
+ if ( !FileTimeToSystemTime( &t.ft, &st ) )
+ goto fail;
+
+ if ( !SetSystemTime( &st ) )
+ goto fail;
+
+ // Just like POSIX clock_settime(), we return 0 on success.
+ return 0;
+}
- return 0; // FIXME this is actually not true
+fail:
+ // One of the Windows API calls above failed, so the
+ // original error information is a Windows error code.
+ // Anyway, we try to find an appropriate POSIX error
+ // code, set the POSIX errno variable accordingly, and
+ // return -1 just like POSIX clock_settime() does
+ //.in case of error.
+
+ dw = GetLastError();
+
+ switch ( dw )
+ {
+ case ERROR_PRIVILEGE_NOT_HELD:
+ errno = EPERM;
+ break;
+
+ case ERROR_INVALID_PARAMETER:
+ default:
+ errno = EINVAL;
}
- else
- return -1; // TODO this is e.g. in case of CLOCK_MONOTONIC, we could use QPC then.
-} // mbg_clock_settime
+ return -1;
+} // mbg_clock_settime
bool force_legacy_gstaft;
@@ -163,7 +217,7 @@ void check_precise_time_api( void )
GSTAFT_FNC tmp_fnc;
HINSTANCE h = LoadLibrary( "kernel32.dll" );
- if ( h == NULL )
+ if ( h == NULL ) // TODO error msg
{
info = "Precise system time may not be supported; failed to get handle for kernel32.dll.";
goto out;
@@ -213,7 +267,7 @@ int snprint_utc_offs( char *s, size_t max_len, const char *info, long utc_offs )
size_t n = 0;
// utc_offs is in [s]
- char utc_offs_sign = ( utc_offs < 0 ) ? '-' : '+';
+ char utc_offs_sign = (char) ( ( utc_offs < 0 ) ? '-' : '+' );
ulong abs_utc_offs = labs( utc_offs );
ulong utc_offs_hours = abs_utc_offs / SECS_PER_HOUR;
ulong tmp = abs_utc_offs % SECS_PER_HOUR;
diff --git a/mbglib/common/timeutil.h b/mbglib/common/timeutil.h
index 517a725..c2390cd 100755
--- a/mbglib/common/timeutil.h
+++ b/mbglib/common/timeutil.h
@@ -1,7 +1,7 @@
/**************************************************************************
*
- * $Id: timeutil.h 1.7 2018/01/30 09:54:12 martin REL_M $
+ * $Id: timeutil.h 1.12 2019/08/07 09:05:44 martin REL_M $
*
* Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
*
@@ -10,7 +10,22 @@
*
* -----------------------------------------------------------------------
* $Log: timeutil.h $
- * Revision 1.7 2018/01/30 09:54:12 martin
+ * Revision 1.12 2019/08/07 09:05:44 martin
+ * New inline function mbg_gmtime64().
+ * Updated doxygen comments.
+ * Revision 1.11 2019/07/19 14:29:07 martin
+ * Define CLOCK_MONOTONIC, clock_gettime() and
+ * clock_settime() on Windows, if appropriate.
+ * Updated function prototypes.
+ * Revision 1.10 2019/02/11 09:49:46 martin
+ * Support the mingw build environment.
+ * Fixed build for targets that don't support 64 bit types.
+ * Revision 1.9 2018/12/11 15:36:40 martin
+ * cvt_to_time_t() now expects an int64_t type parameter.
+ * Revision 1.8 2018/11/29 15:32:04Z martin
+ * Moved some inline functions here.
+ * Include mbgtime.h.
+ * Revision 1.7 2018/01/30 09:54:12Z martin
* Updated function prototypes.
* Revision 1.6 2018/01/15 18:31:21Z martin
* Updated function prototypes.
@@ -34,6 +49,7 @@
#include <gpsdefs.h>
#include <mbgerror.h>
+#include <mbgtime.h>
#include <time.h>
#include <stddef.h>
@@ -54,13 +70,18 @@ extern "C" {
#endif
+// exclude for MinGW
#if defined( MBG_TGT_WIN32 ) || defined( MBG_TGT_DOS )
+ #if !defined( MBG_TGT_MINGW )
+ typedef int clockid_t;
+ #define clockid_t clockid_t
-typedef int clockid_t;
-#define clockid_t clockid_t
-
-#define CLOCK_REALTIME ( (clockid_t) 0 )
+ #define CLOCK_REALTIME ( (clockid_t) 0 )
+ #define CLOCK_MONOTONIC ( (clockid_t) 1 )
+ #define clock_gettime mbg_clock_gettime
+ #define clock_settime mbg_clock_settime
+ #endif
#endif
@@ -86,10 +107,18 @@ _ext GSTAFT_FNC gstaft_fnc
#endif
+
+#if !defined( MBG_TGT_MISSING_64_BIT_TYPES )
+ typedef int64_t mbg_time_t; // we try to always use 64 bit types
+#else
+ typedef time_t mbg_time_t; // fall back to the default
+#endif
+
+
static __mbg_inline
-time_t cvt_to_time_t( time_t t )
+time_t cvt_to_time_t( mbg_time_t t )
{
- // Eventually we can do some epoch check here.
+ // Eventually we can do some epoch check / conversion here.
return (time_t) t;
} // cvt_to_time_t
@@ -97,11 +126,38 @@ time_t cvt_to_time_t( time_t t )
static __mbg_inline
-int mbg_gmtime( struct tm *p_tm, const time_t *p_time )
+/**
+ * @brief A replacement function for POSIX gmtime().
+ *
+ * This function calls the original gmtime() function, but unlike gmtime()
+ * it expects the address of a struct tm variable which is only filled
+ * if gmtime() completed successfully. An appropriate return code
+ * is provided, indicating if the conversion succeeded, or not.
+ *
+ * The original gmtime() function returns a NULL pointer if the conversion
+ * fails, so a programs which just uses the pointer without checking it first
+ * may trap if the conversion fails. This can't happen with this function.
+ *
+ * This variant expects the address of a generic time_t variable. A time_t can
+ * be 32 bit or 64 bit wide, depending on the build environment and target system.
+ * Systems with 32 bit time_t will suffer from the Y2038 problem.
+ *
+ * There is also the ::mbg_gmtime64 variant, which expects a pointer to an
+ * ::MBG_TIME64_T variable to be converted, which is always 64 bits, even
+ * on 32 bit systems.
+ *
+ * @param[out] p_tm Address of a struct tm variable to take the conversion result.
+ * @param[in] p_t Address of a time_t variable to be converted.
+ *
+ * @return ::MBG_SUCCESS on success, else one of the @ref MBG_ERROR_CODES
+ *
+ * @see ::mbg_gmtime64
+ */
+int mbg_gmtime( struct tm *p_tm, const time_t *p_t )
{
- struct tm *p_tm_tmp = gmtime( p_time );
+ struct tm *p_tm_tmp = gmtime( p_t );
- if ( p_tm_tmp == NULL ) // conversion failed
+ if ( p_tm_tmp == NULL ) // Conversion failed.
return mbg_get_last_error( NULL );
*p_tm = *p_tm_tmp;
@@ -112,14 +168,188 @@ int mbg_gmtime( struct tm *p_tm, const time_t *p_time )
+static __mbg_inline
+/**
+ * @brief A replacement function for POSIX gmtime().
+ *
+ * This variant of ::mbg_gmtime expects a pointer to an ::MBG_TIME64_T variable
+ * to be converted, which is always 64 bits, even on 32 bit systems.
+ *
+ * Actually, the ::MBG_TIME64_T value is truncated to a native time_t, which can
+ * be 32 bit or 64 bit wide, depending on the build environment and target system.
+ * So this "just works" on systems with 64 bit time_t, but systems with 32 bit time_t
+ * will anyway suffer from the Y2038 problem, unless the gmtime() call is replaced
+ * by a function that does a 64 bit conversion even on systems with 32 bit time_t.
+ *
+ * @param[out] p_tm Address of a struct tm variable to take the conversion result.
+ * @param[in] p_t Address of an ::MBG_TIME64_T variable to be converted.
+ *
+ * @return ::MBG_SUCCESS on success, else one of the @ref MBG_ERROR_CODES
+ *
+ * @see ::mbg_gmtime
+ */
+int mbg_gmtime64( struct tm *p_tm, const MBG_TIME64_T *p_t )
+{
+ struct tm *p_tm_tmp;
+ time_t t = (time_t) (*p_t);
+
+ p_tm_tmp = gmtime( &t );
+
+ if ( p_tm_tmp == NULL ) // Conversion failed.
+ return mbg_get_last_error( NULL );
+
+ *p_tm = *p_tm_tmp;
+
+ return MBG_SUCCESS;
+
+} // mbg_gmtime64
+
+
+
+#if !defined( MBG_TGT_MISSING_64_BIT_TYPES )
+
+static __mbg_inline /*HDR*/
+double ntp_tstamp_to_double( const NTP_TSTAMP *t )
+{
+ return (double) t->seconds + ( ( (double) t->fractions ) / NTP_FRAC_PER_SEC );
+
+} // ntp_tstamp_to_double
+
+
+
+static __mbg_inline /*HDR*/
+ulong ntp_frac_to_nsec( uint32_t frac )
+{
+ uint64_t tmp = ( (uint64_t) frac * NSEC_PER_SEC ) / NTP_FRAC_PER_SEC;
+
+#if 0 // TODO Currently not supported
+ if ( tmp >= NSEC_PER_SEC )
+ mbglog( LOG_WARNING, "Range overflow in ntp_frac_to_nsec: 0x%X -> %Lu",
+ frac, tmp );
+#endif
+
+ return (ulong) tmp;
+
+} // ntp_frac_to_nsec
+
+
+
+static __mbg_inline /*HDR*/
+uint32_t nsec_to_ntp_frac( ulong nsec )
+{
+ uint64_t tmp;
+
+ tmp = ( (uint64_t) nsec * NTP_FRAC_PER_SEC ) / NSEC_PER_SEC;
+
+#if 0 // TODO Currently not supported
+ if ( tmp >= NTP_FRAC_PER_SEC )
+ mbglog( LOG_WARNING, "Range overflow in nsec_to_ntp_frac: %lu -> 0x%LX",
+ (ulong) nsec, tmp );
+#endif
+
+ return (uint32_t) tmp;
+
+} // nsec_to_ntp_frac
+
+
+
+static __mbg_inline /*HDR*/
+void timespec_to_ntp_tstamp( NTP_TSTAMP *t_ntp, const struct timespec *t_ts )
+{
+ t_ntp->seconds = (uint32_t) t_ts->tv_sec + NTP_SEC_BIAS;
+ t_ntp->fractions = nsec_to_ntp_frac( t_ts->tv_nsec );
+
+} // timespec_to_ntp_tstamp
+
+
+
+static __mbg_inline /*HDR*/
+void ntp_tstamp_to_timespec( struct timespec *t_ts, const NTP_TSTAMP *t_ntp )
+{
+ t_ts->tv_sec = t_ntp->seconds - NTP_SEC_BIAS;
+ t_ts->tv_nsec = ntp_frac_to_nsec( t_ntp->fractions );
+
+} // ntp_tstamp_to_timespec
+
+#endif // !defined( MBG_TGT_MISSING_64_BIT_TYPES )
+
+
+
+static __mbg_inline /*HDR*/
+int timespec_is_set( const struct timespec *p )
+{
+ return p->tv_sec != 0 || p->tv_nsec != 0;
+
+} // timespec_is_set
+
+
+
+//
+// compute the difference of two timespec variables
+//
+static __mbg_inline /*HDR*/
+double delta_timespec_d_s( const struct timespec *ts,
+ const struct timespec *ts_ref )
+{
+ return ( (double) ts->tv_sec - (double) ts_ref->tv_sec )
+ + ( (double) ts->tv_nsec - (double) ts_ref->tv_nsec ) / NSEC_PER_SEC;
+
+} // delta_timespec_d_s
+
+
+
+#if !defined( MBG_TGT_MISSING_64_BIT_TYPES )
+
+static __mbg_inline /*HDR*/
+int64_t delta_timespec_ll_ns( const struct timespec *ts,
+ const struct timespec *ts_ref )
+{
+ int64_t tmp = ts->tv_sec - ts_ref->tv_sec;
+ tmp = ( tmp * NSEC_PER_SEC ) + ( ts->tv_nsec - ts_ref->tv_nsec );
+
+ return tmp;
+
+} // delta_timespec_ll_ns
+
+#endif // !defined( MBG_TGT_MISSING_64_BIT_TYPES )
+
+
+
/* ----- function prototypes begin ----- */
/* This section was generated automatically */
/* by MAKEHDR, do not remove the comments. */
int snprint_gmtime_error( char *s, size_t max_len, int mbg_errno, time_t t, const char *calling_fnc ) ;
+ /**
+ * @brief A Windows implementation for POSIX clock_gettime().
+ *
+ * @param[in] clock_id Identifier of a specific clock, e.g.
+ * CLOCK_REALTIME, CLOCK_MONOTONIC, etc.
+ * @param[out] tp Address of a struct timespec to take up
+ * the current time.
+ *
+ * @return 0 on success, -1 on error, just like the POSIX function.
+ * In case of an error the POSIX errno variable is set
+ * appropriately.
+ */
int mbg_clock_gettime( clockid_t clock_id, struct timespec *tp ) ;
+
+ /**
+ * @brief A Windows implementation for POSIX clock_settime().
+ *
+ * @param[in] clock_id Identifier of a specific clock, i.e.
+ * CLOCK_REALTIME which is the only clock
+ * supported by this call.
+ * @param[in] tp Pointer to a struct timespec providing
+ * the time to be set.
+ *
+ * @return 0 on success, -1 on error, just like the POSIX function.
+ * In case of an error the POSIX errno variable is set
+ * appropriately.
+ */
int mbg_clock_settime( clockid_t clock_id, const struct timespec *tp ) ;
+
void check_precise_time_api( void ) ;
/**
* @brief Print a UTC offset into a string
diff --git a/mbglib/common/words.h b/mbglib/common/words.h
index d947bed..cf6dee7 100755
--- a/mbglib/common/words.h
+++ b/mbglib/common/words.h
@@ -1,7 +1,7 @@
/**************************************************************************
*
- * $Id: words.h 1.44 2018/06/25 09:21:02 martin REL_M $
+ * $Id: words.h 1.48 2019/06/17 08:38:59 thomas-b REL_M $
*
* Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
*
@@ -10,6 +10,14 @@
*
* -----------------------------------------------------------------------
* $Log: words.h $
+ * Revision 1.48 2019/06/17 08:38:59 thomas-b
+ * Renamed structs according to Meinberg naming convention
+ * Revision 1.47 2019/06/06 12:17:14 thomas-b
+ * Added several struct names to allow forward declaration
+ * Revision 1.46 2019/02/07 14:38:56 martin
+ * Removed a duplicate definition.
+ * Revision 1.45 2018/11/22 16:38:15 martin
+ * Removed inclusion of mbg_no_tgt.h which is obsolete here.
* Revision 1.44 2018/06/25 09:21:02 martin
* Support MBG_TGT_NO_TGT.
* Improved STRINGIFY() macro.
@@ -147,11 +155,7 @@
#if !_IS_MBG_FIRMWARE
- #ifdef MBG_TGT_NO_TGT
- #include <mbg_no_tgt.h>
- #else
- #include <mbg_tgt.h>
- #endif
+ #include <mbg_tgt.h>
#else
#if defined( __ARMCC_VERSION ) // Keil RealView Compiler for ARM
#define __mbg_inline __inline
@@ -572,7 +576,7 @@ typedef struct
* @see ::_nano_time_zero
* @see ::NANO_TIME_64
*/
-typedef struct
+typedef struct nano_time_s
{
// ATTENTION:
// This structure is and has has been used in public API calls for a long time,
@@ -694,10 +698,6 @@ do \
*/
#define STRINGIFY(x) XSTRINGIFY(x)
-// The XSTRINGIFY() macro is just a helper macro to implement STRINGIFY()
-// and should not be used alone.
-#define XSTRINGIFY(x) #x
-
/* End of header body */
diff --git a/ntptest.c b/ntptest.c
index ea63f4f..2a18e2f 100755
--- a/ntptest.c
+++ b/ntptest.c
@@ -1,7 +1,7 @@
/**************************************************************************
*
- * $Id: ntptest.c 1.14 2018/07/31 16:14:14 martin REL_M $
+ * $Id: ntptest.c 1.15 2019/09/04 07:42:17 martin REL_M $
*
* Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
*
@@ -12,6 +12,9 @@
*
* -----------------------------------------------------------------------
* $Log: ntptest.c $
+ * Revision 1.15 2019/09/04 07:42:17 martin
+ * Return specific exit codes in case of error, and
+ * list the valid exit codes in the usage information.
* Revision 1.14 2018/07/31 16:14:14 martin
* Changed version number to 1.10.
* Support symmetric MD5 keys.
@@ -87,10 +90,42 @@
static const char program_name[] = "ntptest";
-static const char program_version[] = "v1.10";
-static const char program_copyright[] = "(c) Meinberg 2014-2018";
+static const char program_version[] = "v1.11";
+static const char program_copyright[] = "(c) Meinberg 2014-2019";
static const char program_contact[] = "contact: <martin.burnicki@meinberg.de>";
+enum EXIT_CODES
+{
+ EXIT_OK, // Success (EXIT_SUCCESS is a predefined symbol)
+ EXIT_FAIL_USAGE, // Usage printed, invalid parameter?
+ EXIT_FAILED_GENERIC, // General failure, e.g. insufficient memory.
+ EXIT_FAIL_DNS, // Host name lookup failed.
+ EXIT_FAIL_SOCK_ID, // Failed to open UDP socket.
+ EXIT_FAIL_SEND, // Failed to send UDP packet.
+ EXIT_FAIL_RECEIVE, // Failed to receive UDP packet.
+ EXIT_FAIL_ENCRYPT, // Failed to encrypt a packet.
+ EXIT_FAIL_DECRYPT, // Failed to decrypt an encrypted packet.
+ EXIT_FAIL_CRYPT_NACK, // Failed because received a crypto NACK.
+ N_EXIT_CODES
+};
+
+#define EXIT_CODE_INFO_STRS \
+{ \
+ "Success", /* EXIT_OK */ \
+ "Usage printed, e.g. invalid parameter.", /* EXIT_FAIL_USAGE */ \
+ "General failure, e.g. insufficient memory.", /* EXIT_FAILED_GENERIC */ \
+ "Host name lookup failed.", /* EXIT_FAIL_DNS */ \
+ "Failed to open UDP socket.", /* EXIT_FAIL_SOCK_ID */ \
+ "Failed to send UDP packet.", /* EXIT_FAIL_SEND */ \
+ "Failed to receive UDP packet.", /* EXIT_FAIL_RECEIVE */ \
+ "Failed to encrypt a packet.", /* EXIT_FAIL_ENCRYPT */ \
+ "Failed to decrypt an encrypted packet.", /* EXIT_FAIL_DECRYPT */ \
+ "Received a crypto NACK." /* EXIT_FAIL_CRYPT_NACK */ \
+}
+
+
+static int exit_code = EXIT_OK;
+
#if defined( MBG_TGT_WIN32 )
#define __const__ const
@@ -188,7 +223,7 @@ void do_ntp_queries( void )
mbglog( LOG_ERR, "Failed to open UDP socket in %s: %s", __func__,
mbg_strerror( mbg_get_last_socket_error( NULL ) ) );
- exit( 1 );
+ exit( EXIT_FAIL_SOCK_ID );
}
@@ -213,7 +248,10 @@ void do_ntp_queries( void )
int auth_code = mbg_ntp_auth_encrypt( &key_cache, &req_info, md5_key_id );
if ( auth_code != MBG_NTP_AUTH_OK )
+ {
+ exit_code = EXIT_FAIL_ENCRYPT;
mbglog( LOG_WARNING, "Failed to encrypt request packet with key %i", md5_key_id );
+ }
}
#endif
@@ -224,7 +262,7 @@ void do_ntp_queries( void )
{
mbglog( LOG_ERR, "Failed to send UDP packet in %s: %s", __func__,
mbg_strerror( mbg_get_last_socket_error( NULL ) ) );
- exit( 1 );
+ exit( EXIT_FAIL_SEND );
}
glb_query_stats.requests++;
@@ -272,7 +310,7 @@ void do_ntp_queries( void )
{
mbglog( LOG_ERR, "Failed to receive from UDP socket: %s",
mbg_strerror( mbg_get_last_socket_error( NULL ) ) );
- goto cont;
+ exit( EXIT_FAIL_RECEIVE );
}
// Use the receive time stamp taken earlier.
@@ -281,6 +319,12 @@ void do_ntp_queries( void )
// Authentication must be checked *before* byte order is adjusted
#if defined( _USE_MBG_NTP_AUTH )
rslt.auth_code = mbg_ntp_auth_decrypt( &key_cache, &resp_info );
+
+ if ( rslt.auth_code == MBG_NTP_AUTH_NACK )
+ exit_code = EXIT_FAIL_CRYPT_NACK;
+ else
+ if ( rslt.auth_code == MBG_NTP_AUTH_FAIL )
+ exit_code = EXIT_FAIL_DECRYPT;
#else
rslt.auth_code = MBG_NTP_AUTH_NONE;
#endif
@@ -365,6 +409,9 @@ cont:
static /*HDR*/
void usage( void )
{
+ const char *exit_code_str[N_EXIT_CODES] = EXIT_CODE_INFO_STRS;
+ int i;
+
printf( "\n" );
printf( "Usage: %s [[OPTION]...] <host>\n", program_name );
printf( "\n" );
@@ -396,6 +443,13 @@ void usage( void )
printf( " -?, -h print this usage information\n"
"\n" );
+ printf( "Program exit codes:\n" );
+
+ for ( i = 0; i < N_EXIT_CODES; i++ )
+ printf( " %i: %s\n", i, exit_code_str[i] );
+
+ printf( "\n" );
+
} // usage
@@ -505,6 +559,10 @@ int main( int argc, char *argv[] )
fprintf( stderr, "\n%s %s, %s, %s\n\n", program_name, program_version,
program_copyright, program_contact );
+ #if 0 && defined( _MSC_VER )
+ printf( "built with _MSC_VER %i, _MSC_FULL_VER %i\n", _MSC_VER, _MSC_FULL_VER );
+ #endif
+
check_options( argc, argv );
if ( !run_continuously )
@@ -524,7 +582,7 @@ int main( int argc, char *argv[] )
if ( must_print_usage )
{
usage();
- return 1;
+ exit( EXIT_FAIL_USAGE );
}
@@ -553,7 +611,7 @@ int main( int argc, char *argv[] )
{
int rc = mbg_get_gethostbyname_error( NULL );
mbglog( LOG_ERR, "Unknown host %s: %s", tgt_host, mbg_strerror( rc ) );
- exit( 2 );
+ exit( EXIT_FAIL_DNS );
}
// set up host info for sending
@@ -569,8 +627,15 @@ int main( int argc, char *argv[] )
init_ntp_req_packet( &default_ntp_req_packet, req_mode, prot_version );
#if defined( _USE_MBG_NTP_AUTH )
+ {
// Setup authentication support
- mbg_ntp_auth_init();
+ int rc = mbg_ntp_auth_init();
+
+ if ( mbg_rc_is_error( rc ) )
+ {
+ exit_code = EXIT_FAILED_GENERIC;
+ goto out;
+ }
if ( md5_key_id ) // A key ID has been specified on the command line
{
@@ -587,12 +652,13 @@ int main( int argc, char *argv[] )
if ( md5_key_id )
mbg_ntp_auth_test( md5_key_id );
#endif
-
+ }
#endif // defined( _USE_MBG_NTP_AUTH )
printf( "Host %s%s\n", tgt_host, run_continuously ? ", running continuously" : "" );
do_ntp_queries();
- return 0;
+out:
+ return exit_code;
}