Tag Archives: unable to create a new stream: Invalid argument

[Solved] iperf Analyze Error: unable to create a new stream: Invalid argument

The root cause of the error reported in the title is that the iperf command code customized by the company is relatively old, so the open source code still needs to be updated in time!

Device: Android p

Problem: when the iperf server is opened on the PC and the iperf client is opened on the Android device, the throughput cannot be tested normally, as shown in the following figure:

The Android device reports an error “unable to create a new stream: invalid argument”. The PC can see that there is a connection at the beginning, and then the connection is closed due to the client.

Solution:

First look at the error report, “Unable to create a new stream” corresponds to “case IECREATESTREAM”. It can be determined that this error occurs in a function of iperf_api, and it is found in the iperf_new_stream() function. It is simple here. Exclude the first few A malloc, the first function mkstemp(), this function is interesting, the actual problem is also here, first introduce this function.

The mkstep function creates a file with a unique file name in the system and opens it. Only the current user can access the temporary file and perform read and write operations. The mkstamp function has only one parameter template, which is a non empty string ending in “XXXXXX”. The mkstemp function will replace “XXXXXX” with a randomly generated string to ensure the uniqueness of the file name.

You can see the common error codes eexist and einval through the man Manual:

1>   EEXIST:   Unable to create a unique temporary file, the content of parameter template is undefined;

2>   Einval: for mkstamp(), the last six characters of template are not XXXXXX;

It is worth mentioning that in glibc 2.0.6 and earlier glibc libraries, the access permission of this file is 0666, that is, all users in UNIX system can access it, which will have a big problem. Therefore, the access permission of this file in glibc libraries after glibc 2.0.7 is adjusted to 0600.

The temporary file created by the mkstemp function cannot be deleted automatically, so after executing the mkstemp function, you need to call the unlink function. The unlink function deletes the directory entry of the file, but the temporary file can also be accessed through the file descriptor until the last open process closes the file, or the temporary file is automatically and completely deleted after the program exits.

The original code snippet of the error is as follows

struct iperf_stream *iperf_new_stream(struct iperf_test *test, int s)
{
    int i;
    struct iperf_stream *sp;
    //The problem is here, the template parameter format is incorrect
    char template[] = "/data/data/iperf3.0.11";
    ......
    /* Create and randomize the buffer */
    sp->buffer_fd = mkstemp(template);
    if (sp->buffer_fd == -1) {
        i_errno = IECREATESTREAM;
        free(sp->result);
        free(sp);
        return NULL;
    }
    if (unlink(template) < 0) {
        i_errno = IECREATESTREAM;
        free(sp->result);
        free(sp);
        return NULL;
    }
    if (ftruncate(sp->buffer_fd, test->settings->blksize) < 0) {
        i_errno = IECREATESTREAM;
        free(sp->result);
        free(sp);
        return NULL;
    }
    ......

It’s clear from here that the problem with the template parameter causes an error in mkstamp() and returns einval. The corresponding Android device executes the iperf command and reports an error of ‘invalid argument’. I was going to make a simple change. After thinking about iperf, everyone is using it. There should be no big problem. I went to GitHub community and found that predecessors have paved the way. I changed it with reference to the submission in the community.

diff --git a/system/core/swiperf/iperf.h b/system/core/swiperf/iperf.h
index f4d394b..2bc0b73 100644
--- a/system/core/swiperf/iperf.h
+++ b/system/core/swiperf/iperf.h
@@ -178,6 +178,7 @@ struct iperf_test
     struct protocol *protocol;
     signed char state;
     char     *server_hostname;                  /* -c option */
     //The latest open source code iperf_test structure itself has tmp_template
+    char     *tmp_template;
     char     *bind_address;                     /* -B option */
     int       server_port;
     int       omit;                             /* duration of omit period (-O flag) */
@@ -252,6 +253,9 @@ struct iperf_test
     char *server_output_text;
     cJSON *json_server_output;

+    /* Store User Defined Path and use it for creating temporary streams */
+    char *user_stream_path;
+
     /* Server output (use on server side only) */
     TAILQ_HEAD(iperf_textlisthead, iperf_textline) server_output_list;

diff --git a/system/core/swiperf/iperf_api.c b/system/core/swiperf/iperf_api.c
index 13269d5..27a92ac 100644
--- a/system/core/swiperf/iperf_api.c
+++ b/system/core/swiperf/iperf_api.c
@@ -27,6 +27,9 @@
 #define _GNU_SOURCE
 #define __USE_GNU

+/* Suppress Warning generated by GCC __attribute__ for write */
+#pragma GCC diagnostic ignored "-Wunused-result"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -235,8 +238,21 @@ iperf_get_test_one_off(struct iperf_test *ipt)
     return ipt->one_off;
 }

