1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 1998 - 2019, 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.haxx.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 HAVE_SIGNAL_H |
27 | #include <signal.h> |
28 | #endif |
29 | |
30 | #ifdef USE_NSS |
31 | #include <nspr.h> |
32 | #include <plarenas.h> |
33 | #endif |
34 | |
35 | #define ENABLE_CURLX_PRINTF |
36 | /* use our own printf() functions */ |
37 | #include "curlx.h" |
38 | |
39 | #include "tool_cfgable.h" |
40 | #include "tool_convert.h" |
41 | #include "tool_doswin.h" |
42 | #include "tool_msgs.h" |
43 | #include "tool_operate.h" |
44 | #include "tool_panykey.h" |
45 | #include "tool_vms.h" |
46 | #include "tool_main.h" |
47 | #include "tool_libinfo.h" |
48 | |
49 | /* |
50 | * This is low-level hard-hacking memory leak tracking and similar. Using |
51 | * the library level code from this client-side is ugly, but we do this |
52 | * anyway for convenience. |
53 | */ |
54 | #include "memdebug.h" /* keep this as LAST include */ |
55 | |
56 | #ifdef __VMS |
57 | /* |
58 | * vms_show is a global variable, used in main() as parameter for |
59 | * function vms_special_exit() to allow proper curl tool exiting. |
60 | * Its value may be set in other tool_*.c source files thanks to |
61 | * forward declaration present in tool_vms.h |
62 | */ |
63 | int vms_show = 0; |
64 | #endif |
65 | |
66 | #ifdef __MINGW32__ |
67 | /* |
68 | * There seems to be no way to escape "*" in command-line arguments with MinGW |
69 | * when command-line argument globbing is enabled under the MSYS shell, so turn |
70 | * it off. |
71 | */ |
72 | int _CRT_glob = 0; |
73 | #endif /* __MINGW32__ */ |
74 | |
75 | /* if we build a static library for unit tests, there is no main() function */ |
76 | #ifndef UNITTESTS |
77 | |
78 | /* |
79 | * Ensure that file descriptors 0, 1 and 2 (stdin, stdout, stderr) are |
80 | * open before starting to run. Otherwise, the first three network |
81 | * sockets opened by curl could be used for input sources, downloaded data |
82 | * or error logs as they will effectively be stdin, stdout and/or stderr. |
83 | */ |
84 | static void main_checkfds(void) |
85 | { |
86 | #ifdef HAVE_PIPE |
87 | int fd[2] = { STDIN_FILENO, STDIN_FILENO }; |
88 | while(fd[0] == STDIN_FILENO || |
89 | fd[0] == STDOUT_FILENO || |
90 | fd[0] == STDERR_FILENO || |
91 | fd[1] == STDIN_FILENO || |
92 | fd[1] == STDOUT_FILENO || |
93 | fd[1] == STDERR_FILENO) |
94 | if(pipe(fd) < 0) |
95 | return; /* Out of handles. This isn't really a big problem now, but |
96 | will be when we try to create a socket later. */ |
97 | close(fd[0]); |
98 | close(fd[1]); |
99 | #endif |
100 | } |
101 | |
102 | #ifdef CURLDEBUG |
103 | static void memory_tracking_init(void) |
104 | { |
105 | char *env; |
106 | /* if CURL_MEMDEBUG is set, this starts memory tracking message logging */ |
107 | env = curlx_getenv("CURL_MEMDEBUG" ); |
108 | if(env) { |
109 | /* use the value as file name */ |
110 | char fname[CURL_MT_LOGFNAME_BUFSIZE]; |
111 | if(strlen(env) >= CURL_MT_LOGFNAME_BUFSIZE) |
112 | env[CURL_MT_LOGFNAME_BUFSIZE-1] = '\0'; |
113 | strcpy(fname, env); |
114 | curl_free(env); |
115 | curl_dbg_memdebug(fname); |
116 | /* this weird stuff here is to make curl_free() get called before |
117 | curl_gdb_memdebug() as otherwise memory tracking will log a free() |
118 | without an alloc! */ |
119 | } |
120 | /* if CURL_MEMLIMIT is set, this enables fail-on-alloc-number-N feature */ |
121 | env = curlx_getenv("CURL_MEMLIMIT" ); |
122 | if(env) { |
123 | char *endptr; |
124 | long num = strtol(env, &endptr, 10); |
125 | if((endptr != env) && (endptr == env + strlen(env)) && (num > 0)) |
126 | curl_dbg_memlimit(num); |
127 | curl_free(env); |
128 | } |
129 | } |
130 | #else |
131 | # define memory_tracking_init() Curl_nop_stmt |
132 | #endif |
133 | |
134 | /* |
135 | * This is the main global constructor for the app. Call this before |
136 | * _any_ libcurl usage. If this fails, *NO* libcurl functions may be |
137 | * used, or havoc may be the result. |
138 | */ |
139 | static CURLcode main_init(struct GlobalConfig *config) |
140 | { |
141 | CURLcode result = CURLE_OK; |
142 | |
143 | #if defined(__DJGPP__) || defined(__GO32__) |
144 | /* stop stat() wasting time */ |
145 | _djstat_flags |= _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE; |
146 | #endif |
147 | |
148 | /* Initialise the global config */ |
149 | config->showerror = -1; /* Will show errors */ |
150 | config->errors = stderr; /* Default errors to stderr */ |
151 | config->styled_output = TRUE; /* enable detection */ |
152 | config->parallel_max = PARALLEL_DEFAULT; |
153 | |
154 | /* Allocate the initial operate config */ |
155 | config->first = config->last = malloc(sizeof(struct OperationConfig)); |
156 | if(config->first) { |
157 | /* Perform the libcurl initialization */ |
158 | result = curl_global_init(CURL_GLOBAL_DEFAULT); |
159 | if(!result) { |
160 | /* Get information about libcurl */ |
161 | result = get_libcurl_info(); |
162 | |
163 | if(!result) { |
164 | /* Initialise the config */ |
165 | config_init(config->first); |
166 | config->first->global = config; |
167 | } |
168 | else { |
169 | helpf(stderr, "error retrieving curl library information\n" ); |
170 | free(config->first); |
171 | } |
172 | } |
173 | else { |
174 | helpf(stderr, "error initializing curl library\n" ); |
175 | free(config->first); |
176 | } |
177 | } |
178 | else { |
179 | helpf(stderr, "error initializing curl\n" ); |
180 | result = CURLE_FAILED_INIT; |
181 | } |
182 | |
183 | return result; |
184 | } |
185 | |
186 | static void free_globalconfig(struct GlobalConfig *config) |
187 | { |
188 | Curl_safefree(config->trace_dump); |
189 | |
190 | if(config->errors_fopened && config->errors) |
191 | fclose(config->errors); |
192 | config->errors = NULL; |
193 | |
194 | if(config->trace_fopened && config->trace_stream) |
195 | fclose(config->trace_stream); |
196 | config->trace_stream = NULL; |
197 | |
198 | Curl_safefree(config->libcurl); |
199 | } |
200 | |
201 | /* |
202 | * This is the main global destructor for the app. Call this after |
203 | * _all_ libcurl usage is done. |
204 | */ |
205 | static void main_free(struct GlobalConfig *config) |
206 | { |
207 | /* Cleanup the easy handle */ |
208 | /* Main cleanup */ |
209 | curl_global_cleanup(); |
210 | convert_cleanup(); |
211 | metalink_cleanup(); |
212 | #ifdef USE_NSS |
213 | if(PR_Initialized()) { |
214 | /* prevent valgrind from reporting still reachable mem from NSRP arenas */ |
215 | PL_ArenaFinish(); |
216 | /* prevent valgrind from reporting possibly lost memory (fd cache, ...) */ |
217 | PR_Cleanup(); |
218 | } |
219 | #endif |
220 | free_globalconfig(config); |
221 | |
222 | /* Free the config structures */ |
223 | config_free(config->last); |
224 | config->first = NULL; |
225 | config->last = NULL; |
226 | } |
227 | |
228 | #ifdef WIN32 |
229 | /* TerminalSettings for Windows */ |
230 | static struct TerminalSettings { |
231 | HANDLE hStdOut; |
232 | DWORD dwOutputMode; |
233 | } TerminalSettings; |
234 | |
235 | static void configure_terminal(void) |
236 | { |
237 | /* |
238 | * If we're running Windows, enable VT output. |
239 | * Note: VT mode flag can be set on any version of Windows, but VT |
240 | * processing only performed on Win10 >= Creators Update) |
241 | */ |
242 | |
243 | /* Define the VT flags in case we're building with an older SDK */ |
244 | #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING |
245 | #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 |
246 | #endif |
247 | |
248 | memset(&TerminalSettings, 0, sizeof(TerminalSettings)); |
249 | |
250 | /* Enable VT output */ |
251 | TerminalSettings.hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); |
252 | if((TerminalSettings.hStdOut != INVALID_HANDLE_VALUE) |
253 | && (GetConsoleMode(TerminalSettings.hStdOut, |
254 | &TerminalSettings.dwOutputMode))) { |
255 | SetConsoleMode(TerminalSettings.hStdOut, |
256 | TerminalSettings.dwOutputMode |
257 | | ENABLE_VIRTUAL_TERMINAL_PROCESSING); |
258 | } |
259 | } |
260 | #else |
261 | #define configure_terminal() |
262 | #endif |
263 | |
264 | static void restore_terminal(void) |
265 | { |
266 | #ifdef WIN32 |
267 | /* Restore Console output mode and codepage to whatever they were |
268 | * when Curl started */ |
269 | SetConsoleMode(TerminalSettings.hStdOut, TerminalSettings.dwOutputMode); |
270 | #endif |
271 | } |
272 | |
273 | /* |
274 | ** curl tool main function. |
275 | */ |
276 | int main(int argc, char *argv[]) |
277 | { |
278 | CURLcode result = CURLE_OK; |
279 | struct GlobalConfig global; |
280 | memset(&global, 0, sizeof(global)); |
281 | |
282 | /* Perform any platform-specific terminal configuration */ |
283 | configure_terminal(); |
284 | |
285 | main_checkfds(); |
286 | |
287 | #if defined(HAVE_SIGNAL) && defined(SIGPIPE) |
288 | (void)signal(SIGPIPE, SIG_IGN); |
289 | #endif |
290 | |
291 | /* Initialize memory tracking */ |
292 | memory_tracking_init(); |
293 | |
294 | /* Initialize the curl library - do not call any libcurl functions before |
295 | this point */ |
296 | result = main_init(&global); |
297 | |
298 | #ifdef WIN32 |
299 | /* Undocumented diagnostic option to list the full paths of all loaded |
300 | modules, regardless of whether or not initialization succeeded. */ |
301 | if(argc == 2 && !strcmp(argv[1], "--dump-module-paths" )) { |
302 | struct curl_slist *item, *head = GetLoadedModulePaths(); |
303 | for(item = head; item; item = item->next) { |
304 | printf("%s\n" , item->data); |
305 | } |
306 | curl_slist_free_all(head); |
307 | if(!result) |
308 | main_free(&global); |
309 | } |
310 | else |
311 | #endif /* WIN32 */ |
312 | if(!result) { |
313 | /* Start our curl operation */ |
314 | result = operate(&global, argc, argv); |
315 | |
316 | #ifdef __SYMBIAN32__ |
317 | if(global.showerror) |
318 | tool_pressanykey(); |
319 | #endif |
320 | |
321 | /* Perform the main cleanup */ |
322 | main_free(&global); |
323 | } |
324 | |
325 | /* Return the terminal to its original state */ |
326 | restore_terminal(); |
327 | |
328 | #ifdef __NOVELL_LIBC__ |
329 | if(getenv("_IN_NETWARE_BASH_" ) == NULL) |
330 | tool_pressanykey(); |
331 | #endif |
332 | |
333 | #ifdef __VMS |
334 | vms_special_exit(result, vms_show); |
335 | #else |
336 | return (int)result; |
337 | #endif |
338 | } |
339 | |
340 | #endif /* ndef UNITTESTS */ |
341 | |