diff options
author | Martin Burnicki <martin.burnicki@meinberg.de> | 2019-09-04 12:00:00 +0200 |
---|---|---|
committer | Martin Burnicki <martin.burnicki@meinberg.de> | 2019-09-04 12:00:00 +0200 |
commit | a859d75c21202b0237d6e9bffe0512656d435ff5 (patch) | |
tree | 9d5a9ffd36ecdbbf3f26851a90c4a67cbef2ef3d | |
parent | 54ad38deb28269fc8b74173a4f513adab13dd503 (diff) | |
download | ntptest-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-x | README | 40 | ||||
-rwxr-xr-x | mbglib/common/mbg_ntp_auth.c | 187 | ||||
-rwxr-xr-x | mbglib/common/mbg_ntp_auth.h | 6 | ||||
-rwxr-xr-x | mbglib/common/mbg_ntp_test_util.h | 99 | ||||
-rwxr-xr-x | mbglib/common/mbg_tgt.h | 160 | ||||
-rwxr-xr-x | mbglib/common/mbgerror.c | 355 | ||||
-rwxr-xr-x | mbglib/common/mbgerror.h | 303 | ||||
-rwxr-xr-x | mbglib/common/mbgtime.h | 187 | ||||
-rwxr-xr-x | mbglib/common/str_util.c | 63 | ||||
-rwxr-xr-x | mbglib/common/str_util.h | 115 | ||||
-rwxr-xr-x | mbglib/common/timeutil.c | 186 | ||||
-rwxr-xr-x | mbglib/common/timeutil.h | 252 | ||||
-rwxr-xr-x | mbglib/common/words.h | 22 | ||||
-rwxr-xr-x | ntptest.c | 88 |
14 files changed, 1518 insertions, 545 deletions
@@ -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 */ @@ -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; } |