1 /* mswindows.c -- Windows-specific support
2 Copyright (C) 1995, 1996, 1997, 1998, 2004
3 Free Software Foundation, Inc.
5 This file is part of GNU Wget.
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.
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.
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.
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. */
40 #ifdef HACK_BCC_UTIME_BUG
46 # ifdef HAVE_SYS_UTIME_H
47 # include <sys/utime.h>
59 #ifndef ES_SYSTEM_REQUIRED
60 #define ES_SYSTEM_REQUIRED 0x00000001
64 #define ES_CONTINUOUS 0x80000000
68 /* Defined in log.c. */
69 void log_request_redirect_output PARAMS ((const char *));
71 /* Windows version of xsleep in utils.c. */
74 xsleep (double seconds)
79 /* Explained in utils.c. */
81 seconds -= (long) seconds;
83 usleep (seconds * 1000000L);
84 #else /* not HAVE_USLEEP */
85 SleepEx (seconds * 1000, FALSE);
86 #endif /* not HAVE_USLEEP */
90 windows_main_junk (int *argc, char **argv, char **exec_name)
94 /* Remove .EXE from filename if it has one. */
95 *exec_name = xstrdup (*exec_name);
96 p = strrchr (*exec_name, '.');
108 ws_hangup (const char *reason)
110 /* Whether we arrange our own version of opt.lfilename here. */
115 opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
118 printf (_("Continuing in background.\n"));
120 printf (_("Output will be written to `%s'.\n"), opt.lfilename);
122 log_request_redirect_output (reason);
124 /* Detach process from the current console. Under Windows 9x, if we
125 were launched from a 16-bit process (which is usually the case;
126 command.com is 16-bit) the parent process should resume right away.
127 Under NT or if launched from a 32-process under 9x, this is a futile
128 gesture as the parent will wait for us to terminate before resuming. */
132 /* Construct the name for a named section (a.k.a. `file mapping') object.
133 The returned string is dynamically allocated and needs to be xfree()'d. */
135 make_section_name (DWORD pid)
137 return aprintf ("gnu_wget_fake_fork_%lu", pid);
140 /* This structure is used to hold all the data that is exchanged between
142 struct fake_fork_info
146 char lfilename[MAX_PATH + 1];
149 /* Determines if we are the child and if so performs the child logic.
156 fake_fork_child (void)
158 HANDLE section, event;
159 struct fake_fork_info *info;
162 name = make_section_name (GetCurrentProcessId ());
163 section = OpenFileMapping (FILE_MAP_WRITE, FALSE, name);
165 /* It seems that Windows 9x and NT set last-error inconsistently when
166 OpenFileMapping() fails; so we assume it failed because the section
167 object does not exist. */
169 return 0; /* We are the parent. */
171 info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
174 CloseHandle (section);
182 opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
184 strncpy (info->lfilename, opt.lfilename, sizeof (info->lfilename));
185 info->lfilename[sizeof (info->lfilename) - 1] = '\0';
190 UnmapViewOfFile (info);
191 CloseHandle (section);
193 /* Inform the parent that we've done our part. */
194 if (!SetEvent (event))
198 return 1; /* We are the child. */
201 /* Windows doesn't support the fork() call; so we fake it by invoking
202 another copy of Wget with the same arguments with which we were
203 invoked. The child copy of Wget should perform the same initialization
204 sequence as the parent; so we should have two processes that are
205 essentially identical. We create a specially named section object that
206 allows the child to distinguish itself from the parent and is used to
207 exchange information between the two processes. We use an event object
208 for synchronization. */
212 char exe[MAX_PATH + 1];
214 SECURITY_ATTRIBUTES sa;
215 HANDLE section, event, h[2];
217 PROCESS_INFORMATION pi;
218 struct fake_fork_info *info;
222 event = section = pi.hProcess = pi.hThread = NULL;
224 /* Get the fully qualified name of our executable. This is more reliable
225 than using argv[0]. */
226 exe_len = GetModuleFileName (GetModuleHandle (NULL), exe, sizeof (exe));
227 if (!exe_len || (exe_len >= sizeof (exe)))
230 sa.nLength = sizeof (sa);
231 sa.lpSecurityDescriptor = NULL;
232 sa.bInheritHandle = TRUE;
234 /* Create an anonymous inheritable event object that starts out
236 event = CreateEvent (&sa, FALSE, FALSE, NULL);
240 /* Create the child process detached form the current console and in a
242 memset (&si, 0, sizeof (si));
244 rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE,
245 CREATE_SUSPENDED | DETACHED_PROCESS,
246 NULL, NULL, &si, &pi);
250 /* Create a named section object with a name based on the process id of
252 name = make_section_name (pi.dwProcessId);
254 CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
255 sizeof (struct fake_fork_info), name);
258 /* Fail if the section object already exists (should not happen). */
259 if (!section || (le == ERROR_ALREADY_EXISTS))
265 /* Copy the event handle into the section object. */
266 info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
275 UnmapViewOfFile (info);
277 /* Start the child process. */
278 rv = ResumeThread (pi.hThread);
281 TerminateProcess (pi.hProcess, (DWORD) -1);
285 /* Wait for the child to signal to us that it has done its part. If it
286 terminates before signaling us it's an error. */
290 rv = WAIT_OBJECT_0 == WaitForMultipleObjects (2, h, FALSE, 5 * 60 * 1000);
294 info = MapViewOfFile (section, FILE_MAP_READ, 0, 0, 0);
301 /* Ensure string is properly terminated. */
302 if (info->changedp &&
303 !memchr (info->lfilename, '\0', sizeof (info->lfilename)))
309 printf (_("Continuing in background, pid %lu.\n"), pi.dwProcessId);
311 printf (_("Output will be written to `%s'.\n"), info->lfilename);
313 UnmapViewOfFile (info);
320 CloseHandle (section);
322 CloseHandle (pi.hThread);
324 CloseHandle (pi.hProcess);
326 /* We're the parent. If all is well, terminate. */
330 /* We failed, return. */
333 /* This is the corresponding Windows implementation of the
334 fork_to_background() function in utils.c. */
336 fork_to_background (void)
340 rv = fake_fork_child ();
343 fprintf (stderr, "fake_fork_child() failed\n");
348 /* We're the parent. */
350 /* If fake_fork() returns, it failed. */
351 fprintf (stderr, "fake_fork() failed\n");
354 /* If we get here, we're the child. */
358 ws_handler (DWORD dwEvent)
364 ws_hangup ("CTRL+C");
367 #ifdef CTRLBREAK_BACKGND
368 case CTRL_BREAK_EVENT:
369 ws_hangup ("CTRL+Break");
377 static char *title_buf = NULL;
378 static char *curr_url = NULL;
379 static int old_percentage = -1;
381 /* Updates the console title with the URL of the current file being
384 ws_changetitle (const char *url)
386 xfree_null (title_buf);
387 xfree_null (curr_url);
388 title_buf = (char *)xmalloc (strlen (url) + 20);
389 curr_url = xstrdup (url);
391 sprintf (title_buf, "Wget %s", curr_url);
392 SetConsoleTitle (title_buf);
395 /* Updates the console title with the percentage of the current file
398 ws_percenttitle (double percentage_float)
402 if (!title_buf || !curr_url)
405 percentage = (int) percentage_float;
407 /* Clamp percentage value. */
410 if (percentage > 100)
413 /* Only update the title when the percentage has changed. */
414 if (percentage == old_percentage)
417 old_percentage = percentage;
419 sprintf (title_buf, "Wget [%d%%] %s", percentage, curr_url);
420 SetConsoleTitle (title_buf);
423 /* Returns a pointer to the fully qualified name of the directory that
424 contains the Wget binary (wget.exe). The returned path does not have a
425 trailing path separator. Returns NULL on failure. */
429 static char *wspathsave = NULL;
433 char buf[MAX_PATH + 1];
437 len = GetModuleFileName (GetModuleHandle (NULL), buf, sizeof (buf));
438 if (!len || (len >= sizeof (buf)))
441 p = strrchr (buf, PATH_SEPARATOR);
446 wspathsave = xstrdup (buf);
452 /* Prevent Windows entering sleep/hibernation-mode while Wget is doing
453 a lengthy transfer. Windows does not, by default, consider network
454 activity in console-programs as activity! Works on Win-98/ME/2K
457 set_sleep_mode (void)
459 typedef DWORD (WINAPI *func_t) (DWORD);
460 func_t set_exec_state;
463 (func_t) GetProcAddress (GetModuleHandle ("KERNEL32.DLL"),
464 "SetThreadExecutionState");
467 set_exec_state (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
470 /* Perform Windows specific initialization. */
478 requested = MAKEWORD (1, 1);
479 err = WSAStartup (requested, &data);
482 fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
487 if (data.wVersion < requested)
489 fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
497 SetConsoleCtrlHandler (ws_handler, TRUE);
500 /* Replacement utime function for buggy Borland C++Builder 5.5 compiler.
501 (The Borland utime function only works on Windows NT.) */
503 #ifdef HACK_BCC_UTIME_BUG
505 borland_utime (const char *path, const struct utimbuf *times)
512 if ((fd = open (path, O_RDWR)) < 0)
515 ptr_tm = localtime (×->modtime);
516 ft.ft_tsec = ptr_tm->tm_sec >> 1;
517 ft.ft_min = ptr_tm->tm_min;
518 ft.ft_hour = ptr_tm->tm_hour;
519 ft.ft_day = ptr_tm->tm_mday;
520 ft.ft_month = ptr_tm->tm_mon + 1;
521 ft.ft_year = ptr_tm->tm_year - 80;
522 res = setftime (fd, &ft);
528 /* run_with_timeout Windows implementation. */
530 /* Stack size 0 uses default thread stack-size (reserve+commit).
531 Determined by what's in the PE header. */
532 #define THREAD_STACK_SIZE 0
536 void (*fun) (void *);
541 /* The callback that runs FUN(ARG) in a separate thread. This
542 function exists for two reasons: a) to not require FUN to be
543 declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
544 which are per-thread. The latter is useful when FUN calls Winsock
545 functions, which is how run_with_timeout is used in Wget.
547 [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
548 the default (wcc386 -3r). */
551 thread_helper (void *arg)
553 struct thread_data *td = (struct thread_data *) arg;
555 /* Initialize Winsock error to what it was in the parent. That way
556 the subsequent call to WSAGetLastError will return the same value
557 if td->fun doesn't change Winsock error state. */
558 WSASetLastError (td->ws_error);
562 /* Return Winsock error to the caller, in case FUN ran Winsock
564 td->ws_error = WSAGetLastError ();
568 /* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
569 seconds. Returns non-zero if the function was interrupted with a
570 timeout, zero otherwise.
572 This works by running FUN in a separate thread and terminating the
573 thread if it doesn't finish in the specified time. */
576 run_with_timeout (double seconds, void (*fun) (void *), void *arg)
578 static HANDLE thread_hnd = NULL;
579 struct thread_data thread_arg;
583 DEBUGP (("seconds %.2f, ", seconds));
592 /* Should never happen, but test for recursivety anyway. */
593 assert (thread_hnd == NULL);
595 thread_arg.fun = fun;
596 thread_arg.arg = arg;
597 thread_arg.ws_error = WSAGetLastError ();
598 thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
599 &thread_arg, 0, &thread_id);
602 DEBUGP (("CreateThread() failed; %s\n", strerror (GetLastError ())));
603 goto blocking_fallback;
606 if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
609 /* Propagate error state (which is per-thread) to this thread,
610 so the caller can inspect it. */
611 WSASetLastError (thread_arg.ws_error);
612 DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
617 TerminateThread (thread_hnd, 1);
621 CloseHandle (thread_hnd); /* Clear-up after TerminateThread(). */