system/curl: add a small curl-like HTTP client command

Add the "curl" NSH command: a command-line HTTP client built on top of
the netutils webclient library. It implements a subset of the real curl
options: GET and POST (and other methods via -X), custom request headers
(-H), a raw request body (-d, including -d @file), multipart/form-data
file uploads (-F name=@file), saving the response body to a file (-o)
and verbose output (-v). HTTP only (no HTTPS).

Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
This commit is contained in:
Jorge Guzman 2026-07-19 13:46:50 -03:00 committed by Alin Jerpelea
parent 98f683dea8
commit 865393d419
5 changed files with 996 additions and 0 deletions

View file

@ -0,0 +1,33 @@
# ##############################################################################
# apps/system/curl/CMakeLists.txt
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
# license agreements. See the NOTICE file distributed with this work for
# additional information regarding copyright ownership. The ASF licenses this
# file to you under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
#
# ##############################################################################
if(CONFIG_SYSTEM_CURL)
nuttx_add_application(
NAME
${CONFIG_SYSTEM_CURL_PROGNAME}
SRCS
curl_main.c
STACKSIZE
${CONFIG_SYSTEM_CURL_STACKSIZE}
PRIORITY
${CONFIG_SYSTEM_CURL_PRIORITY})
endif()

42
system/curl/Kconfig Normal file
View file

@ -0,0 +1,42 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#
config SYSTEM_CURL
tristate "curl HTTP command"
default n
depends on NETUTILS_WEBCLIENT
---help---
Enable the "curl" command: a small command-line HTTP client
supporting GET and POST (and other methods via -X), custom
headers (-H), a raw request body (-d, including -d @file) and
multipart/form-data file uploads (-F name=@file), built on top
of the netutils webclient library. HTTP only (no HTTPS).
if SYSTEM_CURL
config SYSTEM_CURL_PROGNAME
string "Program name"
default "curl"
---help---
This is the name of the program that will be used when the
NSH ELF program is installed.
config SYSTEM_CURL_PRIORITY
int "curl task priority"
default 100
config SYSTEM_CURL_STACKSIZE
int "curl stack size"
default DEFAULT_TASK_STACKSIZE
config SYSTEM_CURL_BUFFERSIZE
int "curl transfer buffer size"
default 512
---help---
Size of the buffer used to hold the request/response headers and
to stream body data. It must be large enough to hold the whole
request header line and the whole response header.
endif

25
system/curl/Make.defs Normal file
View file

@ -0,0 +1,25 @@
############################################################################
# apps/system/curl/Make.defs
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
ifneq ($(CONFIG_SYSTEM_CURL),)
CONFIGURED_APPS += $(APPDIR)/system/curl
endif

36
system/curl/Makefile Normal file
View file

@ -0,0 +1,36 @@
############################################################################
# apps/system/curl/Makefile
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
include $(APPDIR)/Make.defs
# curl built-in application info
PROGNAME = $(CONFIG_SYSTEM_CURL_PROGNAME)
PRIORITY = $(CONFIG_SYSTEM_CURL_PRIORITY)
STACKSIZE = $(CONFIG_SYSTEM_CURL_STACKSIZE)
MODULE = $(CONFIG_SYSTEM_CURL)
# curl command
MAINSRC = curl_main.c
include $(APPDIR)/Application.mk

860
system/curl/curl_main.c Normal file
View file