-/************** Setter routines for some fields inside iperf_test *************/
+/* Getter routine for user-defined stream-path */
+char *
+iperf_get_stream_path(struct iperf_test *ipt)
+{
+    return ipt->user_stream_path;
+}

+/* Setter routine for user-defined stream-path */
+void
+iperf_set_stream_path(struct iperf_test *ipt, char *_stram_path)
+{
+    ipt->user_stream_path = strdup(_stram_path);
+}
+
+/************** Setter routines for some fields inside iperf_test *************/
 void
 iperf_set_verbose(struct iperf_test *ipt, int verbose)
 {
@@ -596,6 +612,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
        {"get-server-output", no_argument, NULL, OPT_GET_SERVER_OUTPUT},
         {"debug", no_argument, NULL, 'd'},
         {"help", no_argument, NULL, 'h'},
         //Add the analysis of the corresponding parameters
+        {"stream-path", required_argument, NULL, OPT_STREAM_PATH},
         {NULL, 0, NULL, 0}
     };
     int flag;
@@ -608,6 +625,15 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
     server_flag = client_flag = rate_flag = duration_flag = 0;
     while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:M:N46S:L:ZO:F:A:T:C:dh", longopts, NULL)) != -1) {
         switch (flag) {
+            /*
+             * Set Stream Path to user-defined path
+             * Reason: To work on Android as /tmp is not available on Android
+             * also access to a static /data/local/tmp/ is also restricted for Applications
+             * Only native application path is accessable with complete permissions
+             */
+            case OPT_STREAM_PATH:
+                iperf_set_stream_path(test, optarg);
+                break;
             case 'p':
                 test->server_port = atoi(optarg);
                 break;
@@ -2367,7 +2393,27 @@ iperf_new_stream(struct iperf_test *test, int s)
 {
     int i;
     struct iperf_stream *sp;
-    char template[] = "/data/data/iperf3.0.11";
+    char template[1024];
+    if (test->tmp_template) {
+        snprintf(template, sizeof(template)/sizeof(char), "%s", test->tmp_template);
+    } else {
+        //find the system temporary dir *unix, windows, cygwin support
+        char* tempdir = getenv("TMPDIR");
+        if (tempdir == 0) {
+            tempdir = getenv("TEMP");
+        }
+        if (tempdir == 0) {
+            tempdir = getenv("TMP");
+        }
+        /* Use User Specific Path if available */
+        if (test->user_stream_path != NULL) { //There is a pass to go here
+            tempdir = iperf_get_stream_path(test);
+        }
+        if (tempdir == 0) { // Android devices without parameters, create temporary files under /tmp by default
+            tempdir = "/tmp";
+        }
+        snprintf(template, sizeof(template)/sizeof(char), "%s/iperf3.XXXXXX", tempdir);
+    }

     h_errno = 0;

diff --git a/system/core/swiperf/iperf_api.h b/system/core/swiperf/iperf_api.h
index c019a31..e19492b 100644
--- a/system/core/swiperf/iperf_api.h
+++ b/system/core/swiperf/iperf_api.h
@@ -44,6 +44,8 @@ struct iperf_stream;
 #define OPT_SCTP 1
 #define OPT_LOGFILE 2
 #define OPT_GET_SERVER_OUTPUT 3
+/* Stream Path Edit for Android Compatibility */
+#define OPT_STREAM_PATH 4

 /* states */
 #define TEST_START 1
@@ -87,6 +89,10 @@ int  iperf_get_test_zerocopy( struct iperf_test* ipt );
 int    iperf_get_test_get_server_output( struct iperf_test* ipt );
 char*  iperf_get_test_bind_address ( struct iperf_test* ipt );
 int    iperf_get_test_one_off( struct iperf_test* ipt );
+/* Getter routine for user-defined stream-path */
+char* iperf_get_stream_path( struct iperf_test *ipt );
+/* Setter routine for user-defined stream-path */
+void iperf_set_stream_path( struct iperf_test *ipt, char *_stram_path );

 /* Setter routines for some fields inside iperf_test. */
 void   iperf_set_verbose( struct iperf_test* ipt, int verbose );
diff --git a/system/core/swiperf/iperf_locale.c b/system/core/swiperf/iperf_locale.c
index 30e6290..9fe7ba6 100644
--- a/system/core/swiperf/iperf_locale.c
+++ b/system/core/swiperf/iperf_locale.c
@@ -141,6 +141,7 @@ const char usage_longstr[] = "Usage: iperf [-s|-c host] [options]\n"
                            "  -O, --omit N              omit the first n seconds\n"
                            "  -T, --title str           prefix every output line with this string\n"
                            "  --get-server-output       get results from server\n"
                            //New --stream-path passing parameter, support custom write path
+                           "  --stream-path             use user-defied path to create temporary stream\n"

 #ifdef NOT_YET_SUPPORTED /* still working on these */
 #endif