1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24#include "tool_setup.h" 25 26#include <sys/stat.h> 27 28#ifdef _WIN32 29#include <tchar.h> 30#endif 31 32#include <signal.h> 33 34#ifdef HAVE_FCNTL_H 35#include <fcntl.h> 36#endif 37 38#define ENABLE_CURLX_PRINTF 39/* use our own printf() functions */ 40#include "curlx.h" 41 42#include "tool_cfgable.h" 43#include "tool_doswin.h" 44#include "tool_msgs.h" 45#include "tool_operate.h" 46#include "tool_vms.h" 47#include "tool_main.h" 48#include "tool_libinfo.h" 49#include "tool_stderr.h" 50 51/* 52 * This is low-level hard-hacking memory leak tracking and similar. Using 53 * the library level code from this client-side is ugly, but we do this 54 * anyway for convenience. 55 */ 56#include "memdebug.h" /* keep this as LAST include */ 57 58#ifdef __VMS 59/* 60 * vms_show is a global variable, used in main() as parameter for 61 * function vms_special_exit() to allow proper curl tool exiting. 62 * Its value may be set in other tool_*.c source files thanks to 63 * forward declaration present in tool_vms.h 64 */ 65int vms_show = 0; 66#endif 67 68#ifdef __MINGW32__ 69/* 70 * There seems to be no way to escape "*" in command-line arguments with MinGW 71 * when command-line argument globbing is enabled under the MSYS shell, so turn 72 * it off. 73 */ 74extern int _CRT_glob; 75int _CRT_glob = 0; 76#endif /* __MINGW32__ */ 77 78/* if we build a static library for unit tests, there is no main() function */ 79#ifndef UNITTESTS 80 81#if defined(HAVE_PIPE) && defined(HAVE_FCNTL) 82/* 83 * Ensure that file descriptors 0, 1 and 2 (stdin, stdout, stderr) are 84 * open before starting to run. Otherwise, the first three network 85 * sockets opened by curl could be used for input sources, downloaded data 86 * or error logs as they will effectively be stdin, stdout and/or stderr. 87 * 88 * fcntl's F_GETFD instruction returns -1 if the file descriptor is closed, 89 * otherwise it returns "the file descriptor flags (which typically can only 90 * be FD_CLOEXEC, which is not set here). 91 */ 92static int main_checkfds(void) 93{ 94 int fd[2]; 95 while((fcntl(STDIN_FILENO, F_GETFD) == -1) || 96 (fcntl(STDOUT_FILENO, F_GETFD) == -1) || 97 (fcntl(STDERR_FILENO, F_GETFD) == -1)) 98 if(pipe(fd)) 99 return 1; 100 return 0; 101} 102#else 103#define main_checkfds() 0 104#endif 105 106#ifdef CURLDEBUG 107static void memory_tracking_init(void) 108{ 109 char *env; 110 /* if CURL_MEMDEBUG is set, this starts memory tracking message logging */ 111 env = curlx_getenv("CURL_MEMDEBUG"); 112 if(env) { 113 /* use the value as file name */ 114 char fname[CURL_MT_LOGFNAME_BUFSIZE]; 115 if(strlen(env) >= CURL_MT_LOGFNAME_BUFSIZE) 116 env[CURL_MT_LOGFNAME_BUFSIZE-1] = '\0'; 117 strcpy(fname, env); 118 curl_free(env); 119 curl_dbg_memdebug(fname); 120 /* this weird stuff here is to make curl_free() get called before 121 curl_dbg_memdebug() as otherwise memory tracking will log a free() 122 without an alloc! */ 123 } 124 /* if CURL_MEMLIMIT is set, this enables fail-on-alloc-number-N feature */ 125 env = curlx_getenv("CURL_MEMLIMIT"); 126 if(env) { 127 char *endptr; 128 long num = strtol(env, &endptr, 10); 129 if((endptr != env) && (endptr == env + strlen(env)) && (num > 0)) 130 curl_dbg_memlimit(num); 131 curl_free(env); 132 } 133} 134#else 135# define memory_tracking_init() Curl_nop_stmt 136#endif 137 138/* 139 * This is the main global constructor for the app. Call this before 140 * _any_ libcurl usage. If this fails, *NO* libcurl functions may be 141 * used, or havoc may be the result. 142 */ 143static CURLcode main_init(struct GlobalConfig *config) 144{ 145 CURLcode result = CURLE_OK; 146 147#if defined(__DJGPP__) || defined(__GO32__) 148 /* stop stat() wasting time */ 149 _djstat_flags |= _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE; 150#endif 151 152 /* Initialise the global config */ 153 config->showerror = FALSE; /* show errors when silent */ 154 config->styled_output = TRUE; /* enable detection */ 155 config->parallel_max = PARALLEL_DEFAULT; 156 157 /* Allocate the initial operate config */ 158 config->first = config->last = malloc(sizeof(struct OperationConfig)); 159 if(config->first) { 160 /* Perform the libcurl initialization */ 161 result = curl_global_init(CURL_GLOBAL_DEFAULT); 162 if(!result) { 163 /* Get information about libcurl */ 164 result = get_libcurl_info(); 165 166 if(!result) { 167 /* Initialise the config */ 168 config_init(config->first); 169 config->first->global = config; 170 } 171 else { 172 errorf(config, "error retrieving curl library information"); 173 free(config->first); 174 } 175 } 176 else { 177 errorf(config, "error initializing curl library"); 178 free(config->first); 179 } 180 } 181 else { 182 errorf(config, "error initializing curl"); 183 result = CURLE_FAILED_INIT; 184 } 185 186 return result; 187} 188 189static void free_globalconfig(struct GlobalConfig *config) 190{ 191 Curl_safefree(config->trace_dump); 192 193 if(config->trace_fopened && config->trace_stream) 194 fclose(config->trace_stream); 195 config->trace_stream = NULL; 196 197 Curl_safefree(config->libcurl); 198} 199 200/* 201 * This is the main global destructor for the app. Call this after 202 * _all_ libcurl usage is done. 203 */ 204static void main_free(struct GlobalConfig *config) 205{ 206 /* Cleanup the easy handle */ 207 /* Main cleanup */ 208 curl_global_cleanup(); 209 free_globalconfig(config); 210 211 /* Free the config structures */ 212 config_free(config->last); 213 config->first = NULL; 214 config->last = NULL; 215} 216 217/* 218** curl tool main function. 219*/ 220#ifdef _UNICODE 221#if defined(__GNUC__) 222/* GCC doesn't know about wmain() */ 223#pragma GCC diagnostic push 224#pragma GCC diagnostic ignored "-Wmissing-prototypes" 225#pragma GCC diagnostic ignored "-Wmissing-declarations" 226#endif 227int wmain(int argc, wchar_t *argv[]) 228#else 229int main(int argc, char *argv[]) 230#endif 231{ 232 CURLcode result = CURLE_OK; 233 struct GlobalConfig global; 234 memset(&global, 0, sizeof(global)); 235 236 tool_init_stderr(); 237 238#ifdef _WIN32 239 /* Undocumented diagnostic option to list the full paths of all loaded 240 modules. This is purposely pre-init. */ 241 if(argc == 2 && !_tcscmp(argv[1], _T("--dump-module-paths"))) { 242 struct curl_slist *item, *head = GetLoadedModulePaths(); 243 for(item = head; item; item = item->next) 244 printf("%s\n", item->data); 245 curl_slist_free_all(head); 246 return head ? 0 : 1; 247 } 248 /* win32_init must be called before other init routines. */ 249 result = win32_init(); 250 if(result) { 251 errorf(&global, "(%d) Windows-specific init failed", result); 252 return result; 253 } 254#endif 255 256 if(main_checkfds()) { 257 errorf(&global, "out of file descriptors"); 258 return CURLE_FAILED_INIT; 259 } 260 261#if defined(HAVE_SIGNAL) && defined(SIGPIPE) 262 (void)signal(SIGPIPE, SIG_IGN); 263#endif 264 265 /* Initialize memory tracking */ 266 memory_tracking_init(); 267 268 /* Initialize the curl library - do not call any libcurl functions before 269 this point */ 270 result = main_init(&global); 271 if(!result) { 272 /* Start our curl operation */ 273 result = operate(&global, argc, argv); 274 275 /* Perform the main cleanup */ 276 main_free(&global); 277 } 278 279#ifdef _WIN32 280 /* Flush buffers of all streams opened in write or update mode */ 281 fflush(NULL); 282#endif 283 284#ifdef __VMS 285 vms_special_exit(result, vms_show); 286#else 287 return (int)result; 288#endif 289} 290 291#ifdef _UNICODE 292#ifdef __GNUC__ 293#pragma GCC diagnostic pop 294#endif 295#endif 296 297#endif /* ndef UNITTESTS */ 298