@ -0,0 +1,860 @@
/****************************************************************************
* apps/system/curl/curl_main.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <getopt.h>
#include <fcntl.h>
#include <errno.h>
#include <libgen.h>
#include "netutils/webclient.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_SYSTEM_CURL_BUFFERSIZE
# define CONFIG_SYSTEM_CURL_BUFFERSIZE 512
#endif
/* Limits for user-provided -H headers and -F form fields. */
#define CURL_MAX_HEADERS 12
#define CURL_MAX_FORMS 8
/* Each form field expands to at most 3 body chunks (part header, content,
* trailing CRLF), plus one final closing-boundary chunk.
*/
#define CURL_MAX_CHUNKS (CURL_MAX_FORMS * 3 + 1)
/****************************************************************************
* Private Types
****************************************************************************/
/* A single piece of the request body. It is either a block of memory
* (fd < 0, data in "mem") or the contents of an open file (fd >= 0).
*/
struct curl_chunk_s
{
int fd; /* >= 0: read from this file; < 0: use "mem" */
FAR char *mem; /* Heap buffer owned by this chunk (freed on cleanup) */
size_t len; /* Total number of bytes in this chunk */
size_t off; /* Bytes already consumed */
};
/* The full request body, streamed chunk by chunk. */
struct curl_body_s
{
struct curl_chunk_s chunks[CURL_MAX_CHUNKS];
int nchunks;
int cur;
};
/* Destination for the response body. */
struct curl_sink_s
{
int fd;
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: curl_guess_mime
*
* Description:
* Very small extension-to-MIME map for the common cases. Anything else
* defaults to application/octet-stream, exactly like curl's fallback.
*
****************************************************************************/
static FAR const char *curl_guess_mime(FAR const char *name)
{
FAR const char *dot = strrchr(name, '.');
if (dot != NULL)
{
if (strcasecmp(dot, ".png") == 0)
{
return "image/png";
}
if (strcasecmp(dot, ".jpg") == 0 || strcasecmp(dot, ".jpeg") == 0)
{
return "image/jpeg";
}
if (strcasecmp(dot, ".gif") == 0)
{
return "image/gif";
}
if (strcasecmp(dot, ".json") == 0)
{
return "application/json";
}
if (strcasecmp(dot, ".txt") == 0)
{
return "text/plain";
}
}
return "application/octet-stream";
}
/****************************************************************************
* Name: curl_add_mem_chunk
*
* Description:
* Append a memory chunk. Takes ownership of "mem" (heap allocated).
*
****************************************************************************/
static int curl_add_mem_chunk(FAR struct curl_body_s *body, FAR char *mem,
size_t len)
{
FAR struct curl_chunk_s *c;
if (body->nchunks >= CURL_MAX_CHUNKS)
{
return -ENOSPC;
}
c = &body->chunks[body->nchunks++];
c->fd = -1;
c->mem = mem;
c->len = len;
c->off = 0;
return OK;
}
/****************************************************************************
* Name: curl_add_file_chunk
*
* Description:
* Append a file chunk. Takes ownership of the open descriptor "fd".
*
****************************************************************************/
static int curl_add_file_chunk(FAR struct curl_body_s *body, int fd,
size_t len)
{
FAR struct curl_chunk_s *c;
if (body->nchunks >= CURL_MAX_CHUNKS)
{
return -ENOSPC;
}
c = &body->chunks[body->nchunks++];
c->fd = fd;
c->mem = NULL;
c->len = len;
c->off = 0;
return OK;
}
/****************************************************************************
* Name: curl_add_str_chunk
*
* Description:
* Convenience wrapper: duplicate a string and append it as a memory
* chunk.
*
****************************************************************************/
static int curl_add_str_chunk(FAR struct curl_body_s *body,
FAR const char *str, size_t len)
{
FAR char *dup = strdup(str);
int ret;
if (dup == NULL)
{
return -ENOMEM;
}
ret = curl_add_mem_chunk(body, dup, len);
if (ret < 0)
{
free(dup);
}
return ret;
}
/****************************************************************************
* Name: curl_body_cleanup
****************************************************************************/
static void curl_body_cleanup(FAR struct curl_body_s *body)
{
int i;
for (i = 0; i < body->nchunks; i++)
{
FAR struct curl_chunk_s *c = &body->chunks[i];
if (c->fd >= 0)
{
close(c->fd);
}
free(c->mem);
}
body->nchunks = 0;
}
/****************************************************************************
* Name: curl_body_total
****************************************************************************/
static size_t curl_body_total(FAR const struct curl_body_s *body)
{
size_t total = 0;
int i;
for (i = 0; i < body->nchunks; i++)
{
total += body->chunks[i].len;
}
return total;
}
/****************************************************************************
* Name: curl_body_callback
*
* Description:
* webclient request-body provider. Fills "buffer" with up to *sizep bytes
* pulled sequentially from the chunk list. Called repeatedly by webclient
* until exactly bodylen bytes have been produced.
*
****************************************************************************/
static int curl_body_callback(FAR void *buffer, FAR size_t *sizep,
FAR const void * FAR *datap, size_t reqsize,
FAR void *arg)
{
FAR struct curl_body_s *body = (FAR struct curl_body_s *)arg;
FAR char *out = (FAR char *)buffer;
size_t cap = *sizep;
size_t done = 0;
(void)reqsize;
(void)datap;
while (done < cap && body->cur < body->nchunks)
{
FAR struct curl_chunk_s *c = &body->chunks[body->cur];
size_t remain = c->len - c->off;
size_t want;
if (remain == 0)
{
body->cur++;
continue;
}
want = cap - done;
if (want > remain)
{
want = remain;
}
if (c->fd < 0)
{
memcpy(out + done, c->mem + c->off, want);
c->off += want;
done += want;
}
else
{
ssize_t n = read(c->fd, out + done, want);
if (n < 0)
{
return -errno;
}
if (n == 0)
{
/* Short file: the pre-computed Content-Length can no longer
* be honoured, so fail rather than send a truncated body.
*/
return -EIO;
}
c->off += n;
done += n;
}
}
*sizep = done;
return OK;
}
/****************************************************************************
* Name: curl_sink_callback
*
* Description:
* webclient response-body consumer. Writes each chunk to the destination
* file descriptor (stdout or the -o file).
*
****************************************************************************/
static int curl_sink_callback(FAR char **buffer, int offset, int datend,
FAR int *buflen, FAR void *arg)
{
FAR struct curl_sink_s *sink = (FAR struct curl_sink_s *)arg;
int len = datend - offset;
(void)buflen;
while (len > 0)
{
ssize_t n = write(sink->fd, *buffer + offset, len);
if (n < 0)
{
return -errno;
}
offset += n;
len -= n;
}
return OK;
}
/****************************************************************************
* Name: curl_header_callback
****************************************************************************/
static int curl_header_callback(FAR const char *line, bool truncated,
FAR void *arg)
{
(void)truncated;
(void)arg;
fprintf(stderr, "< %s\n", line);
return OK;
}
/****************************************************************************
* Name: curl_add_form
*
* Description:
* Parse a single -F "name=value" or -F "name=@path[;type=mime]" spec and
* append the corresponding multipart chunks to the body.
*
****************************************************************************/
static int curl_add_form(FAR struct curl_body_s *body,
FAR const char *boundary, FAR const char *spec)
{
char hdr[256];
FAR const char *eq;
FAR const char *value;
FAR char name[64];
size_t namelen;
int len;
int ret;
eq = strchr(spec, '=');
if (eq == NULL)
{
fprintf(stderr, "curl: bad -F spec '%s' (want name=value)\n", spec);
return -EINVAL;
}
namelen = eq - spec;
if (namelen >= sizeof(name))
{
return -E2BIG;
}
memcpy(name, spec, namelen);
name[namelen] = '\0';
value = eq + 1;
if (value[0] == '@')
{
/* File upload: name=@path[;type=mime] */
FAR const char *path = value + 1;
FAR const char *mime;
FAR char *typesep;
char pathbuf[CONFIG_WEBCLIENT_MAXFILENAME];
struct stat st;
int fd;
strlcpy(pathbuf, path, sizeof(pathbuf));
typesep = strchr(pathbuf, ';');
if (typesep != NULL && strncmp(typesep, ";type=", 6) == 0)
{
*typesep = '\0';
mime = typesep + 6;
}
else
{
mime = curl_guess_mime(pathbuf);
}
fd = open(pathbuf, O_RDONLY | O_CLOEXEC);
if (fd < 0)
{
fprintf(stderr, "curl: cannot open '%s': %d\n", pathbuf, errno);
return -errno;
}
if (fstat(fd, &st) < 0)
{
fprintf(stderr, "curl: stat '%s' failed: %d\n", pathbuf, errno);
close(fd);
return -errno;
}
len = snprintf(hdr, sizeof(hdr),
"--%s\r\n"
"Content-Disposition: form-data; name=\"%s\"; "
"filename=\"%s\"\r\n"
"Content-Type: %s\r\n\r\n",
boundary, name, basename(pathbuf), mime);
if (len < 0 || len >= sizeof(hdr))
{
close(fd);
return -E2BIG;
}
ret = curl_add_str_chunk(body, hdr, len);
if (ret < 0)
{
close(fd);
return ret;
}
ret = curl_add_file_chunk(body, fd, st.st_size);
if (ret < 0)
{
close(fd);
return ret;
}
}
else
{
/* Plain text field: name=value */
len = snprintf(hdr, sizeof(hdr),
"--%s\r\n"
"Content-Disposition: form-data; name=\"%s\"\r\n\r\n",
boundary, name);
if (len < 0 || len >= sizeof(hdr))
{
return -E2BIG;
}
ret = curl_add_str_chunk(body, hdr, len);
if (ret < 0)
{
return ret;
}
ret = curl_add_str_chunk(body, value, strlen(value));
if (ret < 0)
{
return ret;
}
}
/* Trailing CRLF that terminates this part's content. */
return curl_add_str_chunk(body, "\r\n", 2);
}
/****************************************************************************
* Name: curl_usage
****************************************************************************/
static void curl_usage(FAR const char *progname)
{
fprintf(stderr,
"Usage: %s [options] <url>\n"
" -X <method> HTTP method (default GET, or POST with -d/-F).\n"
" -d <data> Request body. Use -d @file to read from a file.\n"
" --data-binary <d> Like -d but never interprets @-less data.\n"
" -F <name=content> multipart/form-data field. Use name=@file to\n"
" upload a file. Repeatable. Implies POST.\n"
" -H <header> Add a request header. Repeatable.\n"
" -o, --output <f> Write response body to <f> instead of stdout.\n"
" -v Verbose: print response headers to stderr.\n"
" -h Show this help.\n"
"\n"
"Only http:// URLs are supported (no HTTPS). A scheme-less URL such\n"
"as host:port/path is treated as http://host:port/path.\n",
progname);
}
/****************************************************************************
* Public Functions
****************************************************************************/
int main(int argc, FAR char *argv[])
{
FAR const char *headers[CURL_MAX_HEADERS];
FAR const char *forms[CURL_MAX_FORMS];
FAR const char *method = NULL;
FAR const char *data = NULL;
FAR const char *rawurl = NULL;
FAR const char *outfile = NULL;
struct webclient_context ctx;
struct curl_body_s body;
struct curl_sink_s sink;
char boundary[48];
/* Sized so the compiler can prove the Content-Type snprintf never
* truncates: the fixed prefix (including its NUL) plus the largest
* possible boundary string.
*/
char ctype[sizeof("Content-Type: multipart/form-data; boundary=") +
sizeof(boundary)];
char urlbuf[CONFIG_WEBCLIENT_MAXHOSTNAME + CONFIG_WEBCLIENT_MAXFILENAME +
16];
FAR const char *url;
FAR char *buffer = NULL;
unsigned int nheaders = 0;
unsigned int nforms = 0;
bool have_ctype = false;
bool verbose = false;
int ret;
int opt;
unsigned int i;
struct option long_options[] =
{
{"data", required_argument, NULL, 'd'},
{"data-binary", required_argument, NULL, 'B'},
{"form", required_argument, NULL, 'F'},
{"header", required_argument, NULL, 'H'},
{"request", required_argument, NULL, 'X'},
{"output", required_argument, NULL, 'o'},
{"verbose", no_argument, NULL, 'v'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
body.nchunks = 0;
body.cur = 0;
while ((opt = getopt_long(argc, argv, "X:d:F:H:o:vh",
long_options, NULL)) != ERROR)
{
switch (opt)
{
case 'X':
method = optarg;
break;
case 'd':
case 'B':
data = optarg;
break;
case 'F':
if (nforms >= CURL_MAX_FORMS)
{
fprintf(stderr, "curl: too many -F fields (max %d)\n",
CURL_MAX_FORMS);
return EXIT_FAILURE;
}
forms[nforms++] = optarg;
break;
case 'H':
if (nheaders >= CURL_MAX_HEADERS - 1)
{
fprintf(stderr, "curl: too many -H headers (max %d)\n",
CURL_MAX_HEADERS - 1);
return EXIT_FAILURE;
}
if (strncasecmp(optarg, "Content-Type:", 13) == 0)
{
have_ctype = true;
}
headers[nheaders++] = optarg;
break;
case 'o':
outfile = optarg;
break;
case 'v':
verbose = true;
break;
case 'h':
curl_usage(argv[0]);
return EXIT_SUCCESS;
case '?':
default:
curl_usage(argv[0]);
return EXIT_FAILURE;
}
}
if (optind >= argc)
{
fprintf(stderr, "curl: missing URL\n");
curl_usage(argv[0]);
return EXIT_FAILURE;
}
rawurl = argv[optind];
if (data != NULL && nforms > 0)
{
fprintf(stderr, "curl: -d and -F cannot be combined\n");
return EXIT_FAILURE;
}
/* Prepend a default http:// scheme when the URL has none. */
if (strstr(rawurl, "://") == NULL)
{
snprintf(urlbuf, sizeof(urlbuf), "http://%s", rawurl);
url = urlbuf;
}
else
{
url = rawurl;
}
/* Default method: POST when sending a body, GET otherwise. */
if (method == NULL)
{
method = (data != NULL || nforms > 0) ? "POST" : "GET";
}
/* Build the request body. */
if (nforms > 0)
{
/* multipart/form-data: derive a boundary and add the Content-Type
* header ourselves.
*/
snprintf(boundary, sizeof(boundary), "----NuttXCurl%08x%08x",
getpid(), nforms);
for (i = 0; i < nforms; i++)
{
ret = curl_add_form(&body, boundary, forms[i]);
if (ret < 0)
{
goto errout_body;
}
}
/* Closing boundary. */
ret = curl_add_str_chunk(&body, "--", 2);
if (ret >= 0)
{
ret = curl_add_str_chunk(&body, boundary, strlen(boundary));
}
if (ret >= 0)
{
ret = curl_add_str_chunk(&body, "--\r\n", 4);
}
if (ret < 0)
{
goto errout_body;
}
if (nheaders < CURL_MAX_HEADERS && !have_ctype)
{
snprintf(ctype, sizeof(ctype),
"Content-Type: multipart/form-data; boundary=%s",
boundary);
headers[nheaders++] = ctype;
have_ctype = true;
}
}
else if (data != NULL && data[0] == '@')
{
/* -d @file : stream the file as the raw request body. */
struct stat st;
int fd = open(data + 1, O_RDONLY | O_CLOEXEC);
if (fd < 0)
{
fprintf(stderr, "curl: cannot open '%s': %d\n", data + 1, errno);
ret = -errno;
goto errout_body;
}
if (fstat(fd, &st) < 0)
{
fprintf(stderr, "curl: stat '%s' failed: %d\n", data + 1, errno);
ret = -errno;
close(fd);
goto errout_body;
}
ret = curl_add_file_chunk(&body, fd, st.st_size);
if (ret < 0)
{
close(fd);
goto errout_body;
}
}
else if (data != NULL)
{
/* -d 'literal' : the string itself is the body. */
ret = curl_add_str_chunk(&body, data, strlen(data));
if (ret < 0)
{
goto errout_body;
}
}
/* curl adds a form Content-Type by default for -d without an explicit
* one. JSON callers are expected to pass -H "Content-Type: ...".
*/
if (data != NULL && !have_ctype && nheaders < CURL_MAX_HEADERS)
{
headers[nheaders++] =
"Content-Type: application/x-www-form-urlencoded";
}
buffer = malloc(CONFIG_SYSTEM_CURL_BUFFERSIZE);
if (buffer == NULL)
{
fprintf(stderr, "curl: out of memory\n");
ret = -ENOMEM;
goto errout_body;
}
/* Open the destination for the response body. */
if (outfile != NULL)
{
sink.fd = open(outfile,
O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
if (sink.fd < 0)
{
fprintf(stderr, "curl: cannot open '%s': %d\n", outfile, errno);
ret = -errno;
goto errout_buffer;
}
}
else
{
sink.fd = STDOUT_FILENO;
}
/* Assemble and run the request. */
webclient_set_defaults(&ctx);
ctx.method = method;
ctx.url = url;
ctx.buffer = buffer;
ctx.buflen = CONFIG_SYSTEM_CURL_BUFFERSIZE;
ctx.sink_callback = curl_sink_callback;
ctx.sink_callback_arg = &sink;
if (nheaders > 0)
{
ctx.headers = headers;
ctx.nheaders = nheaders;
}
if (verbose)
{
ctx.header_callback = curl_header_callback;
fprintf(stderr, "> %s %s\n", method, url);
for (i = 0; i < nheaders; i++)
{
fprintf(stderr, "> %s\n", headers[i]);
}
}
if (body.nchunks > 0)
{
ctx.body_callback = curl_body_callback;
ctx.body_callback_arg = &body;
ctx.bodylen = curl_body_total(&body);
}
ret = webclient_perform(&ctx);
if (ret < 0)
{
fprintf(stderr, "curl: request failed: %d\n", ret);
}
else
{
fprintf(stderr, "curl: HTTP %u\n", ctx.http_status);
}
if (sink.fd != STDOUT_FILENO)
{
close(sink.fd);
}
errout_buffer:
free(buffer);
errout_body:
curl_body_cleanup(&body);
return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}