diff --git a/configure.ac b/configure.ac index bbb655b0b..da6703ccb 100644 --- a/configure.ac +++ b/configure.ac @@ -104,7 +104,10 @@ AC_CHECK_DECLS([__CYGWIN__]) AC_SEARCH_LIBS(accept, network socket) # Checks for library functions. -AC_CHECK_FUNCS([accept4 getaddrinfo gettimeofday inet_pton inet_ntop select socket strerror strlcpy]) +AC_CHECK_FUNCS([accept4 getaddrinfo gettimeofday inet_pton inet_ntop select socket strerror_r strlcpy]) + +# Check which flavour of strerror_r is available on the target +AC_FUNC_STRERROR_R # Required for MinGW with GCC v4.8.1 on Win7 AC_DEFINE(WINVER, 0x0501, _) diff --git a/docs/index.md b/docs/index.md index 89fcbbba8..6e1909829 100644 --- a/docs/index.md +++ b/docs/index.md @@ -245,9 +245,11 @@ shall return either a NULL value (if returning a pointer) or a negative value (if returning an integer), and the actual error code shall be stored in the `errno` variable. -The *modbus_strerror()* function is provided to translate libmodbus-specific -error codes into error message strings; for details refer to -[modbus_strerror](modbus_strerror.md). +The *modbus_strerror_r()* function is provided to translate libmodbus-specific +error codes into error message strings; for details refer to: + +- [modbus_strerror_r](modbus_strerror_r.md) +- [modbus_strerror](modbus_strerror.md) **deprecated** ## Miscellaneous diff --git a/docs/modbus_close.md b/docs/modbus_close.md index 05cc080eb..02c905799 100644 --- a/docs/modbus_close.md +++ b/docs/modbus_close.md @@ -26,7 +26,9 @@ modbus_t *ctx; ctx = modbus_new_tcp("127.0.0.1", 502); if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "Connection failed: %s\n", + modbus_strerror_r(errno), buf, sizeof(buf)); modbus_free(ctx); return -1; } diff --git a/docs/modbus_connect.md b/docs/modbus_connect.md index 541d9d982..14ccf1a65 100644 --- a/docs/modbus_connect.md +++ b/docs/modbus_connect.md @@ -29,7 +29,9 @@ modbus_t *ctx; ctx = modbus_new_tcp("127.0.0.1", 502); if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "Connection failed: %s\n", + modbus_strerror_r(errno), buf, sizeof(buf)); modbus_free(ctx); return -1; } diff --git a/docs/modbus_mapping_new.md b/docs/modbus_mapping_new.md index bedac63fd..3091cbfa0 100644 --- a/docs/modbus_mapping_new.md +++ b/docs/modbus_mapping_new.md @@ -45,9 +45,10 @@ mb_mapping = modbus_mapping_new( INPUT_REGISTERS_ADDRESS + INPUT_REGISTERS_NB ); if (mb_mapping == NULL) { + char buf[128]; fprintf( stderr, "Failed to allocate the mapping: %s\n", - modbus_strerror(errno) + modbus_strerror_r(errno, buf, sizeof(buf)) ); modbus_free(ctx); return -1; diff --git a/docs/modbus_mapping_new_start_address.md b/docs/modbus_mapping_new_start_address.md index c72cd3d49..8cf5d8f91 100644 --- a/docs/modbus_mapping_new_start_address.md +++ b/docs/modbus_mapping_new_start_address.md @@ -70,9 +70,10 @@ mb_mapping = modbus_mapping_new_start_address( INPUT_REGISTERS_ADDRESS, INPUT_REGISTERS_NB ); if (mb_mapping == NULL) { + char buf[128]; fprintf( stderr, "Failed to allocate the mapping: %s\n", - modbus_strerror(errno) + modbus_strerror_r(errno, buf, sizeof(buf)) ); modbus_free(ctx); return -1; diff --git a/docs/modbus_new_rtu.md b/docs/modbus_new_rtu.md index 15dac8e3b..e6375479b 100644 --- a/docs/modbus_new_rtu.md +++ b/docs/modbus_new_rtu.md @@ -86,7 +86,9 @@ if (ctx == NULL) { } if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "Connection failed: %s\n", + modbus_strerror_r(errno, buf, sizeof(buf))); modbus_free(ctx); return -1; } diff --git a/docs/modbus_new_tcp.md b/docs/modbus_new_tcp.md index 1bb2ae944..74b72eb99 100644 --- a/docs/modbus_new_tcp.md +++ b/docs/modbus_new_tcp.md @@ -48,7 +48,9 @@ if (ctx == NULL) { } if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "Connection failed: %s\n", + modbus_strerror_r(errno, buf, sizeof(buf))); modbus_free(ctx); return -1; } diff --git a/docs/modbus_new_tcp_pi.md b/docs/modbus_new_tcp_pi.md index fa0b5c0ca..b0e9282bc 100644 --- a/docs/modbus_new_tcp_pi.md +++ b/docs/modbus_new_tcp_pi.md @@ -49,7 +49,9 @@ if (ctx == NULL) { } if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "Connection failed: %s\n", + modbus_strerror_r(errno, buf, sizeof(buf))); modbus_free(ctx); return -1; } diff --git a/docs/modbus_read_registers.md b/docs/modbus_read_registers.md index 8f853cc7c..28cdcc5a5 100644 --- a/docs/modbus_read_registers.md +++ b/docs/modbus_read_registers.md @@ -40,14 +40,17 @@ int i; ctx = modbus_new_tcp("127.0.0.1", 1502); if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "Connection failed: %s\n", + modbus_strerror_r(errno, buf, sizeof(buf))); modbus_free(ctx); return -1; } rc = modbus_read_registers(ctx, 0, 10, tab_reg); if (rc == -1) { - fprintf(stderr, "%s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "%s\n", modbus_strerror_r(errno, buf, sizeof(buf)); return -1; } diff --git a/docs/modbus_rtu_set_rts.md b/docs/modbus_rtu_set_rts.md index 5a33d1c6a..0d867af0e 100644 --- a/docs/modbus_rtu_set_rts.md +++ b/docs/modbus_rtu_set_rts.md @@ -49,14 +49,17 @@ modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485); modbus_rtu_set_rts(ctx, MODBUS_RTU_RTS_UP); if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "Connection failed: %s\n", + modbus_strerror_r(errno, buf, sizeof(buf))); modbus_free(ctx); return -1; } rc = modbus_read_registers(ctx, 0, 7, tab_reg); if (rc == -1) { - fprintf(stderr, "%s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "%s\n", modbus_strerror_r(errno, buf, sizeof(buf))); return -1; } diff --git a/docs/modbus_send_raw_request.md b/docs/modbus_send_raw_request.md index 66ceceaa2..975f3b420 100644 --- a/docs/modbus_send_raw_request.md +++ b/docs/modbus_send_raw_request.md @@ -40,7 +40,9 @@ uint8_t rsp[MODBUS_TCP_MAX_ADU_LENGTH]; ctx = modbus_new_tcp("127.0.0.1", 1502); if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "Connection failed: %s\n", + modbus_strerror_r(errno, buf, sizeof(buf))); modbus_free(ctx); return -1; } diff --git a/docs/modbus_set_slave.md b/docs/modbus_set_slave.md index efdeea386..bfcf9e3f0 100644 --- a/docs/modbus_set_slave.md +++ b/docs/modbus_set_slave.md @@ -66,7 +66,9 @@ if (rc == -1) { } if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "Connection failed: %s\n", + modbus_strerror_r(errno, buf, sizeof(buf))); modbus_free(ctx); return -1; } diff --git a/docs/modbus_strerror.md b/docs/modbus_strerror.md index 9db8b799e..42ab34cc3 100644 --- a/docs/modbus_strerror.md +++ b/docs/modbus_strerror.md @@ -10,6 +10,9 @@ modbus_strerror - return the error message const char *modbus_strerror(int errnum); ``` +Warning, this function is *deprecated* since libmodbus **tbd** and has been +replaced by *modbus_strerror_r()*. + ## Description The *modbus_strerror()* function shall return a pointer to an error message diff --git a/docs/modbus_strerror_r.md b/docs/modbus_strerror_r.md new file mode 100644 index 000000000..ae10f5bf7 --- /dev/null +++ b/docs/modbus_strerror_r.md @@ -0,0 +1,50 @@ +# modbus_strerror_r + +## Name + +modbus_strerror_r - return the error message + +## Synopsis + +```c +const char *modbus_strerro_r(int errnum, char buf[.buflen], size_t buflen); +``` + +## Description + +The *modbus_strerror_r()* function is a reentrant replacement for +*modbus_strerror()*. + +The *modbus_strerror_r()* function returns a pointer to a string containing +the error message. This may either be a pointer to a string that the +function stores in *buf*, or a pointer to some (immutable) static string +(in which case *buf* is unused). If the function stores a string in *buf*, +then at most *buflen* bytes are stored. The string may be truncated if +*buflen* is tool small and errnum is unknown. The string always includes a +terminating null byte ('\0'). + +As libmodbus defines additional error numbers over and above those defined +by the operating system, applications should use *modbus_strerror_r()* in +preference to the standard *strerror_r()* function. + +## Return value + +The *modbus_strerror()* function shall return a pointer to an error message +string. + +## Errors + +No errors are defined. + +## Example + +Display an error message when a Modbus connection cannot be established + +```c +if (modbus_connect(ctx) == -1) { + char buf[128] + fprintf(stderr, "Connection failed: %s\n", + modbus_strerror_r(errno, buf, sizeof(buf))); + abort(); +} +``` diff --git a/src/modbus-rtu.c b/src/modbus-rtu.c index b7749230c..99137c256 100644 --- a/src/modbus-rtu.c +++ b/src/modbus-rtu.c @@ -12,6 +12,7 @@ #ifndef _MSC_VER #include #endif +#include "modbus.h" #include "modbus-private.h" #include @@ -649,10 +650,11 @@ static int _modbus_rtu_connect(modbus_t *ctx) ctx->s = open(ctx_rtu->device, flags); if (ctx->s < 0) { if (ctx->debug) { + char buf[128]; fprintf(stderr, "ERROR Can't open the device %s (%s)\n", ctx_rtu->device, - strerror(errno)); + modbus_strerror_r(errno, buf, sizeof(buf))); } return -1; } diff --git a/src/modbus.c b/src/modbus.c index 0360d5ccd..622650e37 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -41,7 +41,9 @@ typedef enum { _STEP_DATA } _step_t; -const char *modbus_strerror(int errnum) +// returns NULL if the error belongs to the operating system, otherwise a +// pointer to the error message. +static const char *libmodbus_specific_error_text(int errnum) { switch (errnum) { case EMBXILFUN: @@ -75,14 +77,82 @@ const char *modbus_strerror(int errnum) case EMBBADSLAVE: return "Response not from requested slave"; default: - return strerror(errnum); + return NULL; } } +const char *modbus_strerror(int errnum) +{ + const char *s = libmodbus_specific_error_text(errnum); + + if (s) { + return s; + } + return strerror(errnum); +} + +const char *modbus_strerror_r(int errnum, char *buf, size_t buflen) +{ + const char *s = libmodbus_specific_error_text(errnum); + + if (s) { + return s; + } + +#if HAVE_STRERROR_R + /* + * The glibc provides 2 different versions of strerror_r. + * + * XSI-compliant version: + * int strerror_r(int errnum, char *buf, size_t buflen); + * + * GNU-specific version: + * char *strerror_r(int errnum, char *buf, size_t buflen); + * + * See "man 3 strerror_r" for details. + */ +#if STRERROR_R_CHAR_P + /* GNU-specific strerror_r() */ + /* + * The statement below will hopefully result in a compiler warning if + * the XSI-compliant implementation is provided instead of the GNU-specific + * version (although detected by configure). + */ + return strerror_r(errnum, buf, buflen); +#else + /* XSI-complaint strerror_r() */ + /* + * Neither POSIX nor the Linux man page specify the buffer content when + * strerror_r() completes unsuccessfully. Therefore, we initialize the + * buffer with '\0' before we call strerror_r(), and manually terminate + * the string in case strerror_r() signals an error. + */ + memset(buf, 0, buflen); + + /* + * The statement below will hopefully result in a compiler warning if + * the GNU-specific version is provided instead of the XSI-compliant + * implementation. + */ + int res = strerror_r(errnum, buf, buflen); + if (res != 0) + buf[buflen - 1] = 0; + + return buf; + +#endif /* STRERROR_R_CHAR_P */ +#else + /* fallback in case strerror_r() is not available */ + snprintf(buf, buflen, "error %d", errnum); + return buf; +#endif /* HAVE_STRERROR_R */ +} + void _error_print(modbus_t *ctx, const char *context) { if (ctx->debug) { - fprintf(stderr, "ERROR %s", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "ERROR %s", modbus_strerror_r(errno, buf, sizeof(buf))); if (context != NULL) { fprintf(stderr, ": %s\n", context); } else { diff --git a/src/modbus.h b/src/modbus.h index 55ef08a0d..07abbeeba 100644 --- a/src/modbus.h +++ b/src/modbus.h @@ -216,6 +216,7 @@ MODBUS_API int modbus_flush(modbus_t *ctx); MODBUS_API int modbus_set_debug(modbus_t *ctx, int flag); MODBUS_API const char *modbus_strerror(int errnum); +MODBUS_API const char *modbus_strerror_r(int errnum, char *buf, size_t buflen); MODBUS_API int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest); MODBUS_API int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest); diff --git a/tests/bandwidth-client.c b/tests/bandwidth-client.c index 842462b59..394c348e0 100644 --- a/tests/bandwidth-client.c +++ b/tests/bandwidth-client.c @@ -76,7 +76,9 @@ int main(int argc, char *argv[]) modbus_set_slave(ctx, 1); } if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "Connection failed: %s\n", + modbus_strerror_r(errno, buf, sizeof(buf))); modbus_free(ctx); return -1; } @@ -96,7 +98,9 @@ int main(int argc, char *argv[]) for (i = 0; i < n_loop; i++) { rc = modbus_read_bits(ctx, 0, nb_points, tab_bit); if (rc == -1) { - fprintf(stderr, "%s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "%s\n", + modbus_strerror_r(errno, buf, sizeof(buf))); return -1; } } @@ -133,7 +137,8 @@ int main(int argc, char *argv[]) for (i = 0; i < n_loop; i++) { rc = modbus_read_registers(ctx, 0, nb_points, tab_reg); if (rc == -1) { - fprintf(stderr, "%s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "%s\n", modbus_strerror_r(errno, buf, sizeof(buf))); return -1; } } @@ -171,7 +176,8 @@ int main(int argc, char *argv[]) rc = modbus_write_and_read_registers( ctx, 0, nb_points, tab_reg, 0, nb_points, tab_reg); if (rc == -1) { - fprintf(stderr, "%s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "%s\n", modbus_strerror_r(errno, buf, sizeof(buf))); return -1; } } diff --git a/tests/bandwidth-server-many-up.c b/tests/bandwidth-server-many-up.c index f6eb7d7fb..da4bbc0cf 100644 --- a/tests/bandwidth-server-many-up.c +++ b/tests/bandwidth-server-many-up.c @@ -55,7 +55,9 @@ int main(void) mb_mapping = modbus_mapping_new(MODBUS_MAX_READ_BITS, 0, MODBUS_MAX_READ_REGISTERS, 0); if (mb_mapping == NULL) { - fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "Failed to allocate the mapping: %s\n", + modbus_strerror_r(errno, buf, sizeof(buf))); modbus_free(ctx); return -1; } diff --git a/tests/bandwidth-server-one.c b/tests/bandwidth-server-one.c index c7ef1ab20..9edd4cedb 100644 --- a/tests/bandwidth-server-one.c +++ b/tests/bandwidth-server-one.c @@ -30,6 +30,7 @@ int main(int argc, char *argv[]) modbus_mapping_t *mb_mapping = NULL; int rc; int use_backend; + char errbuf[128]; /* TCP */ if (argc > 1) { @@ -61,7 +62,8 @@ int main(int argc, char *argv[]) mb_mapping = modbus_mapping_new(MODBUS_MAX_READ_BITS, 0, MODBUS_MAX_READ_REGISTERS, 0); if (mb_mapping == NULL) { - fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); + fprintf(stderr, "Failed to allocate the mapping: %s\n", + modbus_strerror_r(errno, errbuf, sizeof(errbuf))); modbus_free(ctx); return -1; } @@ -78,7 +80,8 @@ int main(int argc, char *argv[]) } } - printf("Quit the loop: %s\n", modbus_strerror(errno)); + printf("Quit the loop: %s\n", + modbus_strerror_r(errno, errbuf, sizeof(errbuf))); modbus_mapping_free(mb_mapping); if (s != -1) { diff --git a/tests/random-test-client.c b/tests/random-test-client.c index c2ccf63fd..805a380c7 100644 --- a/tests/random-test-client.c +++ b/tests/random-test-client.c @@ -60,7 +60,9 @@ int main(void) modbus_set_debug(ctx, TRUE); if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "Connection failed: %s\n", + modbus_strerror_r(errno, buf, sizeof(buf))); modbus_free(ctx); return -1; } diff --git a/tests/random-test-server.c b/tests/random-test-server.c index a064fa8a3..09a9c4665 100644 --- a/tests/random-test-server.c +++ b/tests/random-test-server.c @@ -18,13 +18,15 @@ int main(void) int s = -1; modbus_t *ctx; modbus_mapping_t *mb_mapping; + char errbuf[128]; ctx = modbus_new_tcp("127.0.0.1", 1502); /* modbus_set_debug(ctx, TRUE); */ mb_mapping = modbus_mapping_new(500, 500, 500, 500); if (mb_mapping == NULL) { - fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); + fprintf(stderr, "Failed to allocate the mapping: %s\n", + modbus_strerror_r(errno, errbuf, sizeof(errbuf))); modbus_free(ctx); return -1; } @@ -46,7 +48,8 @@ int main(void) } } - printf("Quit the loop: %s\n", modbus_strerror(errno)); + printf("Quit the loop: %s\n", + modbus_strerror_r(errno, errbuf, sizeof(errbuf))); if (s != -1) { close(s); diff --git a/tests/unit-test-client.c b/tests/unit-test-client.c index 6a25d3622..5086c2ad5 100644 --- a/tests/unit-test-client.c +++ b/tests/unit-test-client.c @@ -138,7 +138,9 @@ int main(int argc, char *argv[]) modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec); if (modbus_connect(ctx) == -1) { - fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + char buf[128]; + fprintf(stderr, "Connection failed: %s\n", + modbus_strerror_r(errno, buf, sizeof(buf))); modbus_free(ctx); return -1; } diff --git a/tests/unit-test-server.c b/tests/unit-test-server.c index 561d64d01..0124b44a3 100644 --- a/tests/unit-test-server.c +++ b/tests/unit-test-server.c @@ -43,6 +43,7 @@ int main(int argc, char *argv[]) uint8_t *query; int header_length; char *ip_or_device; + char errbuf[128]; if (argc > 1) { if (strcmp(argv[1], "tcp") == 0) { @@ -105,7 +106,8 @@ int main(int argc, char *argv[]) UT_INPUT_REGISTERS_ADDRESS, UT_INPUT_REGISTERS_NB); if (mb_mapping == NULL) { - fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); + fprintf(stderr, "Failed to allocate the mapping: %s\n", + modbus_strerror_r(errno, errbuf, sizeof(errbuf))); modbus_free(ctx); return -1; } @@ -131,7 +133,8 @@ int main(int argc, char *argv[]) } else { rc = modbus_connect(ctx); if (rc == -1) { - fprintf(stderr, "Unable to connect %s\n", modbus_strerror(errno)); + fprintf(stderr, "Unable to connect %s\n", + modbus_strerror_r(errno, errbuf, sizeof(errbuf))); modbus_free(ctx); return -1; } @@ -212,7 +215,8 @@ int main(int argc, char *argv[]) } } - printf("Quit the loop: %s\n", modbus_strerror(errno)); + printf("Quit the loop: %s\n", + modbus_strerror_r(errno, errbuf, sizeof(errbuf))); if (use_backend == TCP) { if (s != -1) {