Browse Source

curl: add variables to --write-out

In particular, these ones can help a user to create its own error
message when one or transfers fail.

writeout: add 'onerror', 'url', 'urlnum', 'exitcode', 'errormsg'

onerror - lets a user only show the rest on non-zero exit codes

url - the input URL used for this transfer

urlnum - the numerical URL counter (0 indexed) for this transfer

exitcode - the numerical exit code for the transfer

errormsg - obvious

Reported-by: Earnestly on github
Fixes #6199
Closes #6207
Daniel Stenberg 3 years ago
parent
commit
7a90ddf88f
7 changed files with 128 additions and 104 deletions
  1. 16 0
      docs/cmdline-opts/write-out.d
  2. 2 1
      src/tool_operate.c
  3. 1 0
      src/tool_operate.h
  4. 2 0
      src/tool_paramhlp.c
  5. 1 0
      src/tool_sdecls.h
  6. 72 75
      src/tool_writeout.c
  7. 34 28
      src/tool_writeout.h

+ 16 - 0
docs/cmdline-opts/write-out.d

@@ -29,6 +29,12 @@ The variables available are:
 .B content_type
 The Content-Type of the requested document, if there was any.
 .TP
+.B errormsg
+The error message. (Added in 7.75.0)
+.TP
+.B exitcode
+The numerical exitcode. (Added in 7.75.0)
+.TP
 .B filename_effective
 The ultimate filename that curl writes out to. This is only meaningful if curl
 is told to write to a file with the --remote-name or --output
