| 1 | /* mswindows.c -- Windows-specific support
|
|---|
| 2 | Copyright (C) 1995, 1996, 1997, 1998, 2004
|
|---|
| 3 | Free Software Foundation, Inc.
|
|---|
| 4 |
|
|---|
| 5 | This file is part of GNU Wget.
|
|---|
| 6 |
|
|---|
| 7 | GNU Wget is free software; you can redistribute it and/or modify
|
|---|
| 8 | it under the terms of the GNU General Public License as published by
|
|---|
| 9 | the Free Software Foundation; either version 2 of the License, or
|
|---|
| 10 | (at your option) any later version.
|
|---|
| 11 |
|
|---|
| 12 | GNU Wget is distributed in the hope that it will be useful,
|
|---|
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 15 | GNU General Public License for more details.
|
|---|
| 16 |
|
|---|
| 17 | You should have received a copy of the GNU General Public License
|
|---|
| 18 | along with Wget; if not, write to the Free Software
|
|---|
| 19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|---|
| 20 |
|
|---|
| 21 | In addition, as a special exception, the Free Software Foundation
|
|---|
| 22 | gives permission to link the code of its release of Wget with the
|
|---|
| 23 | OpenSSL project's "OpenSSL" library (or with modified versions of it
|
|---|
| 24 | that use the same license as the "OpenSSL" library), and distribute
|
|---|
| 25 | the linked executables. You must obey the GNU General Public License
|
|---|
| 26 | in all respects for all of the code used other than "OpenSSL". If you
|
|---|
| 27 | modify this file, you may extend this exception to your version of the
|
|---|
| 28 | file, but you are not obligated to do so. If you do not wish to do
|
|---|
| 29 | so, delete this exception statement from your version. */
|
|---|
| 30 |
|
|---|
| 31 | #include <config.h>
|
|---|
| 32 |
|
|---|
| 33 | #include <stdio.h>
|
|---|
| 34 | #include <stdlib.h>
|
|---|
| 35 | #include <string.h>
|
|---|
| 36 | #include <assert.h>
|
|---|
| 37 | #include <errno.h>
|
|---|
| 38 | #include <math.h>
|
|---|
| 39 |
|
|---|
| 40 | #ifdef HACK_BCC_UTIME_BUG
|
|---|
| 41 | # include <io.h>
|
|---|
| 42 | # include <fcntl.h>
|
|---|
| 43 | # ifdef HAVE_UTIME_H
|
|---|
| 44 | # include <utime.h>
|
|---|
| 45 | # endif
|
|---|
| 46 | # ifdef HAVE_SYS_UTIME_H
|
|---|
| 47 | # include <sys/utime.h>
|
|---|
| 48 | # endif
|
|---|
| 49 | #endif
|
|---|
| 50 |
|
|---|
| 51 | #define INHIBIT_WRAP /* avoid wrapping of socket, bind, ... */
|
|---|
| 52 |
|
|---|
| 53 | #include "wget.h"
|
|---|
| 54 | #include "utils.h"
|
|---|
| 55 | #include "url.h"
|
|---|
| 56 |
|
|---|
| 57 | #ifndef errno
|
|---|
| 58 | extern int errno;
|
|---|
| 59 | #endif
|
|---|
| 60 |
|
|---|
| 61 | #ifndef ES_SYSTEM_REQUIRED
|
|---|
| 62 | #define ES_SYSTEM_REQUIRED 0x00000001
|
|---|
| 63 | #endif
|
|---|
| 64 |
|
|---|
| 65 | #ifndef ES_CONTINUOUS
|
|---|
| 66 | #define ES_CONTINUOUS 0x80000000
|
|---|
| 67 | #endif
|
|---|
| 68 |
|
|---|
| 69 |
|
|---|
| 70 | /* Defined in log.c. */
|
|---|
| 71 | void log_request_redirect_output PARAMS ((const char *));
|
|---|
| 72 |
|
|---|
| 73 | /* Windows version of xsleep in utils.c. */
|
|---|
| 74 |
|
|---|
| 75 | void
|
|---|
| 76 | xsleep (double seconds)
|
|---|
| 77 | {
|
|---|
| 78 | #ifdef HAVE_USLEEP
|
|---|
| 79 | if (seconds > 1000)
|
|---|
| 80 | {
|
|---|
| 81 | /* Explained in utils.c. */
|
|---|
| 82 | sleep (seconds);
|
|---|
| 83 | seconds -= (long) seconds;
|
|---|
| 84 | }
|
|---|
| 85 | usleep (seconds * 1000000L);
|
|---|
| 86 | #else /* not HAVE_USLEEP */
|
|---|
| 87 | SleepEx ((DWORD) (seconds * 1000 + .5), FALSE);
|
|---|
| 88 | #endif /* not HAVE_USLEEP */
|
|---|
| 89 | }
|
|---|
| 90 |
|
|---|
| 91 | void
|
|---|
| 92 | windows_main (int *argc, char **argv, char **exec_name)
|
|---|
| 93 | {
|
|---|
| 94 | char *p;
|
|---|
| 95 |
|
|---|
| 96 | /* Remove .EXE from filename if it has one. */
|
|---|
| 97 | *exec_name = xstrdup (*exec_name);
|
|---|
| 98 | p = strrchr (*exec_name, '.');
|
|---|
| 99 | if (p)
|
|---|
| 100 | *p = '\0';
|
|---|
| 101 | }
|
|---|
| 102 | |
|---|
| 103 |
|
|---|
| 104 | static void
|
|---|
| 105 | ws_cleanup (void)
|
|---|
| 106 | {
|
|---|
| 107 | WSACleanup ();
|
|---|
| 108 | }
|
|---|
| 109 |
|
|---|
| 110 | static void
|
|---|
| 111 | ws_hangup (const char *reason)
|
|---|
| 112 | {
|
|---|
| 113 | fprintf (stderr, _("Continuing in background.\n"));
|
|---|
| 114 | log_request_redirect_output (reason);
|
|---|
| 115 |
|
|---|
| 116 | /* Detach process from the current console. Under Windows 9x, if we
|
|---|
| 117 | were launched from a 16-bit process (which is usually the case;
|
|---|
| 118 | command.com is 16-bit) the parent process should resume right away.
|
|---|
| 119 | Under NT or if launched from a 32-process under 9x, this is a futile
|
|---|
| 120 | gesture as the parent will wait for us to terminate before resuming. */
|
|---|
| 121 | FreeConsole ();
|
|---|
| 122 | }
|
|---|
| 123 |
|
|---|
| 124 | /* Construct the name for a named section (a.k.a. `file mapping') object.
|
|---|
| 125 | The returned string is dynamically allocated and needs to be xfree()'d. */
|
|---|
| 126 | static char *
|
|---|
| 127 | make_section_name (DWORD pid)
|
|---|
| 128 | {
|
|---|
| 129 | return aprintf ("gnu_wget_fake_fork_%lu", pid);
|
|---|
| 130 | }
|
|---|
| 131 |
|
|---|
| 132 | /* This structure is used to hold all the data that is exchanged between
|
|---|
| 133 | parent and child. */
|
|---|
| 134 | struct fake_fork_info
|
|---|
| 135 | {
|
|---|
| 136 | HANDLE event;
|
|---|
| 137 | int logfile_changed;
|
|---|
| 138 | char lfilename[MAX_PATH + 1];
|
|---|
| 139 | };
|
|---|
| 140 |
|
|---|
| 141 | /* Determines if we are the child and if so performs the child logic.
|
|---|
| 142 | Return values:
|
|---|
| 143 | < 0 error
|
|---|
| 144 | 0 parent
|
|---|
| 145 | > 0 child
|
|---|
| 146 | */
|
|---|
| 147 | static int
|
|---|
| 148 | fake_fork_child (void)
|
|---|
| 149 | {
|
|---|
| 150 | HANDLE section, event;
|
|---|
| 151 | struct fake_fork_info *info;
|
|---|
| 152 | char *name;
|
|---|
| 153 |
|
|---|
| 154 | name = make_section_name (GetCurrentProcessId ());
|
|---|
| 155 | section = OpenFileMapping (FILE_MAP_WRITE, FALSE, name);
|
|---|
| 156 | xfree (name);
|
|---|
| 157 | /* It seems that Windows 9x and NT set last-error inconsistently when
|
|---|
| 158 | OpenFileMapping() fails; so we assume it failed because the section
|
|---|
| 159 | object does not exist. */
|
|---|
| 160 | if (!section)
|
|---|
| 161 | return 0; /* We are the parent. */
|
|---|
| 162 |
|
|---|
| 163 | info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
|
|---|
| 164 | if (!info)
|
|---|
| 165 | {
|
|---|
| 166 | CloseHandle (section);
|
|---|
| 167 | return -1;
|
|---|
| 168 | }
|
|---|
| 169 |
|
|---|
| 170 | event = info->event;
|
|---|
| 171 |
|
|---|
| 172 | info->logfile_changed = 0;
|
|---|
| 173 | if (!opt.lfilename)
|
|---|
| 174 | {
|
|---|
| 175 | /* See utils:fork_to_background for explanation. */
|
|---|
| 176 | FILE *new_log_fp = unique_create (DEFAULT_LOGFILE, 0, &opt.lfilename);
|
|---|
| 177 | if (new_log_fp)
|
|---|
| 178 | {
|
|---|
| 179 | info->logfile_changed = 1;
|
|---|
| 180 | strncpy (info->lfilename, opt.lfilename, sizeof (info->lfilename));
|
|---|
| 181 | info->lfilename[sizeof (info->lfilename) - 1] = '\0';
|
|---|
| 182 | fclose (new_log_fp);
|
|---|
| 183 | }
|
|---|
| 184 | }
|
|---|
| 185 |
|
|---|
| 186 | UnmapViewOfFile (info);
|
|---|
| 187 | CloseHandle (section);
|
|---|
| 188 |
|
|---|
| 189 | /* Inform the parent that we've done our part. */
|
|---|
| 190 | if (!SetEvent (event))
|
|---|
| 191 | return -1;
|
|---|
| 192 |
|
|---|
| 193 | CloseHandle (event);
|
|---|
| 194 | return 1; /* We are the child. */
|
|---|
| 195 | }
|
|---|
| 196 |
|
|---|
| 197 | /* Windows doesn't support the fork() call; so we fake it by invoking
|
|---|
| 198 | another copy of Wget with the same arguments with which we were
|
|---|
| 199 | invoked. The child copy of Wget should perform the same initialization
|
|---|
| 200 | sequence as the parent; so we should have two processes that are
|
|---|
| 201 | essentially identical. We create a specially named section object that
|
|---|
| 202 | allows the child to distinguish itself from the parent and is used to
|
|---|
| 203 | exchange information between the two processes. We use an event object
|
|---|
| 204 | for synchronization. */
|
|---|
| 205 | static void
|
|---|
| 206 | fake_fork (void)
|
|---|
| 207 | {
|
|---|
| 208 | char exe[MAX_PATH + 1];
|
|---|
| 209 | DWORD exe_len, le;
|
|---|
| 210 | SECURITY_ATTRIBUTES sa;
|
|---|
| 211 | HANDLE section, event, h[2];
|
|---|
| 212 | STARTUPINFO si;
|
|---|
| 213 | PROCESS_INFORMATION pi;
|
|---|
| 214 | struct fake_fork_info *info;
|
|---|
| 215 | char *name;
|
|---|
| 216 | BOOL rv;
|
|---|
| 217 |
|
|---|
| 218 | section = pi.hProcess = pi.hThread = NULL;
|
|---|
| 219 |
|
|---|
| 220 | /* Get the fully qualified name of our executable. This is more reliable
|
|---|
| 221 | than using argv[0]. */
|
|---|
| 222 | exe_len = GetModuleFileName (GetModuleHandle (NULL), exe, sizeof (exe));
|
|---|
| 223 | if (!exe_len || (exe_len >= sizeof (exe)))
|
|---|
| 224 | return;
|
|---|
| 225 |
|
|---|
| 226 | sa.nLength = sizeof (sa);
|
|---|
| 227 | sa.lpSecurityDescriptor = NULL;
|
|---|
| 228 | sa.bInheritHandle = TRUE;
|
|---|
| 229 |
|
|---|
| 230 | /* Create an anonymous inheritable event object that starts out
|
|---|
| 231 | non-signaled. */
|
|---|
| 232 | event = CreateEvent (&sa, FALSE, FALSE, NULL);
|
|---|
| 233 | if (!event)
|
|---|
| 234 | return;
|
|---|
| 235 |
|
|---|
| 236 | /* Create the child process detached form the current console and in a
|
|---|
| 237 | suspended state. */
|
|---|
| 238 | xzero (si);
|
|---|
| 239 | si.cb = sizeof (si);
|
|---|
| 240 | rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE,
|
|---|
| 241 | CREATE_SUSPENDED | DETACHED_PROCESS,
|
|---|
| 242 | NULL, NULL, &si, &pi);
|
|---|
| 243 | if (!rv)
|
|---|
| 244 | goto cleanup;
|
|---|
| 245 |
|
|---|
| 246 | /* Create a named section object with a name based on the process id of
|
|---|
| 247 | the child. */
|
|---|
| 248 | name = make_section_name (pi.dwProcessId);
|
|---|
| 249 | section =
|
|---|
| 250 | CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
|
|---|
| 251 | sizeof (struct fake_fork_info), name);
|
|---|
| 252 | le = GetLastError();
|
|---|
| 253 | xfree (name);
|
|---|
| 254 | /* Fail if the section object already exists (should not happen). */
|
|---|
| 255 | if (!section || (le == ERROR_ALREADY_EXISTS))
|
|---|
| 256 | {
|
|---|
| 257 | rv = FALSE;
|
|---|
| 258 | goto cleanup;
|
|---|
| 259 | }
|
|---|
| 260 |
|
|---|
| 261 | /* Copy the event handle into the section object. */
|
|---|
| 262 | info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
|
|---|
| 263 | if (!info)
|
|---|
| 264 | {
|
|---|
| 265 | rv = FALSE;
|
|---|
| 266 | goto cleanup;
|
|---|
| 267 | }
|
|---|
| 268 |
|
|---|
| 269 | info->event = event;
|
|---|
| 270 |
|
|---|
| 271 | UnmapViewOfFile (info);
|
|---|
| 272 |
|
|---|
| 273 | /* Start the child process. */
|
|---|
| 274 | rv = ResumeThread (pi.hThread);
|
|---|
| 275 | if (!rv)
|
|---|
| 276 | {
|
|---|
| 277 | TerminateProcess (pi.hProcess, (DWORD) -1);
|
|---|
| 278 | goto cleanup;
|
|---|
| 279 | }
|
|---|
| 280 |
|
|---|
| 281 | /* Wait for the child to signal to us that it has done its part. If it
|
|---|
| 282 | terminates before signaling us it's an error. */
|
|---|
| 283 |
|
|---|
| 284 | h[0] = event;
|
|---|
| 285 | h[1] = pi.hProcess;
|
|---|
| 286 | rv = WAIT_OBJECT_0 == WaitForMultipleObjects (2, h, FALSE, 5 * 60 * 1000);
|
|---|
| 287 | if (!rv)
|
|---|
| 288 | goto cleanup;
|
|---|
| 289 |
|
|---|
| 290 | info = MapViewOfFile (section, FILE_MAP_READ, 0, 0, 0);
|
|---|
| 291 | if (!info)
|
|---|
| 292 | {
|
|---|
| 293 | rv = FALSE;
|
|---|
| 294 | goto cleanup;
|
|---|
| 295 | }
|
|---|
| 296 |
|
|---|
| 297 | /* Ensure string is properly terminated. */
|
|---|
| 298 | if (info->logfile_changed &&
|
|---|
| 299 | !memchr (info->lfilename, '\0', sizeof (info->lfilename)))
|
|---|
| 300 | {
|
|---|
| 301 | rv = FALSE;
|
|---|
| 302 | goto cleanup;
|
|---|
| 303 | }
|
|---|
| 304 |
|
|---|
| 305 | printf (_("Continuing in background, pid %lu.\n"), pi.dwProcessId);
|
|---|
| 306 | if (info->logfile_changed)
|
|---|
| 307 | printf (_("Output will be written to `%s'.\n"), info->lfilename);
|
|---|
| 308 |
|
|---|
| 309 | UnmapViewOfFile (info);
|
|---|
| 310 |
|
|---|
| 311 | cleanup:
|
|---|
| 312 |
|
|---|
| 313 | if (event)
|
|---|
| 314 | CloseHandle (event);
|
|---|
| 315 | if (section)
|
|---|
| 316 | CloseHandle (section);
|
|---|
| 317 | if (pi.hThread)
|
|---|
| 318 | CloseHandle (pi.hThread);
|
|---|
| 319 | if (pi.hProcess)
|
|---|
| 320 | CloseHandle (pi.hProcess);
|
|---|
| 321 |
|
|---|
| 322 | /* We're the parent. If all is well, terminate. */
|
|---|
| 323 | if (rv)
|
|---|
| 324 | exit (0);
|
|---|
| 325 |
|
|---|
| 326 | /* We failed, return. */
|
|---|
| 327 | }
|
|---|
| 328 |
|
|---|
| 329 | /* This is the corresponding Windows implementation of the
|
|---|
| 330 | fork_to_background() function in utils.c. */
|
|---|
| 331 | void
|
|---|
| 332 | fork_to_background (void)
|
|---|
| 333 | {
|
|---|
| 334 | int rv;
|
|---|
| 335 |
|
|---|
| 336 | rv = fake_fork_child ();
|
|---|
| 337 | if (rv < 0)
|
|---|
| 338 | {
|
|---|
| 339 | fprintf (stderr, "fake_fork_child() failed\n");
|
|---|
| 340 | abort ();
|
|---|
| 341 | }
|
|---|
| 342 | else if (rv == 0)
|
|---|
| 343 | {
|
|---|
| 344 | /* We're the parent. */
|
|---|
| 345 | fake_fork ();
|
|---|
| 346 | /* If fake_fork() returns, it failed. */
|
|---|
| 347 | fprintf (stderr, "fake_fork() failed\n");
|
|---|
| 348 | abort ();
|
|---|
| 349 | }
|
|---|
| 350 | /* If we get here, we're the child. */
|
|---|
| 351 | }
|
|---|
| 352 |
|
|---|
| 353 | static BOOL WINAPI
|
|---|
| 354 | ws_handler (DWORD dwEvent)
|
|---|
| 355 | {
|
|---|
| 356 | switch (dwEvent)
|
|---|
| 357 | {
|
|---|
| 358 | #ifdef CTRLC_BACKGND
|
|---|
| 359 | case CTRL_C_EVENT:
|
|---|
| 360 | ws_hangup ("CTRL+C");
|
|---|
| 361 | return TRUE;
|
|---|
| 362 | #endif
|
|---|
| 363 | #ifdef CTRLBREAK_BACKGND
|
|---|
| 364 | case CTRL_BREAK_EVENT:
|
|---|
| 365 | ws_hangup ("CTRL+Break");
|
|---|
| 366 | return TRUE;
|
|---|
| 367 | #endif
|
|---|
| 368 | default:
|
|---|
| 369 | return FALSE;
|
|---|
| 370 | }
|
|---|
| 371 | }
|
|---|
| 372 |
|
|---|
| 373 | static char *title_buf = NULL;
|
|---|
| 374 | static char *curr_url = NULL;
|
|---|
| 375 | static int old_percentage = -1;
|
|---|
| 376 |
|
|---|
| 377 | /* Updates the console title with the URL of the current file being
|
|---|
| 378 | transferred. */
|
|---|
| 379 | void
|
|---|
| 380 | ws_changetitle (const char *url)
|
|---|
| 381 | {
|
|---|
| 382 | xfree_null (title_buf);
|
|---|
| 383 | xfree_null (curr_url);
|
|---|
| 384 | title_buf = (char *)xmalloc (strlen (url) + 20);
|
|---|
| 385 | curr_url = xstrdup (url);
|
|---|
| 386 | old_percentage = -1;
|
|---|
| 387 | sprintf (title_buf, "Wget %s", curr_url);
|
|---|
| 388 | SetConsoleTitle (title_buf);
|
|---|
| 389 | }
|
|---|
| 390 |
|
|---|
| 391 | /* Updates the console title with the percentage of the current file
|
|---|
| 392 | transferred. */
|
|---|
| 393 | void
|
|---|
| 394 | ws_percenttitle (double percentage_float)
|
|---|
| 395 | {
|
|---|
| 396 | int percentage;
|
|---|
| 397 |
|
|---|
| 398 | if (!title_buf || !curr_url)
|
|---|
| 399 | return;
|
|---|
| 400 |
|
|---|
| 401 | percentage = (int) percentage_float;
|
|---|
| 402 |
|
|---|
| 403 | /* Clamp percentage value. */
|
|---|
| 404 | if (percentage < 0)
|
|---|
| 405 | percentage = 0;
|
|---|
| 406 | if (percentage > 100)
|
|---|
| 407 | percentage = 100;
|
|---|
| 408 |
|
|---|
| 409 | /* Only update the title when the percentage has changed. */
|
|---|
| 410 | if (percentage == old_percentage)
|
|---|
| 411 | return;
|
|---|
| 412 |
|
|---|
| 413 | old_percentage = percentage;
|
|---|
| 414 |
|
|---|
| 415 | sprintf (title_buf, "Wget [%d%%] %s", percentage, curr_url);
|
|---|
| 416 | SetConsoleTitle (title_buf);
|
|---|
| 417 | }
|
|---|
| 418 |
|
|---|
| 419 | /* Returns a pointer to the fully qualified name of the directory that
|
|---|
| 420 | contains the Wget binary (wget.exe). The returned path does not have a
|
|---|
| 421 | trailing path separator. Returns NULL on failure. */
|
|---|
| 422 | char *
|
|---|
| 423 | ws_mypath (void)
|
|---|
| 424 | {
|
|---|
| 425 | static char *wspathsave = NULL;
|
|---|
| 426 |
|
|---|
| 427 | if (!wspathsave)
|
|---|
| 428 | {
|
|---|
| 429 | char buf[MAX_PATH + 1];
|
|---|
| 430 | char *p;
|
|---|
| 431 | DWORD len;
|
|---|
| 432 |
|
|---|
| 433 | len = GetModuleFileName (GetModuleHandle (NULL), buf, sizeof (buf));
|
|---|
| 434 | if (!len || (len >= sizeof (buf)))
|
|---|
| 435 | return NULL;
|
|---|
| 436 |
|
|---|
| 437 | p = strrchr (buf, PATH_SEPARATOR);
|
|---|
| 438 | if (!p)
|
|---|
| 439 | return NULL;
|
|---|
| 440 |
|
|---|
| 441 | *p = '\0';
|
|---|
| 442 | wspathsave = xstrdup (buf);
|
|---|
| 443 | }
|
|---|
| 444 |
|
|---|
| 445 | return wspathsave;
|
|---|
| 446 | }
|
|---|
| 447 |
|
|---|
| 448 | /* Prevent Windows entering sleep/hibernation-mode while Wget is doing
|
|---|
| 449 | a lengthy transfer. Windows does not, by default, consider network
|
|---|
| 450 | activity in console-programs as activity! Works on Win-98/ME/2K
|
|---|
| 451 | and up. */
|
|---|
| 452 | static void
|
|---|
| 453 | set_sleep_mode (void)
|
|---|
| 454 | {
|
|---|
| 455 | typedef DWORD (WINAPI *func_t) (DWORD);
|
|---|
| 456 | func_t set_exec_state;
|
|---|
| 457 |
|
|---|
| 458 | set_exec_state =
|
|---|
| 459 | (func_t) GetProcAddress (GetModuleHandle ("KERNEL32.DLL"),
|
|---|
| 460 | "SetThreadExecutionState");
|
|---|
| 461 |
|
|---|
| 462 | if (set_exec_state)
|
|---|
| 463 | set_exec_state (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
|
|---|
| 464 | }
|
|---|
| 465 |
|
|---|
| 466 | /* Perform Windows specific initialization. */
|
|---|
| 467 | void
|
|---|
| 468 | ws_startup (void)
|
|---|
| 469 | {
|
|---|
| 470 | WSADATA data;
|
|---|
| 471 | WORD requested = MAKEWORD (1, 1);
|
|---|
| 472 | int err = WSAStartup (requested, &data);
|
|---|
| 473 | if (err != 0)
|
|---|
| 474 | {
|
|---|
| 475 | fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
|
|---|
| 476 | exec_name);
|
|---|
| 477 | exit (1);
|
|---|
| 478 | }
|
|---|
| 479 |
|
|---|
| 480 | if (data.wVersion < requested)
|
|---|
| 481 | {
|
|---|
| 482 | fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
|
|---|
| 483 | exec_name);
|
|---|
| 484 | WSACleanup ();
|
|---|
| 485 | exit (1);
|
|---|
| 486 | }
|
|---|
| 487 |
|
|---|
| 488 | atexit (ws_cleanup);
|
|---|
| 489 | set_sleep_mode ();
|
|---|
| 490 | SetConsoleCtrlHandler (ws_handler, TRUE);
|
|---|
| 491 | }
|
|---|
| 492 |
|
|---|
| 493 | /* Replacement utime function for buggy Borland C++Builder 5.5 compiler.
|
|---|
| 494 | (The Borland utime function only works on Windows NT.) */
|
|---|
| 495 |
|
|---|
| 496 | #ifdef HACK_BCC_UTIME_BUG
|
|---|
| 497 | int
|
|---|
| 498 | borland_utime (const char *path, const struct utimbuf *times)
|
|---|
| 499 | {
|
|---|
| 500 | int fd;
|
|---|
| 501 | int res;
|
|---|
| 502 | struct ftime ft;
|
|---|
| 503 | struct tm *ptr_tm;
|
|---|
| 504 |
|
|---|
| 505 | if ((fd = open (path, O_RDWR)) < 0)
|
|---|
| 506 | return -1;
|
|---|
| 507 |
|
|---|
| 508 | ptr_tm = localtime (×->modtime);
|
|---|
| 509 | ft.ft_tsec = ptr_tm->tm_sec >> 1;
|
|---|
| 510 | ft.ft_min = ptr_tm->tm_min;
|
|---|
| 511 | ft.ft_hour = ptr_tm->tm_hour;
|
|---|
| 512 | ft.ft_day = ptr_tm->tm_mday;
|
|---|
| 513 | ft.ft_month = ptr_tm->tm_mon + 1;
|
|---|
| 514 | ft.ft_year = ptr_tm->tm_year - 80;
|
|---|
| 515 | res = setftime (fd, &ft);
|
|---|
| 516 | close (fd);
|
|---|
| 517 | return res;
|
|---|
| 518 | }
|
|---|
| 519 | #endif
|
|---|
| 520 | |
|---|
| 521 |
|
|---|
| 522 | /* run_with_timeout Windows implementation. */
|
|---|
| 523 |
|
|---|
| 524 | /* Stack size 0 uses default thread stack-size (reserve+commit).
|
|---|
| 525 | Determined by what's in the PE header. */
|
|---|
| 526 | #define THREAD_STACK_SIZE 0
|
|---|
| 527 |
|
|---|
| 528 | struct thread_data
|
|---|
| 529 | {
|
|---|
| 530 | void (*fun) (void *);
|
|---|
| 531 | void *arg;
|
|---|
| 532 | DWORD ws_error;
|
|---|
| 533 | };
|
|---|
| 534 |
|
|---|
| 535 | /* The callback that runs FUN(ARG) in a separate thread. This
|
|---|
| 536 | function exists for two reasons: a) to not require FUN to be
|
|---|
| 537 | declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
|
|---|
| 538 | which are per-thread. The latter is useful when FUN calls Winsock
|
|---|
| 539 | functions, which is how run_with_timeout is used in Wget.
|
|---|
| 540 |
|
|---|
| 541 | [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
|
|---|
| 542 | the default (wcc386 -3r). */
|
|---|
| 543 |
|
|---|
| 544 | static DWORD WINAPI
|
|---|
| 545 | thread_helper (void *arg)
|
|---|
| 546 | {
|
|---|
| 547 | struct thread_data *td = (struct thread_data *) arg;
|
|---|
| 548 |
|
|---|
| 549 | /* Initialize Winsock error to what it was in the parent. That way
|
|---|
| 550 | the subsequent call to WSAGetLastError will return the same value
|
|---|
| 551 | if td->fun doesn't change Winsock error state. */
|
|---|
| 552 | WSASetLastError (td->ws_error);
|
|---|
| 553 |
|
|---|
| 554 | td->fun (td->arg);
|
|---|
| 555 |
|
|---|
| 556 | /* Return Winsock error to the caller, in case FUN ran Winsock
|
|---|
| 557 | code. */
|
|---|
| 558 | td->ws_error = WSAGetLastError ();
|
|---|
| 559 | return 0;
|
|---|
| 560 | }
|
|---|
| 561 |
|
|---|
| 562 | /* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
|
|---|
| 563 | seconds. Returns non-zero if the function was interrupted with a
|
|---|
| 564 | timeout, zero otherwise.
|
|---|
| 565 |
|
|---|
| 566 | This works by running FUN in a separate thread and terminating the
|
|---|
| 567 | thread if it doesn't finish in the specified time. */
|
|---|
| 568 |
|
|---|
| 569 | int
|
|---|
| 570 | run_with_timeout (double seconds, void (*fun) (void *), void *arg)
|
|---|
| 571 | {
|
|---|
| 572 | static HANDLE thread_hnd = NULL;
|
|---|
| 573 | struct thread_data thread_arg;
|
|---|
| 574 | DWORD thread_id;
|
|---|
| 575 | int rc;
|
|---|
| 576 |
|
|---|
| 577 | DEBUGP (("seconds %.2f, ", seconds));
|
|---|
| 578 |
|
|---|
| 579 | if (seconds == 0)
|
|---|
| 580 | {
|
|---|
| 581 | blocking_fallback:
|
|---|
| 582 | fun (arg);
|
|---|
| 583 | return 0;
|
|---|
| 584 | }
|
|---|
| 585 |
|
|---|
| 586 | /* Should never happen, but test for recursivety anyway. */
|
|---|
| 587 | assert (thread_hnd == NULL);
|
|---|
| 588 |
|
|---|
| 589 | thread_arg.fun = fun;
|
|---|
| 590 | thread_arg.arg = arg;
|
|---|
| 591 | thread_arg.ws_error = WSAGetLastError ();
|
|---|
| 592 | thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
|
|---|
| 593 | &thread_arg, 0, &thread_id);
|
|---|
| 594 | if (!thread_hnd)
|
|---|
| 595 | {
|
|---|
| 596 | DEBUGP (("CreateThread() failed; %s\n", strerror (GetLastError ())));
|
|---|
| 597 | goto blocking_fallback;
|
|---|
| 598 | }
|
|---|
| 599 |
|
|---|
| 600 | if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
|
|---|
| 601 | == WAIT_OBJECT_0)
|
|---|
| 602 | {
|
|---|
| 603 | /* Propagate error state (which is per-thread) to this thread,
|
|---|
| 604 | so the caller can inspect it. */
|
|---|
| 605 | WSASetLastError (thread_arg.ws_error);
|
|---|
| 606 | DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
|
|---|
| 607 | rc = 0;
|
|---|
| 608 | }
|
|---|
| 609 | else
|
|---|
| 610 | {
|
|---|
| 611 | TerminateThread (thread_hnd, 1);
|
|---|
| 612 | rc = 1;
|
|---|
| 613 | }
|
|---|
| 614 |
|
|---|
| 615 | CloseHandle (thread_hnd); /* Clear-up after TerminateThread(). */
|
|---|
| 616 | thread_hnd = NULL;
|
|---|
| 617 | return rc;
|
|---|
| 618 | }
|
|---|
| 619 | |
|---|
| 620 |
|
|---|
| 621 | /* Wget expects network calls such as connect, recv, send, etc., to set
|
|---|
| 622 | errno on failure. To achieve that, Winsock calls are wrapped with code
|
|---|
| 623 | that, in case of error, sets errno to the value of WSAGetLastError().
|
|---|
| 624 | In addition, we provide a wrapper around strerror, which recognizes
|
|---|
| 625 | Winsock errors and prints the appropriate error message. */
|
|---|
| 626 |
|
|---|
| 627 | /* Define a macro that creates a function definition that wraps FUN into
|
|---|
| 628 | a function that sets errno the way the rest of the code expects. */
|
|---|
| 629 |
|
|---|
| 630 | #define WRAP(fun, decl, call) int wrapped_##fun decl { \
|
|---|
| 631 | int retval = fun call; \
|
|---|
| 632 | if (retval < 0) \
|
|---|
| 633 | errno = WSAGetLastError (); \
|
|---|
| 634 | return retval; \
|
|---|
| 635 | }
|
|---|
| 636 |
|
|---|
| 637 | WRAP (socket, (int domain, int type, int protocol), (domain, type, protocol))
|
|---|
| 638 | WRAP (bind, (int s, struct sockaddr *a, int alen), (s, a, alen))
|
|---|
| 639 | WRAP (connect, (int s, const struct sockaddr *a, int alen), (s, a, alen))
|
|---|
| 640 | WRAP (listen, (int s, int backlog), (s, backlog))
|
|---|
| 641 | WRAP (accept, (int s, struct sockaddr *a, int *alen), (s, a, alen))
|
|---|
| 642 | WRAP (recv, (int s, void *buf, int len, int flags), (s, buf, len, flags))
|
|---|
| 643 | WRAP (send, (int s, const void *buf, int len, int flags), (s, buf, len, flags))
|
|---|
| 644 | WRAP (select, (int n, fd_set *r, fd_set *w, fd_set *e, const struct timeval *tm),
|
|---|
| 645 | (n, r, w, e, tm))
|
|---|
| 646 | WRAP (getsockname, (int s, struct sockaddr *n, int *nlen), (s, n, nlen))
|
|---|
| 647 | WRAP (getpeername, (int s, struct sockaddr *n, int *nlen), (s, n, nlen))
|
|---|
| 648 | WRAP (setsockopt, (int s, int level, int opt, const void *val, int len),
|
|---|
| 649 | (s, level, opt, val, len))
|
|---|
| 650 | WRAP (closesocket, (int s), (s))
|
|---|
| 651 |
|
|---|
| 652 | /* Return the text of the error message for Winsock error WSERR. */
|
|---|
| 653 |
|
|---|
| 654 | static const char *
|
|---|
| 655 | get_winsock_error (int wserr)
|
|---|
| 656 | {
|
|---|
| 657 | switch (wserr) {
|
|---|
| 658 | case WSAEINTR: return "Interrupted system call";
|
|---|
| 659 | case WSAEBADF: return "Bad file number";
|
|---|
| 660 | case WSAEACCES: return "Permission denied";
|
|---|
| 661 | case WSAEFAULT: return "Bad address";
|
|---|
| 662 | case WSAEINVAL: return "Invalid argument";
|
|---|
| 663 | case WSAEMFILE: return "Too many open files";
|
|---|
| 664 | case WSAEWOULDBLOCK: return "Resource temporarily unavailable";
|
|---|
| 665 | case WSAEINPROGRESS: return "Operation now in progress";
|
|---|
| 666 | case WSAEALREADY: return "Operation already in progress";
|
|---|
| 667 | case WSAENOTSOCK: return "Socket operation on nonsocket";
|
|---|
| 668 | case WSAEDESTADDRREQ: return "Destination address required";
|
|---|
| 669 | case WSAEMSGSIZE: return "Message too long";
|
|---|
| 670 | case WSAEPROTOTYPE: return "Protocol wrong type for socket";
|
|---|
| 671 | case WSAENOPROTOOPT: return "Bad protocol option";
|
|---|
| 672 | case WSAEPROTONOSUPPORT: return "Protocol not supported";
|
|---|
| 673 | case WSAESOCKTNOSUPPORT: return "Socket type not supported";
|
|---|
| 674 | case WSAEOPNOTSUPP: return "Operation not supported";
|
|---|
| 675 | case WSAEPFNOSUPPORT: return "Protocol family not supported";
|
|---|
| 676 | case WSAEAFNOSUPPORT: return "Address family not supported by protocol family";
|
|---|
| 677 | case WSAEADDRINUSE: return "Address already in use";
|
|---|
| 678 | case WSAEADDRNOTAVAIL: return "Cannot assign requested address";
|
|---|
| 679 | case WSAENETDOWN: return "Network is down";
|
|---|
| 680 | case WSAENETUNREACH: return "Network is unreachable";
|
|---|
| 681 | case WSAENETRESET: return "Network dropped connection on reset";
|
|---|
| 682 | case WSAECONNABORTED: return "Software caused connection abort";
|
|---|
| 683 | case WSAECONNRESET: return "Connection reset by peer";
|
|---|
| 684 | case WSAENOBUFS: return "No buffer space available";
|
|---|
| 685 | case WSAEISCONN: return "Socket is already connected";
|
|---|
| 686 | case WSAENOTCONN: return "Socket is not connected";
|
|---|
| 687 | case WSAESHUTDOWN: return "Cannot send after socket shutdown";
|
|---|
| 688 | case WSAETOOMANYREFS: return "Too many references";
|
|---|
| 689 | case WSAETIMEDOUT: return "Connection timed out";
|
|---|
| 690 | case WSAECONNREFUSED: return "Connection refused";
|
|---|
| 691 | case WSAELOOP: return "Too many levels of symbolic links";
|
|---|
| 692 | case WSAENAMETOOLONG: return "File name too long";
|
|---|
| 693 | case WSAEHOSTDOWN: return "Host is down";
|
|---|
| 694 | case WSAEHOSTUNREACH: return "No route to host";
|
|---|
| 695 | case WSAENOTEMPTY: return "Not empty";
|
|---|
| 696 | case WSAEPROCLIM: return "Too many processes";
|
|---|
| 697 | case WSAEUSERS: return "Too many users";
|
|---|
| 698 | case WSAEDQUOT: return "Bad quota";
|
|---|
| 699 | case WSAESTALE: return "Something is stale";
|
|---|
| 700 | case WSAEREMOTE: return "Remote error";
|
|---|
| 701 | case WSAEDISCON: return "Disconnected";
|
|---|
| 702 |
|
|---|
| 703 | /* Extended Winsock errors */
|
|---|
| 704 | case WSASYSNOTREADY: return "Winsock library is not ready";
|
|---|
| 705 | case WSANOTINITIALISED: return "Winsock library not initalised";
|
|---|
| 706 | case WSAVERNOTSUPPORTED: return "Winsock version not supported";
|
|---|
| 707 |
|
|---|
| 708 | case WSAHOST_NOT_FOUND: return "Host not found";
|
|---|
| 709 | case WSATRY_AGAIN: return "Host not found, try again";
|
|---|
| 710 | case WSANO_RECOVERY: return "Unrecoverable error in call to nameserver";
|
|---|
| 711 | case WSANO_DATA: return "No data record of requested type";
|
|---|
| 712 |
|
|---|
| 713 | default:
|
|---|
| 714 | return NULL;
|
|---|
| 715 | }
|
|---|
| 716 | }
|
|---|
| 717 |
|
|---|
| 718 | /* Return the error message corresponding to ERR. This is different
|
|---|
| 719 | from Windows libc strerror() in that it handles Winsock errors
|
|---|
| 720 | correctly. */
|
|---|
| 721 |
|
|---|
| 722 | const char *
|
|---|
| 723 | windows_strerror (int err)
|
|---|
| 724 | {
|
|---|
| 725 | const char *p;
|
|---|
| 726 | if (err >= 0 && err < sys_nerr)
|
|---|
| 727 | return strerror (err);
|
|---|
| 728 | else if ((p = get_winsock_error (err)) != NULL)
|
|---|
| 729 | return p;
|
|---|
| 730 | else
|
|---|
| 731 | {
|
|---|
| 732 | static char buf[32];
|
|---|
| 733 | snprintf (buf, sizeof (buf), "Unknown error %d (%#x)", err, err);
|
|---|
| 734 | return buf;
|
|---|
| 735 | }
|
|---|
| 736 | }
|
|---|