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
This commit is contained in:
Daniel Stenberg 2020-12-14 10:09:51 +01:00
parent ebdb5f23cc
commit 7a90ddf88f
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
7 changed files with 128 additions and 104 deletions

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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 */

View File

@ -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++;
}
}
}

View File

@ -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_CONNECT_TIME,
VAR_CONTENT_TYPE,
VAR_EFFECTIVE_FILENAME,
VAR_EFFECTIVE_METHOD,
VAR_EFFECTIVE_URL,
VAR_ERRORMSG,
VAR_EXITCODE,
VAR_FTP_ENTRY_PATH,
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_NAMELOOKUP_TIME,
VAR_NUM_CONNECTS,
VAR_NUM_HEADERS,
VAR_ONERROR,
VAR_PRETRANSFER_TIME,
VAR_STARTTRANSFER_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_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_EFFECTIVE_METHOD,
VAR_EFFECTIVE_URL,
VAR_CONTENT_TYPE,
VAR_NUM_CONNECTS,
VAR_REDIRECT_TIME,
VAR_REDIRECT_COUNT,
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_LOCAL_IP,
VAR_LOCAL_PORT,
VAR_HTTP_VERSION,
VAR_SCHEME,
VAR_STDOUT,
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 */