feat: switch iperf3 to JNI with 3.19 source
- Switch from bundled iperf3 binary (Process.start) to JNI compilation - iperf3 3.21 source API was incompatible (start_time, iperf_strerror, etc.) - iperf3 3.19 source is proven to work (used by CellularLab) - CMakeLists.txt updated: config.h generated into cpp/iperf/ for correct include paths - iperf_jni.c fixed for 3.19 API (no start_time on iperf_test) - getTestProgress() returns g_test->duration as fallback (no real-time elapsed tracking yet) - NDK CMake pinned to 3.22.1 - UI layer updated: removed Iperf3BinaryPathProvider references - Tests updated: TestRunViewModelTest matches new service interface - pubspec.yaml: removed iperf3 assets bundle, added http dependency - AndroidManifest: INTERNET permission (needed for hostname resolution)
This commit is contained in:
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2022, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include "iperf.h"
|
||||
#include "iperf_api.h"
|
||||
|
||||
int gerror;
|
||||
|
||||
char iperf_timestrerr[100];
|
||||
|
||||
/* Do a printf to stderr. */
|
||||
void
|
||||
iperf_err(struct iperf_test *test, const char *format, ...)
|
||||
{
|
||||
va_list argp;
|
||||
char str[1000];
|
||||
time_t now;
|
||||
struct tm *ltm = NULL;
|
||||
char *ct = NULL;
|
||||
|
||||
/* Timestamp if requested */
|
||||
if (test != NULL && test->timestamps) {
|
||||
time(&now);
|
||||
ltm = localtime(&now);
|
||||
strftime(iperf_timestrerr, sizeof(iperf_timestrerr), test->timestamp_format, ltm);
|
||||
ct = iperf_timestrerr;
|
||||
}
|
||||
|
||||
va_start(argp, format);
|
||||
vsnprintf(str, sizeof(str), format, argp);
|
||||
if (test != NULL && test->json_output && test->json_top != NULL)
|
||||
cJSON_AddStringToObject(test->json_top, "error", str);
|
||||
else {
|
||||
if (test != NULL && pthread_mutex_lock(&(test->print_mutex)) != 0) {
|
||||
perror("iperf_err: pthread_mutex_lock");
|
||||
}
|
||||
|
||||
if (test != NULL && test->outfile != NULL && test->outfile != stdout) {
|
||||
if (ct) {
|
||||
fprintf(test->outfile, "%s", ct);
|
||||
}
|
||||
fprintf(test->outfile, "iperf3: %s\n", str);
|
||||
}
|
||||
else {
|
||||
if (ct) {
|
||||
fprintf(stderr, "%s", ct);
|
||||
}
|
||||
fprintf(stderr, "iperf3: %s\n", str);
|
||||
}
|
||||
|
||||
if (test != NULL && pthread_mutex_unlock(&(test->print_mutex)) != 0) {
|
||||
perror("iperf_err: pthread_mutex_unlock");
|
||||
}
|
||||
|
||||
}
|
||||
va_end(argp);
|
||||
}
|
||||
|
||||
/* Do a printf to stderr or log file as appropriate, then exit(0). */
|
||||
void
|
||||
iperf_signormalexit(struct iperf_test *test, const char *format, ...)
|
||||
{
|
||||
va_list argp;
|
||||
|
||||
va_start(argp, format);
|
||||
iperf_exit(test, 0, format, argp);
|
||||
}
|
||||
|
||||
/* Do a printf to stderr or log file as appropriate, then exit(1). */
|
||||
void
|
||||
iperf_errexit(struct iperf_test *test, const char *format, ...)
|
||||
{
|
||||
va_list argp;
|
||||
|
||||
va_start(argp, format);
|
||||
iperf_exit(test, 1, format, argp);
|
||||
}
|
||||
|
||||
/* Do a printf to stderr or log file as appropriate, then exit. */
|
||||
void
|
||||
iperf_exit(struct iperf_test *test, int exit_code, const char *format, va_list argp)
|
||||
{
|
||||
char str[1000];
|
||||
time_t now;
|
||||
struct tm *ltm = NULL;
|
||||
char *ct = NULL;
|
||||
|
||||
/* Timestamp if requested */
|
||||
if (test != NULL && test->timestamps) {
|
||||
time(&now);
|
||||
ltm = localtime(&now);
|
||||
strftime(iperf_timestrerr, sizeof(iperf_timestrerr), iperf_get_test_timestamp_format(test), ltm);
|
||||
ct = iperf_timestrerr;
|
||||
}
|
||||
|
||||
vsnprintf(str, sizeof(str), format, argp);
|
||||
if (test != NULL && test->json_output) {
|
||||
if (test->json_top != NULL) {
|
||||
cJSON_AddStringToObject(test->json_top, "error", str);
|
||||
}
|
||||
iperf_json_finish(test);
|
||||
} else {
|
||||
if (test != NULL && pthread_mutex_lock(&(test->print_mutex)) != 0) {
|
||||
perror("iperf_errexit: pthread_mutex_lock");
|
||||
}
|
||||
|
||||
if (test && test->outfile && test->outfile != stdout) {
|
||||
if (ct) {
|
||||
fprintf(test->outfile, "%s", ct);
|
||||
}
|
||||
fprintf(test->outfile, "iperf3: %s\n", str);
|
||||
}
|
||||
else {
|
||||
if (ct) {
|
||||
fprintf(stderr, "%s", ct);
|
||||
}
|
||||
fprintf(stderr, "iperf3: %s\n", str);
|
||||
}
|
||||
|
||||
if (test != NULL && pthread_mutex_unlock(&(test->print_mutex)) != 0) {
|
||||
perror("iperf_errexit: pthread_mutex_unlock");
|
||||
}
|
||||
}
|
||||
|
||||
va_end(argp);
|
||||
if (test)
|
||||
iperf_delete_pidfile(test);
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
int i_errno;
|
||||
|
||||
char *
|
||||
iperf_strerror(int int_errno)
|
||||
{
|
||||
static char errstr[256];
|
||||
int len, perr, herr;
|
||||
perr = herr = 0;
|
||||
|
||||
len = sizeof(errstr);
|
||||
memset(errstr, 0, len);
|
||||
|
||||
switch (int_errno) {
|
||||
case IENONE:
|
||||
snprintf(errstr, len, "no error");
|
||||
break;
|
||||
case IESERVCLIENT:
|
||||
snprintf(errstr, len, "cannot be both server and client");
|
||||
break;
|
||||
case IENOROLE:
|
||||
snprintf(errstr, len, "must either be a client (-c) or server (-s)");
|
||||
break;
|
||||
case IESERVERONLY:
|
||||
snprintf(errstr, len, "some option you are trying to set is server only");
|
||||
break;
|
||||
case IECLIENTONLY:
|
||||
snprintf(errstr, len, "some option you are trying to set is client only");
|
||||
break;
|
||||
case IEDURATION:
|
||||
snprintf(errstr, len, "test duration valid values are 0 to %d seconds", MAX_TIME);
|
||||
break;
|
||||
case IENUMSTREAMS:
|
||||
snprintf(errstr, len, "number of parallel streams too large (maximum = %d)", MAX_STREAMS);
|
||||
break;
|
||||
case IEBLOCKSIZE:
|
||||
snprintf(errstr, len, "block size too large (maximum = %d bytes)", MAX_BLOCKSIZE);
|
||||
break;
|
||||
case IEBUFSIZE:
|
||||
snprintf(errstr, len, "socket buffer size too large (maximum = %d bytes)", MAX_TCP_BUFFER);
|
||||
break;
|
||||
case IEINTERVAL:
|
||||
snprintf(errstr, len, "invalid report interval (min = %g, max = %g seconds)", MIN_INTERVAL, MAX_INTERVAL);
|
||||
break;
|
||||
case IEBIND: /* UNUSED */
|
||||
snprintf(errstr, len, "--bind must be specified to use --cport");
|
||||
break;
|
||||
case IEUDPBLOCKSIZE:
|
||||
snprintf(errstr, len, "block size invalid (minimum = %d bytes, maximum = %d bytes)", MIN_UDP_BLOCKSIZE, MAX_UDP_BLOCKSIZE);
|
||||
break;
|
||||
case IEBADTOS:
|
||||
snprintf(errstr, len, "bad TOS value (must be between 0 and 255 inclusive)");
|
||||
break;
|
||||
case IESETCLIENTAUTH:
|
||||
snprintf(errstr, len, "you must specify a username, password, and path to a valid RSA public key");
|
||||
break;
|
||||
case IESETSERVERAUTH:
|
||||
snprintf(errstr, len, "you must specify a path to a valid RSA private key and a user credential file");
|
||||
break;
|
||||
case IESERVERAUTHUSERS:
|
||||
snprintf(errstr, len, "cannot access authorized users file");
|
||||
break;
|
||||
case IEBADFORMAT:
|
||||
snprintf(errstr, len, "bad format specifier (valid formats are in the set [kmgtKMGT])");
|
||||
break;
|
||||
case IEBADPORT:
|
||||
snprintf(errstr, len, "port number must be between 1 and 65535 inclusive");
|
||||
break;
|
||||
case IEMSS:
|
||||
snprintf(errstr, len, "TCP MSS too large (maximum = %d bytes)", MAX_MSS);
|
||||
break;
|
||||
case IENOSENDFILE:
|
||||
snprintf(errstr, len, "this OS does not support sendfile");
|
||||
break;
|
||||
case IEOMIT:
|
||||
snprintf(errstr, len, "bogus value for --omit (maximum = %d seconds)", MAX_OMIT_TIME);
|
||||
break;
|
||||
case IEUNIMP:
|
||||
snprintf(errstr, len, "an option you are trying to set is not implemented yet");
|
||||
break;
|
||||
case IEFILE:
|
||||
snprintf(errstr, len, "unable to open -F file");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEBURST:
|
||||
snprintf(errstr, len, "invalid burst count (maximum = %d)", MAX_BURST);
|
||||
break;
|
||||
case IEENDCONDITIONS:
|
||||
snprintf(errstr, len, "only one test end condition (-t, -n, -k) may be specified");
|
||||
break;
|
||||
case IELOGFILE:
|
||||
snprintf(errstr, len, "unable to open log file");
|
||||
perr = 1;
|
||||
break;
|
||||
case IENOSCTP:
|
||||
snprintf(errstr, len, "no SCTP support available");
|
||||
break;
|
||||
case IENEWTEST:
|
||||
snprintf(errstr, len, "unable to create a new test");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEINITTEST:
|
||||
snprintf(errstr, len, "test initialization failed");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEAUTHTEST:
|
||||
snprintf(errstr, len, "test authorization failed");
|
||||
break;
|
||||
case IELISTEN:
|
||||
snprintf(errstr, len, "unable to start listener for connections");
|
||||
herr = 1;
|
||||
perr = 1;
|
||||
break;
|
||||
case IECONNECT:
|
||||
snprintf(errstr, len, "unable to connect to server - server may have stopped running or use a different port, firewall issue, etc.");
|
||||
perr = 1;
|
||||
herr = 1;
|
||||
break;
|
||||
case IEACCEPT:
|
||||
snprintf(errstr, len, "unable to accept connection from client");
|
||||
herr = 1;
|
||||
perr = 1;
|
||||
break;
|
||||
case IESENDCOOKIE:
|
||||
snprintf(errstr, len, "unable to send cookie to server");
|
||||
perr = 1;
|
||||
break;
|
||||
case IERECVCOOKIE:
|
||||
snprintf(errstr, len, "unable to receive cookie at server");
|
||||
perr = 1;
|
||||
break;
|
||||
case IECTRLWRITE:
|
||||
snprintf(errstr, len, "unable to write to the control socket");
|
||||
perr = 1;
|
||||
break;
|
||||
case IECTRLREAD:
|
||||
snprintf(errstr, len, "unable to read from the control socket");
|
||||
perr = 1;
|
||||
break;
|
||||
case IECTRLCLOSE:
|
||||
snprintf(errstr, len, "control socket has closed unexpectedly");
|
||||
break;
|
||||
case IEMESSAGE:
|
||||
snprintf(errstr, len, "received an unknown control message (ensure other side is iperf3 and not iperf)");
|
||||
break;
|
||||
case IESENDMESSAGE:
|
||||
snprintf(errstr, len, "unable to send control message - port may not be available, the other side may have stopped running, etc.");
|
||||
perr = 1;
|
||||
break;
|
||||
case IERECVMESSAGE:
|
||||
snprintf(errstr, len, "unable to receive control message - port may not be available, the other side may have stopped running, etc.");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESENDPARAMS:
|
||||
snprintf(errstr, len, "unable to send parameters to server");
|
||||
perr = 1;
|
||||
break;
|
||||
case IERECVPARAMS:
|
||||
snprintf(errstr, len, "unable to receive parameters from client");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPACKAGERESULTS:
|
||||
snprintf(errstr, len, "unable to package results");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESENDRESULTS:
|
||||
snprintf(errstr, len, "unable to send results");
|
||||
perr = 1;
|
||||
break;
|
||||
case IERECVRESULTS:
|
||||
snprintf(errstr, len, "unable to receive results");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESELECT:
|
||||
snprintf(errstr, len, "select failed");
|
||||
perr = 1;
|
||||
break;
|
||||
case IECLIENTTERM:
|
||||
snprintf(errstr, len, "the client has terminated");
|
||||
break;
|
||||
case IESERVERTERM:
|
||||
snprintf(errstr, len, "the server has terminated");
|
||||
break;
|
||||
case IEACCESSDENIED:
|
||||
snprintf(errstr, len, "the server is busy running a test. try again later");
|
||||
break;
|
||||
case IESETNODELAY:
|
||||
snprintf(errstr, len, "unable to set TCP/SCTP NODELAY");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETMSS:
|
||||
snprintf(errstr, len, "unable to set TCP/SCTP MSS");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETBUF:
|
||||
snprintf(errstr, len, "unable to set socket buffer size");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETTOS:
|
||||
snprintf(errstr, len, "unable to set IP TOS");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETCOS:
|
||||
snprintf(errstr, len, "unable to set IPv6 traffic class");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETFLOW:
|
||||
snprintf(errstr, len, "unable to set IPv6 flow label");
|
||||
break;
|
||||
case IEREUSEADDR:
|
||||
snprintf(errstr, len, "unable to reuse address on socket");
|
||||
perr = 1;
|
||||
break;
|
||||
case IENONBLOCKING:
|
||||
snprintf(errstr, len, "unable to set socket to non-blocking");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETWINDOWSIZE:
|
||||
snprintf(errstr, len, "unable to set socket window size");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPROTOCOL:
|
||||
snprintf(errstr, len, "protocol does not exist");
|
||||
break;
|
||||
case IEAFFINITY:
|
||||
snprintf(errstr, len, "unable to set CPU affinity");
|
||||
perr = 1;
|
||||
break;
|
||||
case IERCVTIMEOUT:
|
||||
snprintf(errstr, len, "receive timeout value is incorrect or not in range");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESNDTIMEOUT:
|
||||
snprintf(errstr, len, "send timeout value is incorrect or not in range");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEUDPFILETRANSFER:
|
||||
snprintf(errstr, len, "cannot transfer file using UDP");
|
||||
break;
|
||||
case IERVRSONLYRCVTIMEOUT:
|
||||
snprintf(errstr, len, "client receive timeout is valid only in receiving mode");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEDAEMON:
|
||||
snprintf(errstr, len, "unable to become a daemon");
|
||||
perr = 1;
|
||||
break;
|
||||
case IECREATESTREAM:
|
||||
snprintf(errstr, len, "unable to create a new stream");
|
||||
herr = 1;
|
||||
perr = 1;
|
||||
break;
|
||||
case IEINITSTREAM:
|
||||
snprintf(errstr, len, "unable to initialize stream");
|
||||
herr = 1;
|
||||
perr = 1;
|
||||
break;
|
||||
case IESTREAMLISTEN:
|
||||
snprintf(errstr, len, "unable to start stream listener");
|
||||
herr = 1;
|
||||
perr = 1;
|
||||
break;
|
||||
case IESTREAMCONNECT:
|
||||
snprintf(errstr, len, "unable to connect stream");
|
||||
herr = 1;
|
||||
perr = 1;
|
||||
break;
|
||||
case IESTREAMACCEPT:
|
||||
snprintf(errstr, len, "unable to accept stream connection");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESTREAMWRITE:
|
||||
snprintf(errstr, len, "unable to write to stream socket");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESTREAMREAD:
|
||||
snprintf(errstr, len, "unable to read from stream socket");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESTREAMCLOSE:
|
||||
snprintf(errstr, len, "stream socket has closed unexpectedly");
|
||||
break;
|
||||
case IESTREAMID:
|
||||
snprintf(errstr, len, "stream has an invalid id");
|
||||
break;
|
||||
case IENEWTIMER:
|
||||
snprintf(errstr, len, "unable to create new timer");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEUPDATETIMER:
|
||||
snprintf(errstr, len, "unable to update timer");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETCONGESTION:
|
||||
snprintf(errstr, len, "unable to set TCP_CONGESTION: "
|
||||
"Supplied congestion control algorithm not supported on this host");
|
||||
break;
|
||||
case IEPIDFILE:
|
||||
snprintf(errstr, len, "unable to write PID file");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEV6ONLY:
|
||||
snprintf(errstr, len, "Unable to set/reset IPV6_V6ONLY");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETSCTPDISABLEFRAG:
|
||||
snprintf(errstr, len, "unable to set SCTP_DISABLE_FRAGMENTS");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETSCTPNSTREAM:
|
||||
snprintf(errstr, len, "unable to set SCTP_INIT num of SCTP streams\n");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETPACING:
|
||||
snprintf(errstr, len, "unable to set socket pacing");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETBUF2:
|
||||
snprintf(errstr, len, "socket buffer size not set correctly");
|
||||
break;
|
||||
case IEREVERSEBIDIR:
|
||||
snprintf(errstr, len, "cannot be both reverse and bidirectional");
|
||||
break;
|
||||
case IETOTALRATE:
|
||||
snprintf(errstr, len, "total required bandwidth is larger than server limit");
|
||||
break;
|
||||
case IESKEWTHRESHOLD:
|
||||
snprintf(errstr, len, "skew threshold must be a positive number");
|
||||
break;
|
||||
case IEIDLETIMEOUT:
|
||||
snprintf(errstr, len, "idle timeout parameter is not positive or larger than allowed limit");
|
||||
break;
|
||||
case IEBINDDEV:
|
||||
snprintf(errstr, len, "Unable to bind-to-device (check perror, maybe permissions?)");
|
||||
break;
|
||||
case IEBINDDEVNOSUPPORT:
|
||||
snprintf(errstr, len, "`<ip>%%<dev>` is not supported as system does not support bind to device");
|
||||
break;
|
||||
case IEHOSTDEV:
|
||||
snprintf(errstr, len, "host device name (ip%%<dev>) is supported (and required) only for IPv6 link-local address");
|
||||
break;
|
||||
case IENOMSG:
|
||||
snprintf(errstr, len, "idle timeout for receiving data");
|
||||
break;
|
||||
case IESETDONTFRAGMENT:
|
||||
snprintf(errstr, len, "unable to set IP Do-Not-Fragment flag");
|
||||
break;
|
||||
case IESETUSERTIMEOUT:
|
||||
snprintf(errstr, len, "unable to set TCP USER_TIMEOUT");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPTHREADCREATE:
|
||||
snprintf(errstr, len, "unable to create thread");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPTHREADCANCEL:
|
||||
snprintf(errstr, len, "unable to cancel thread");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPTHREADJOIN:
|
||||
snprintf(errstr, len, "unable to join thread");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPTHREADATTRINIT:
|
||||
snprintf(errstr, len, "unable to create thread attributes");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPTHREADSIGMASK:
|
||||
snprintf(errstr, len, "unable to change mask of blocked signals");
|
||||
break;
|
||||
case IEPTHREADATTRDESTROY:
|
||||
snprintf(errstr, len, "unable to destroy thread attributes");
|
||||
case IECNTLKA:
|
||||
snprintf(errstr, len, "control connection Keepalive period should be larger than the full retry period (interval * count)");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETCNTLKA:
|
||||
snprintf(errstr, len, "unable to set socket keepalive (SO_KEEPALIVE) option");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETCNTLKAKEEPIDLE:
|
||||
snprintf(errstr, len, "unable to set socket keepalive TCP period (TCP_KEEPIDLE) option");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETCNTLKAINTERVAL:
|
||||
snprintf(errstr, len, "unable to set/get socket keepalive TCP retry interval (TCP_KEEPINTVL) option");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETCNTLKACOUNT:
|
||||
snprintf(errstr, len, "unable to set/get socket keepalive TCP number of retries (TCP_KEEPCNT) option");
|
||||
perr = 1;
|
||||
break;
|
||||
default:
|
||||
snprintf(errstr, len, "int_errno=%d", int_errno);
|
||||
perr = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Append the result of strerror() or gai_strerror() if appropriate */
|
||||
if (herr || perr)
|
||||
strncat(errstr, ": ", len - strlen(errstr) - 1);
|
||||
if (errno && perr)
|
||||
strncat(errstr, strerror(errno), len - strlen(errstr) - 1);
|
||||
else if (herr && gerror) {
|
||||
strncat(errstr, gai_strerror(gerror), len - strlen(errstr) - 1);
|
||||
gerror = 0;
|
||||
}
|
||||
|
||||
return errstr;
|
||||
}
|
||||
Reference in New Issue
Block a user