@@ -74,6 +80,10 @@ The number of response headers in the most recent request (restarted at each
 .B num_redirects
 Number of redirects that were followed in the request. (Added in 7.12.3)
 .TP
+.B onerror
+The rest of the output is only shown if the transfer returned a non-zero error
+(Added in 7.75.0)
+.TP
 .B proxy_ssl_verify_result
 The result of the HTTPS proxy's SSL peer certificate verification that was
 requested. 0 means the verification was successful. (Added in 7.52.0)
@@ -161,6 +171,12 @@ server needed to calculate the result.
 .B time_total
 The total time, in seconds, that the full operation lasted.
 .TP
+.B url
+The URL that was fetched. (Added in 7.75.0)
+.TP
+.B urlnum
+The URL index number of this transfer, 0-indexed. (Added in 7.75.0)
+.TP
 .B url_effective
 The URL that was fetched last. This is most meaningful if you've told curl
 to follow location: headers.

+ 2 - 1
src/tool_operate.c

@@ -628,7 +628,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
     fputs("\n", per->progressbar.out);
 
   if(config->writeout)
-    ourWriteOut(per->curl, per, config->writeout);
+    ourWriteOut(per->curl, per, config->writeout, result);
 
   /* Close the outs file */
   if(outs->fopened && outs->stream) {
@@ -873,6 +873,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
         *added = TRUE;
         per->config = config;
         per->curl = curl;
+        per->urlnum = urlnode->num;
 
         /* default headers output stream is stdout */
         heads = &per->heads;

+ 1 - 0
src/tool_operate.h

@@ -41,6 +41,7 @@ struct per_transfer {
   struct metalinkfile *mlfile;
   struct metalink_resource *mlres;
   char *this_url;
+  unsigned int urlnum; /* the index of the given URL */
   char *outfile;
   bool infdopen; /* TRUE if infd needs closing */
   int infd;

+ 2 - 0
src/tool_paramhlp.c

@@ -40,6 +40,7 @@
 
 struct getout *new_getout(struct OperationConfig *config)
 {
+  static int outnum = 0;
   struct getout *node = calloc(1, sizeof(struct getout));
   struct getout *last = config->url_last;
   if(node) {
@@ -53,6 +54,7 @@ struct getout *new_getout(struct OperationConfig *config)
     config->url_last = node;
 
     node->flags = config->default_node_flags;
+    node->num = outnum++;
   }
   return node;
 }

+ 1 - 0
src/tool_sdecls.h

@@ -105,6 +105,7 @@ struct getout {
   char          *outfile;   /* where to store the output */
   char          *infile;    /* file to upload, if GETOUT_UPLOAD is set */
   int            flags;     /* options - composed of GETOUT_* bits */
+  int            num;       /* which URL number in an invocation */
 };
 
 #define GETOUT_OUTFILE    (1<<0)  /* set when outfile is deemed done */

+ 72 - 75
src/tool_writeout.c

@@ -30,80 +30,58 @@
 #include "memdebug.h" /* keep this as LAST include */
 
 static const struct writeoutvar variables[] = {
-  {"url_effective", VAR_EFFECTIVE_URL, 0,
-   CURLINFO_EFFECTIVE_URL, JSON_STRING},
-  {"method", VAR_EFFECTIVE_METHOD, 0,
-   CURLINFO_EFFECTIVE_METHOD, JSON_STRING},
-  {"http_code", VAR_HTTP_CODE, 0,
-   CURLINFO_RESPONSE_CODE, JSON_LONG},
-  {"response_code", VAR_HTTP_CODE, 0,
-   CURLINFO_RESPONSE_CODE, JSON_LONG},
-  {"num_headers", VAR_NUM_HEADERS, 0,
-   0, JSON_LONG},
-  {"http_connect", VAR_HTTP_CODE_PROXY, 0,
-   CURLINFO_HTTP_CONNECTCODE, JSON_LONG},
-  {"time_total", VAR_TOTAL_TIME, 0,
-   CURLINFO_TOTAL_TIME_T, JSON_TIME},
-  {"time_namelookup", VAR_NAMELOOKUP_TIME, 0,
-   CURLINFO_NAMELOOKUP_TIME_T, JSON_TIME},
-  {"time_connect", VAR_CONNECT_TIME, 0,
-   CURLINFO_CONNECT_TIME_T, JSON_TIME},
-  {"time_appconnect", VAR_APPCONNECT_TIME, 0,
-   CURLINFO_APPCONNECT_TIME_T, JSON_TIME},
-  {"time_pretransfer", VAR_PRETRANSFER_TIME, 0,
-   CURLINFO_PRETRANSFER_TIME_T, JSON_TIME},
-  {"time_starttransfer", VAR_STARTTRANSFER_TIME, 0,
-   CURLINFO_STARTTRANSFER_TIME_T, JSON_TIME},
-  {"size_header", VAR_HEADER_SIZE, 0,
-   CURLINFO_HEADER_SIZE, JSON_LONG},
-  {"size_request", VAR_REQUEST_SIZE, 0,
-   CURLINFO_REQUEST_SIZE, JSON_LONG},
-  {"size_download", VAR_SIZE_DOWNLOAD, 0,
-   CURLINFO_SIZE_DOWNLOAD_T, JSON_OFFSET},
-  {"size_upload", VAR_SIZE_UPLOAD, 0,
-   CURLINFO_SIZE_UPLOAD_T, JSON_OFFSET},
-  {"speed_download", VAR_SPEED_DOWNLOAD, 0,
-   CURLINFO_SPEED_DOWNLOAD_T, JSON_OFFSET},
-  {"speed_upload", VAR_SPEED_UPLOAD, 0,
-   CURLINFO_SPEED_UPLOAD_T, JSON_OFFSET},
-  {"content_type", VAR_CONTENT_TYPE, 0,
-   CURLINFO_CONTENT_TYPE, JSON_STRING},
-  {"num_connects", VAR_NUM_CONNECTS, 0,
-   CURLINFO_NUM_CONNECTS, JSON_LONG},
-  {"time_redirect", VAR_REDIRECT_TIME, 0,
-   CURLINFO_REDIRECT_TIME_T, JSON_TIME},
-  {"num_redirects", VAR_REDIRECT_COUNT, 0,
-   CURLINFO_REDIRECT_COUNT, JSON_LONG},
-  {"ftp_entry_path", VAR_FTP_ENTRY_PATH, 0,
-   CURLINFO_FTP_ENTRY_PATH, JSON_STRING},
-  {"redirect_url", VAR_REDIRECT_URL, 0,
-   CURLINFO_REDIRECT_URL, JSON_STRING},
-  {"ssl_verify_result", VAR_SSL_VERIFY_RESULT, 0,
-   CURLINFO_SSL_VERIFYRESULT, JSON_LONG},
+  {"content_type", VAR_CONTENT_TYPE, 0, CURLINFO_CONTENT_TYPE, JSON_STRING},
+  {"filename_effective", VAR_EFFECTIVE_FILENAME, 0, 0, JSON_FILENAME},
+  {"exitcode", VAR_EXITCODE, 0, 0, JSON_LONG},
+  {"errormsg", VAR_ERRORMSG, 0, 0, JSON_STRING},
+  {"ftp_entry_path", VAR_FTP_ENTRY_PATH, 0, CURLINFO_FTP_ENTRY_PATH,
+   JSON_STRING},
+  {"http_code", VAR_HTTP_CODE, 0, CURLINFO_RESPONSE_CODE, JSON_LONG},
+  {"http_connect", VAR_HTTP_CODE_PROXY, 0, CURLINFO_HTTP_CONNECTCODE,
+   JSON_LONG},
+  {"http_version", VAR_HTTP_VERSION, 0, CURLINFO_HTTP_VERSION, JSON_VERSION},
+  {"json", VAR_JSON, 1, 0, JSON_NONE},
+  {"local_ip", VAR_LOCAL_IP, 0, CURLINFO_LOCAL_IP, JSON_STRING},
+  {"local_port", VAR_LOCAL_PORT, 0, CURLINFO_LOCAL_PORT, JSON_LONG},
+  {"method", VAR_EFFECTIVE_METHOD, 0, CURLINFO_EFFECTIVE_METHOD, JSON_STRING},
+  {"num_connects", VAR_NUM_CONNECTS, 0, CURLINFO_NUM_CONNECTS, JSON_LONG},
+  {"num_headers", VAR_NUM_HEADERS, 0, 0, JSON_LONG},
+  {"num_redirects", VAR_REDIRECT_COUNT, 0, CURLINFO_REDIRECT_COUNT, JSON_LONG},
+  {"onerror", VAR_ONERROR, 1, 0, JSON_NONE},
   {"proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT, 0,
    CURLINFO_PROXY_SSL_VERIFYRESULT, JSON_LONG},
-  {"filename_effective", VAR_EFFECTIVE_FILENAME, 0,
-   0, JSON_FILENAME},
-  {"remote_ip", VAR_PRIMARY_IP, 0,
-   CURLINFO_PRIMARY_IP, JSON_STRING},
-  {"remote_port", VAR_PRIMARY_PORT, 0,
-   CURLINFO_PRIMARY_PORT, JSON_LONG},
-  {"local_ip", VAR_LOCAL_IP, 0,
-   CURLINFO_LOCAL_IP, JSON_STRING},
-  {"local_port", VAR_LOCAL_PORT, 0,
-   CURLINFO_LOCAL_PORT, JSON_LONG},
-  {"http_version", VAR_HTTP_VERSION, 0,
-   CURLINFO_HTTP_VERSION, JSON_VERSION},
-  {"scheme", VAR_SCHEME, 0,
-   CURLINFO_SCHEME, JSON_STRING},
-  {"stdout", VAR_STDOUT, 1,
-   0, JSON_NONE},
-  {"stderr", VAR_STDERR, 1,
-   0, JSON_NONE},
-  {"json", VAR_JSON, 1,
-   0, JSON_NONE},
-  {NULL, VAR_NONE, 1,
-   0, JSON_NONE}
+  {"redirect_url", VAR_REDIRECT_URL, 0, CURLINFO_REDIRECT_URL, JSON_STRING},
+  {"remote_ip", VAR_PRIMARY_IP, 0, CURLINFO_PRIMARY_IP, JSON_STRING},
+  {"remote_port", VAR_PRIMARY_PORT, 0, CURLINFO_PRIMARY_PORT, JSON_LONG},
+  {"response_code", VAR_HTTP_CODE, 0, CURLINFO_RESPONSE_CODE, JSON_LONG},
+  {"scheme", VAR_SCHEME, 0, CURLINFO_SCHEME, JSON_STRING},
+  {"size_download", VAR_SIZE_DOWNLOAD, 0, CURLINFO_SIZE_DOWNLOAD_T,
+   JSON_OFFSET},
+  {"size_header", VAR_HEADER_SIZE, 0, CURLINFO_HEADER_SIZE, JSON_LONG},
+  {"size_request", VAR_REQUEST_SIZE, 0, CURLINFO_REQUEST_SIZE, JSON_LONG},
+  {"size_upload", VAR_SIZE_UPLOAD, 0, CURLINFO_SIZE_UPLOAD_T, JSON_OFFSET},
+  {"speed_download", VAR_SPEED_DOWNLOAD, 0, CURLINFO_SPEED_DOWNLOAD_T,
+   JSON_OFFSET},
+  {"speed_upload", VAR_SPEED_UPLOAD, 0, CURLINFO_SPEED_UPLOAD_T, JSON_OFFSET},
+  {"ssl_verify_result", VAR_SSL_VERIFY_RESULT, 0, CURLINFO_SSL_VERIFYRESULT,
+   JSON_LONG},
+  {"stderr", VAR_STDERR, 1, 0, JSON_NONE},
+  {"stdout", VAR_STDOUT, 1, 0, JSON_NONE},
+  {"time_appconnect", VAR_APPCONNECT_TIME, 0, CURLINFO_APPCONNECT_TIME_T,
+   JSON_TIME},
+  {"time_connect", VAR_CONNECT_TIME, 0, CURLINFO_CONNECT_TIME_T, JSON_TIME},
+  {"time_namelookup", VAR_NAMELOOKUP_TIME, 0, CURLINFO_NAMELOOKUP_TIME_T,
+   JSON_TIME},
+  {"time_pretransfer", VAR_PRETRANSFER_TIME, 0, CURLINFO_PRETRANSFER_TIME_T,
+   JSON_TIME},
+  {"time_redirect", VAR_REDIRECT_TIME, 0, CURLINFO_REDIRECT_TIME_T, JSON_TIME},
+  {"time_starttransfer", VAR_STARTTRANSFER_TIME, 0,
+   CURLINFO_STARTTRANSFER_TIME_T, JSON_TIME},
+  {"time_total", VAR_TOTAL_TIME, 0, CURLINFO_TOTAL_TIME_T, JSON_TIME},
+  {"url", VAR_INPUT_URL, 0, 0, JSON_STRING},
+  {"url_effective", VAR_EFFECTIVE_URL, 0, CURLINFO_EFFECTIVE_URL, JSON_STRING},
+  {"urlnum", VAR_URLNUM, 0, 0, JSON_LONG},
+  {NULL, VAR_NONE, 1, 0, JSON_NONE}
 };
 
 static void us2sec(FILE *stream, curl_off_t us)
@@ -114,15 +92,17 @@ static void us2sec(FILE *stream, curl_off_t us)
           secs, us);
 }
 
-void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo)
+void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo,
+                 CURLcode result)
 {
   FILE *stream = stdout;
   const char *ptr = writeinfo;
   char *stringp = NULL;
   long longinfo;
   curl_off_t offinfo;
+  bool done = FALSE;
 
-  while(ptr && *ptr) {
+  while(ptr && *ptr && !done) {
     if('%' == *ptr && ptr[1]) {
       if('%' == ptr[1]) {
         /* an escaped %-letter */
@@ -148,6 +128,24 @@ void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo)
             if(curl_strequal(ptr, variables[i].name)) {
               match = TRUE;
               switch(variables[i].id) {
+              case VAR_ONERROR:
+                if(result == CURLE_OK)
+                  /* this isn't error so skip the rest */
+                  done = TRUE;
+                break;
+              case VAR_EXITCODE:
+                fprintf(stream, "%d", (int)result);
+                break;
+              case VAR_ERRORMSG:
+                fputs(per->errorbuffer[0] ? per->errorbuffer :
+                      curl_easy_strerror(result), stream);
+                break;
+              case VAR_INPUT_URL:
+                fputs(per->this_url, stream);
+                break;
+              case VAR_URLNUM:
+                fprintf(stream, "%u", per->urlnum);
+                break;
               case VAR_EFFECTIVE_URL:
                 if((CURLE_OK ==
                     curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &stringp))
@@ -392,5 +390,4 @@ void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo)
       ptr++;
     }
   }
-
 }

+ 34 - 28
src/tool_writeout.h

@@ -26,41 +26,46 @@
 
 typedef enum {
   VAR_NONE,       /* must be the first */
-  VAR_TOTAL_TIME,
-  VAR_NAMELOOKUP_TIME,
-  VAR_CONNECT_TIME,
   VAR_APPCONNECT_TIME,
-  VAR_PRETRANSFER_TIME,
-  VAR_STARTTRANSFER_TIME,
-  VAR_SIZE_DOWNLOAD,
-  VAR_SIZE_UPLOAD,
-  VAR_SPEED_DOWNLOAD,
-  VAR_SPEED_UPLOAD,
-  VAR_HTTP_CODE,
-  VAR_HTTP_CODE_PROXY,
-  VAR_HEADER_SIZE,
-  VAR_NUM_HEADERS,
-  VAR_REQUEST_SIZE,
+  VAR_CONNECT_TIME,
+  VAR_CONTENT_TYPE,
+  VAR_EFFECTIVE_FILENAME,
   VAR_EFFECTIVE_METHOD,
   VAR_EFFECTIVE_URL,
-  VAR_CONTENT_TYPE,
-  VAR_NUM_CONNECTS,
-  VAR_REDIRECT_TIME,
-  VAR_REDIRECT_COUNT,
+  VAR_ERRORMSG,
+  VAR_EXITCODE,
   VAR_FTP_ENTRY_PATH,
-  VAR_REDIRECT_URL,
-  VAR_SSL_VERIFY_RESULT,
-  VAR_PROXY_SSL_VERIFY_RESULT,
-  VAR_EFFECTIVE_FILENAME,
-  VAR_PRIMARY_IP,
-  VAR_PRIMARY_PORT,
+  VAR_HEADER_SIZE,
+  VAR_HTTP_CODE,
+  VAR_HTTP_CODE_PROXY,
+  VAR_HTTP_VERSION,
+  VAR_INPUT_URL,
+  VAR_JSON,
   VAR_LOCAL_IP,
   VAR_LOCAL_PORT,
-  VAR_HTTP_VERSION,
+  VAR_NAMELOOKUP_TIME,
+  VAR_NUM_CONNECTS,
+  VAR_NUM_HEADERS,
+  VAR_ONERROR,
+  VAR_PRETRANSFER_TIME,
+  VAR_PRIMARY_IP,
+  VAR_PRIMARY_PORT,
+  VAR_PROXY_SSL_VERIFY_RESULT,
+  VAR_REDIRECT_COUNT,
+  VAR_REDIRECT_TIME,
+  VAR_REDIRECT_URL,
+  VAR_REQUEST_SIZE,
   VAR_SCHEME,
-  VAR_STDOUT,
+  VAR_SIZE_DOWNLOAD,
+  VAR_SIZE_UPLOAD,
+  VAR_SPEED_DOWNLOAD,
+  VAR_SPEED_UPLOAD,
+  VAR_SSL_VERIFY_RESULT,
+  VAR_STARTTRANSFER_TIME,
   VAR_STDERR,
-  VAR_JSON,
+  VAR_STDOUT,
+  VAR_TOTAL_TIME,
+  VAR_URLNUM,
   VAR_NUM_OF_VARS /* must be the last */
 } writeoutid;
 
@@ -82,6 +87,7 @@ struct writeoutvar {
   jsontype jsontype;
 };
 
-void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo);
+void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo,
+                 CURLcode exitcode);
 
 #endif /* HEADER_CURL_TOOL_WRITEOUT_H */