1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2021, 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 ***************************************************************************/
22 #include "tool_setup.h"
23
24 #include <sys/stat.h>
25
26 #ifdef WIN32
27 #include <tchar.h>
28 #endif
29
30 #ifdef HAVE_SIGNAL_H
31 #include <signal.h>
32 #endif
33
34 #ifdef USE_NSS
35 #include <nspr.h>
36 #include <plarenas.h>
37 #endif
38
39 #define ENABLE_CURLX_PRINTF
40 /* use our own printf() functions */
41 #include "curlx.h"
42
43 #include "tool_cfgable.h"
44 #include "tool_convert.h"
45 #include "tool_doswin.h"
46 #include "tool_msgs.h"
47 #include "tool_operate.h"
48 #include "tool_panykey.h"
49 #include "tool_vms.h"
50 #include "tool_main.h"
51 #include "tool_libinfo.h"
52
53 /*
54 * This is low-level hard-hacking memory leak tracking and similar. Using
55 * the library level code from this client-side is ugly, but we do this
56 * anyway for convenience.
57 */
58 #include "memdebug.h" /* keep this as LAST include */
59
60 #ifdef __VMS
61 /*
62 * vms_show is a global variable, used in main() as parameter for
63 * function vms_special_exit() to allow proper curl tool exiting.
64 * Its value may be set in other tool_*.c source files thanks to
65 * forward declaration present in tool_vms.h
66 */
67 int vms_show = 0;
68 #endif
69
70 #ifdef __MINGW32__
71 /*
72 * There seems to be no way to escape "*" in command-line arguments with MinGW
73 * when command-line argument globbing is enabled under the MSYS shell, so turn
74 * it off.
75 */
76 int _CRT_glob = 0;
77 #endif /* __MINGW32__ */
78
79 /* if we build a static library for unit tests, there is no main() function */
80 #ifndef UNITTESTS
81
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 */
main_checkfds(void)88 static void main_checkfds(void)
89 {
90 #ifdef HAVE_PIPE
91 int fd[2] = { STDIN_FILENO, STDIN_FILENO };
92 while(fd[0] == STDIN_FILENO ||
93 fd[0] == STDOUT_FILENO ||
94 fd[0] == STDERR_FILENO ||
95 fd[1] == STDIN_FILENO ||
96 fd[1] == STDOUT_FILENO ||
97 fd[1] == STDERR_FILENO)
98 if(pipe(fd) < 0)
99 return; /* Out of handles. This isn't really a big problem now, but
100 will be when we try to create a socket later. */
101 close(fd[0]);
102 close(fd[1]);
103 #endif
104 }
105
106 #ifdef CURLDEBUG
memory_tracking_init(void)107 static 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_gdb_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 */
main_init(struct GlobalConfig * config)143 static 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 = -1; /* Will show errors */
154 config->errors = stderr; /* Default errors to stderr */
155 config->styled_output = TRUE; /* enable detection */
156 config->parallel_max = PARALLEL_DEFAULT;
157
158 /* Allocate the initial operate config */
159 config->first = config->last = malloc(sizeof(struct OperationConfig));
160 if(config->first) {
161 /* Perform the libcurl initialization */
162 result = curl_global_init(CURL_GLOBAL_DEFAULT);
163 if(!result) {
164 /* Get information about libcurl */
165 result = get_libcurl_info();
166
167 if(!result) {
168 /* Initialise the config */
169 config_init(config->first);
170 config->first->global = config;
171 }
172 else {
173 errorf(config, "error retrieving curl library information\n");
174 free(config->first);
175 }
176 }
177 else {
178 errorf(config, "error initializing curl library\n");
179 free(config->first);
180 }
181 }
182 else {
183 errorf(config, "error initializing curl\n");
184 result = CURLE_FAILED_INIT;
185 }
186
187 return result;
188 }
189
free_globalconfig(struct GlobalConfig * config)190 static void free_globalconfig(struct GlobalConfig *config)
191 {
192 Curl_safefree(config->trace_dump);
193
194 if(config->errors_fopened && config->errors)
195 fclose(config->errors);
196 config->errors = NULL;
197
198 if(config->trace_fopened && config->trace_stream)
199 fclose(config->trace_stream);
200 config->trace_stream = NULL;
201
202 Curl_safefree(config->libcurl);
203 }
204
205 /*
206 * This is the main global destructor for the app. Call this after
207 * _all_ libcurl usage is done.
208 */
main_free(struct GlobalConfig * config)209 static void main_free(struct GlobalConfig *config)
210 {
211 /* Cleanup the easy handle */
212 /* Main cleanup */
213 curl_global_cleanup();
214 convert_cleanup();
215 #ifdef USE_NSS
216 if(PR_Initialized()) {
217 /* prevent valgrind from reporting still reachable mem from NSRP arenas */
218 PL_ArenaFinish();
219 /* prevent valgrind from reporting possibly lost memory (fd cache, ...) */
220 PR_Cleanup();
221 }
222 #endif
223 free_globalconfig(config);
224
225 /* Free the config structures */
226 config_free(config->last);
227 config->first = NULL;
228 config->last = NULL;
229 }
230
231 /*
232 ** curl tool main function.
233 */
234 #ifdef _UNICODE
wmain(int argc,wchar_t * argv[])235 int wmain(int argc, wchar_t *argv[])
236 #else
237 int main(int argc, char *argv[])
238 #endif
239 {
240 CURLcode result = CURLE_OK;
241 struct GlobalConfig global;
242 memset(&global, 0, sizeof(global));
243
244 #ifdef WIN32
245 /* Undocumented diagnostic option to list the full paths of all loaded
246 modules. This is purposely pre-init. */
247 if(argc == 2 && !_tcscmp(argv[1], _T("--dump-module-paths"))) {
248 struct curl_slist *item, *head = GetLoadedModulePaths();
249 for(item = head; item; item = item->next)
250 printf("%s\n", item->data);
251 curl_slist_free_all(head);
252 return head ? 0 : 1;
253 }
254 /* win32_init must be called before other init routines. */
255 result = win32_init();
256 if(result) {
257 fprintf(stderr, "curl: (%d) Windows-specific init failed.\n", result);
258 return result;
259 }
260 #endif
261
262 main_checkfds();
263
264 #if defined(HAVE_SIGNAL) && defined(SIGPIPE)
265 (void)signal(SIGPIPE, SIG_IGN);
266 #endif
267
268 /* Initialize memory tracking */
269 memory_tracking_init();
270
271 /* Initialize the curl library - do not call any libcurl functions before
272 this point */
273 result = main_init(&global);
274 if(!result) {
275 /* Start our curl operation */
276 result = operate(&global, argc, argv);
277
278 /* Perform the main cleanup */
279 main_free(&global);
280 }
281
282 #ifdef __NOVELL_LIBC__
283 if(getenv("_IN_NETWARE_BASH_") == NULL)
284 tool_pressanykey();
285 #endif
286
287 #ifdef __VMS
288 vms_special_exit(result, vms_show);
289 #else
290 return (int)result;
291 #endif
292 }
293
294 #endif /* ndef UNITTESTS */
295