]> sjero.net Git - wget/blob - src/mswindows.c
5e10695c51d706377878d6862c8ef71e8348b1b3
[wget] / src / mswindows.c
1 /* mswindows.c -- Windows-specific support
2    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
3    2005, 2006, 2007, 2008, 2009, 2010 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 3 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, see <http://www.gnu.org/licenses/>.
19
20 Additional permission under GNU GPL version 3 section 7
21
22 If you modify this program, or any covered work, by linking or
23 combining it with the OpenSSL project's OpenSSL library (or a
24 modified version of that library), containing parts covered by the
25 terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
26 grants you additional permission to convey the resulting work.
27 Corresponding Source for a non-source form of such a combination
28 shall include the source code for the parts of OpenSSL used as well
29 as that of the covered work.  */
30
31 #define INHIBIT_WRAP /* avoid wrapping of socket, bind, ... */
32
33 #include "wget.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <math.h>
40
41
42 #include "utils.h"
43 #include "url.h"
44
45 #ifndef ES_SYSTEM_REQUIRED
46 #define ES_SYSTEM_REQUIRED  0x00000001
47 #endif
48
49 #ifndef ES_CONTINUOUS
50 #define ES_CONTINUOUS       0x80000000
51 #endif
52
53
54 /* Defined in log.c.  */
55 void log_request_redirect_output (const char *);
56
57 /* Windows version of xsleep in utils.c.  */
58
59 void
60 xsleep (double seconds)
61 {
62 #if defined(HAVE_USLEEP) && defined(HAVE_SLEEP)
63   if (seconds > 1000)
64     {
65       /* Explained in utils.c. */
66       sleep (seconds);
67       seconds -= (long) seconds;
68     }
69   usleep (seconds * 1000000);
70 #else  /* not HAVE_USLEEP */
71   SleepEx ((DWORD) (seconds * 1000 + .5), FALSE);
72 #endif /* not HAVE_USLEEP */
73 }
74
75 void
76 windows_main (char **exec_name)
77 {
78   char *p;
79
80   /* Remove .EXE from filename if it has one.  */
81   *exec_name = xstrdup (*exec_name);
82   p = strrchr (*exec_name, '.');
83   if (p)
84     *p = '\0';
85 }
86 \f
87 static void
88 ws_cleanup (void)
89 {
90   xfree ((char*)exec_name);
91   WSACleanup ();
92 }
93
94 #if defined(CTRLBREAK_BACKGND) || defined(CTRLC_BACKGND)
95 static void
96 ws_hangup (const char *reason)
97 {
98   fprintf (stderr, _("Continuing in background.\n"));
99   log_request_redirect_output (reason);
100
101   /* Detach process from the current console.  Under Windows 9x, if we
102      were launched from a 16-bit process (which is usually the case;
103      command.com is 16-bit) the parent process should resume right away.
104      Under NT or if launched from a 32-process under 9x, this is a futile
105      gesture as the parent will wait for us to terminate before resuming.  */
106   FreeConsole ();
107 }
108 #endif
109
110 /* Construct the name for a named section (a.k.a. `file mapping') object.
111    The returned string is dynamically allocated and needs to be xfree()'d.  */
112 static char *
113 make_section_name (DWORD pid)
114 {
115   return aprintf ("gnu_wget_fake_fork_%lu", pid);
116 }
117
118 /* This structure is used to hold all the data that is exchanged between
119    parent and child.  */
120 struct fake_fork_info
121 {
122   HANDLE event;
123   bool logfile_changed;
124   char lfilename[MAX_PATH + 1];
125 };
126
127 /* Determines if we are the child and if so performs the child logic.
128    Return values:
129      < 0  error
130        0  parent
131      > 0  child
132 */
133 static int
134 fake_fork_child (void)
135 {
136   HANDLE section, event;
137   struct fake_fork_info *info;
138   char *name;
139
140   name = make_section_name (GetCurrentProcessId ());
141   section = OpenFileMapping (FILE_MAP_WRITE, FALSE, name);
142   xfree (name);
143   /* It seems that Windows 9x and NT set last-error inconsistently when
144      OpenFileMapping() fails; so we assume it failed because the section
145      object does not exist.  */
146   if (!section)
147     return 0;                   /* We are the parent.  */
148
149   info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
150   if (!info)
151     {
152       CloseHandle (section);
153       return -1;
154     }
155
156   event = info->event;
157
158   info->logfile_changed = false;
159   if (!opt.lfilename && (!opt.quiet || opt.server_response))
160     {
161       /* See utils:fork_to_background for explanation. */
162       FILE *new_log_fp = unique_create (DEFAULT_LOGFILE, false, &opt.lfilename);
163       if (new_log_fp)
164         {
165           info->logfile_changed = true;
166           strncpy (info->lfilename, opt.lfilename, sizeof (info->lfilename));
167           info->lfilename[sizeof (info->lfilename) - 1] = '\0';
168           fclose (new_log_fp);
169         }
170     }
171
172   UnmapViewOfFile (info);
173   CloseHandle (section);
174
175   /* Inform the parent that we've done our part.  */
176   if (!SetEvent (event))
177     return -1;
178
179   CloseHandle (event);
180   return 1;                     /* We are the child.  */
181 }
182
183 /* Windows doesn't support the fork() call; so we fake it by invoking
184    another copy of Wget with the same arguments with which we were
185    invoked.  The child copy of Wget should perform the same initialization
186    sequence as the parent; so we should have two processes that are
187    essentially identical.  We create a specially named section object that
188    allows the child to distinguish itself from the parent and is used to
189    exchange information between the two processes.  We use an event object
190    for synchronization.  */
191 static void
192 fake_fork (void)
193 {
194   char exe[MAX_PATH + 1];
195   DWORD exe_len, le;
196   SECURITY_ATTRIBUTES sa;
197   HANDLE section, event, h[2];
198   STARTUPINFO si;
199   PROCESS_INFORMATION pi;
200   struct fake_fork_info *info;
201   char *name;
202   BOOL rv;
203
204   section = pi.hProcess = pi.hThread = NULL;
205
206   /* Get the fully qualified name of our executable.  This is more reliable
207      than using argv[0].  */
208   exe_len = GetModuleFileName (GetModuleHandle (NULL), exe, sizeof (exe));
209   if (!exe_len || (exe_len >= sizeof (exe)))
210     return;
211
212   sa.nLength = sizeof (sa);
213   sa.lpSecurityDescriptor = NULL;
214   sa.bInheritHandle = TRUE;
215
216   /* Create an anonymous inheritable event object that starts out
217      non-signaled.  */
218   event = CreateEvent (&sa, FALSE, FALSE, NULL);
219   if (!event)
220     return;
221
222   /* Create the child process detached form the current console and in a
223      suspended state.  */
224   xzero (si);
225   si.cb = sizeof (si);
226   rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE,
227                       CREATE_SUSPENDED | DETACHED_PROCESS,
228                       NULL, NULL, &si, &pi);
229   if (!rv)
230     goto cleanup;
231
232   /* Create a named section object with a name based on the process id of
233      the child.  */
234   name = make_section_name (pi.dwProcessId);
235   section =
236       CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
237                          sizeof (struct fake_fork_info), name);
238   le = GetLastError();
239   xfree (name);
240   /* Fail if the section object already exists (should not happen).  */
241   if (!section || (le == ERROR_ALREADY_EXISTS))
242     {
243       rv = FALSE;
244       goto cleanup;
245     }
246
247   /* Copy the event handle into the section object.  */
248   info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
249   if (!info)
250     {
251       rv = FALSE;
252       goto cleanup;
253     }
254
255   info->event = event;
256
257   UnmapViewOfFile (info);
258
259   /* Start the child process.  */
260   rv = ResumeThread (pi.hThread);
261   if (!rv)
262     {
263       TerminateProcess (pi.hProcess, (DWORD) -1);
264       goto cleanup;
265     }
266
267   /* Wait for the child to signal to us that it has done its part.  If it
268      terminates before signaling us it's an error.  */
269
270   h[0] = event;
271   h[1] = pi.hProcess;
272   rv = WAIT_OBJECT_0 == WaitForMultipleObjects (2, h, FALSE, 5 * 60 * 1000);
273   if (!rv)
274     goto cleanup;
275
276   info = MapViewOfFile (section, FILE_MAP_READ, 0, 0, 0);
277   if (!info)
278     {
279       rv = FALSE;
280       goto cleanup;
281     }
282
283   /* Ensure string is properly terminated.  */
284   if (info->logfile_changed &&
285       !memchr (info->lfilename, '\0', sizeof (info->lfilename)))
286     {
287       rv = FALSE;
288       goto cleanup;
289     }
290
291   printf (_("Continuing in background, pid %lu.\n"), pi.dwProcessId);
292   if (info->logfile_changed)
293     printf (_("Output will be written to %s.\n"), quote (info->lfilename));
294
295   UnmapViewOfFile (info);
296
297 cleanup:
298
299   if (event)
300     CloseHandle (event);
301   if (section)
302     CloseHandle (section);
303   if (pi.hThread)
304     CloseHandle (pi.hThread);
305   if (pi.hProcess)
306     CloseHandle (pi.hProcess);
307
308   /* We're the parent.  If all is well, terminate.  */
309   if (rv)
310     exit (0);
311
312   /* We failed, return.  */
313 }
314
315 /* This is the corresponding Windows implementation of the
316    fork_to_background() function in utils.c.  */
317 void
318 fork_to_background (void)
319 {
320   int rv;
321
322   rv = fake_fork_child ();
323   if (rv < 0)
324     {
325       fprintf (stderr, "fake_fork_child() failed\n");
326       abort ();
327     }
328   else if (rv == 0)
329     {
330       /* We're the parent.  */
331       fake_fork ();
332       /* If fake_fork() returns, it failed.  */
333       fprintf (stderr, "fake_fork() failed\n");
334       abort ();
335     }
336   /* If we get here, we're the child.  */
337 }
338
339 static BOOL WINAPI
340 ws_handler (DWORD dwEvent)
341 {
342   switch (dwEvent)
343     {
344 #ifdef CTRLC_BACKGND
345     case CTRL_C_EVENT:
346       ws_hangup ("CTRL+C");
347       return TRUE;
348 #endif
349 #ifdef CTRLBREAK_BACKGND
350     case CTRL_BREAK_EVENT:
351       ws_hangup ("CTRL+Break");
352       return TRUE;
353 #endif
354     default:
355       return FALSE;
356     }
357 }
358
359 static char *title_buf = NULL;
360 static char *curr_url  = NULL;
361 static int old_percentage = -1;
362
363 /* Updates the console title with the URL of the current file being
364    transferred.  */
365 void
366 ws_changetitle (const char *url)
367 {
368   xfree_null (title_buf);
369   xfree_null (curr_url);
370   title_buf = xmalloc (strlen (url) + 20);
371   curr_url = xstrdup (url);
372   old_percentage = -1;
373   sprintf (title_buf, "Wget %s", curr_url);
374   SetConsoleTitle (title_buf);
375 }
376
377 /* Updates the console title with the percentage of the current file
378    transferred.  */
379 void
380 ws_percenttitle (double percentage_float)
381 {
382   int percentage;
383
384   if (!title_buf || !curr_url)
385     return;
386
387   percentage = (int) percentage_float;
388
389   /* Clamp percentage value.  */
390   if (percentage < 0)
391     percentage = 0;
392   if (percentage > 100)
393     percentage = 100;
394
395   /* Only update the title when the percentage has changed.  */
396   if (percentage == old_percentage)
397     return;
398
399   old_percentage = percentage;
400
401   sprintf (title_buf, "Wget [%d%%] %s", percentage, curr_url);
402   SetConsoleTitle (title_buf);
403 }
404
405 /* Returns a pointer to the fully qualified name of the directory that
406    contains the Wget binary (wget.exe).  The returned path does not have a
407    trailing path separator.  Returns NULL on failure.  */
408 char *
409 ws_mypath (void)
410 {
411   static char *wspathsave = NULL;
412
413   if (!wspathsave)
414     {
415       char buf[MAX_PATH + 1];
416       char *p;
417       DWORD len;
418
419       len = GetModuleFileName (GetModuleHandle (NULL), buf, sizeof (buf));
420       if (!len || (len >= sizeof (buf)))
421         return NULL;
422
423       p = strrchr (buf, PATH_SEPARATOR);
424       if (!p)
425         return NULL;
426
427       *p = '\0';
428       wspathsave = xstrdup (buf);
429     }
430
431   return wspathsave;
432 }
433
434 /* Prevent Windows entering sleep/hibernation-mode while Wget is doing
435    a lengthy transfer.  Windows does not, by default, consider network
436    activity in console-programs as activity!  Works on Win-98/ME/2K
437    and up.  */
438 static void
439 set_sleep_mode (void)
440 {
441   typedef DWORD (WINAPI *func_t) (DWORD);
442   func_t set_exec_state;
443
444   set_exec_state =
445       (func_t) GetProcAddress (GetModuleHandle ("KERNEL32.DLL"),
446                                "SetThreadExecutionState");
447
448   if (set_exec_state)
449     set_exec_state (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
450 }
451
452 /* Perform Windows specific initialization.  */
453 void
454 ws_startup (void)
455 {
456   WSADATA data;
457   WORD requested = MAKEWORD (1, 1);
458   int err = WSAStartup (requested, &data);
459   if (err != 0)
460     {
461       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
462                exec_name);
463       exit (1);
464     }
465
466   if (data.wVersion < requested)
467     {
468       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
469                exec_name);
470       WSACleanup ();
471       exit (1);
472     }
473
474   atexit (ws_cleanup);
475   set_sleep_mode ();
476   SetConsoleCtrlHandler (ws_handler, TRUE);
477 }
478 \f
479 /* run_with_timeout Windows implementation.  */
480
481 /* Stack size 0 uses default thread stack-size (reserve+commit).
482    Determined by what's in the PE header.  */
483 #define THREAD_STACK_SIZE  0
484
485 struct thread_data
486 {
487   void (*fun) (void *);
488   void *arg;
489   DWORD ws_error;
490 };
491
492 /* The callback that runs FUN(ARG) in a separate thread.  This
493    function exists for two reasons: a) to not require FUN to be
494    declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
495    which are per-thread.  The latter is useful when FUN calls Winsock
496    functions, which is how run_with_timeout is used in Wget.
497
498    [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
499    the default (wcc386 -3r).  */
500
501 static DWORD WINAPI
502 thread_helper (void *arg)
503 {
504   struct thread_data *td = (struct thread_data *) arg;
505
506   /* Initialize Winsock error to what it was in the parent.  That way
507      the subsequent call to WSAGetLastError will return the same value
508      if td->fun doesn't change Winsock error state.  */
509   WSASetLastError (td->ws_error);
510
511   td->fun (td->arg);
512
513   /* Return Winsock error to the caller, in case FUN ran Winsock
514      code.  */
515   td->ws_error = WSAGetLastError ();
516   return 0;
517 }
518
519 /* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
520    seconds.  Returns true if the function was interrupted with a
521    timeout, false otherwise.
522
523    This works by running FUN in a separate thread and terminating the
524    thread if it doesn't finish in the specified time.  */
525
526 bool
527 run_with_timeout (double seconds, void (*fun) (void *), void *arg)
528 {
529   HANDLE thread_hnd;
530   struct thread_data thread_arg;
531   DWORD thread_id;
532   bool rc;
533
534   DEBUGP (("seconds %.2f, ", seconds));
535
536   if (seconds == 0)
537     {
538     blocking_fallback:
539       fun (arg);
540       return false;
541     }
542
543   thread_arg.fun = fun;
544   thread_arg.arg = arg;
545   thread_arg.ws_error = WSAGetLastError ();
546   thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
547                              &thread_arg, 0, &thread_id);
548   if (!thread_hnd)
549     {
550       DEBUGP (("CreateThread() failed; [%#lx]\n",
551                (unsigned long) GetLastError ()));
552       goto blocking_fallback;
553     }
554
555   if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
556       == WAIT_OBJECT_0)
557     {
558       /* Propagate error state (which is per-thread) to this thread,
559          so the caller can inspect it.  */
560       WSASetLastError (thread_arg.ws_error);
561       DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
562       rc = false;
563     }
564   else
565     {
566       TerminateThread (thread_hnd, 1);
567       rc = true;
568     }
569
570   CloseHandle (thread_hnd);     /* Clear-up after TerminateThread().  */
571   thread_hnd = NULL;
572   return rc;
573 }
574
575 \f
576 #ifdef ENABLE_IPV6
577 /* An inet_ntop implementation that uses WSAAddressToString.
578    Prototype complies with POSIX 1003.1-2004.  This is only used under
579    IPv6 because Wget prints IPv4 addresses using inet_ntoa.  */
580
581 const char *
582 inet_ntop (int af, const void *src, char *dst, socklen_t cnt)
583 {
584   /* struct sockaddr can't accomodate struct sockaddr_in6. */
585   union {
586     struct sockaddr_in6 sin6;
587     struct sockaddr_in sin;
588   } sa;
589   DWORD dstlen = cnt;
590   size_t srcsize;
591
592   xzero (sa);
593   switch (af)
594     {
595     case AF_INET:
596       sa.sin.sin_family = AF_INET;
597       sa.sin.sin_addr = *(struct in_addr *) src;
598       srcsize = sizeof (sa.sin);
599       break;
600     case AF_INET6:
601       sa.sin6.sin6_family = AF_INET6;
602       sa.sin6.sin6_addr = *(struct in6_addr *) src;
603       srcsize = sizeof (sa.sin6);
604       break;
605     default:
606       abort ();
607     }
608
609   if (WSAAddressToString ((struct sockaddr *) &sa, srcsize, NULL, dst, &dstlen) != 0)
610     {
611       errno = WSAGetLastError();
612       return NULL;
613     }
614   return (const char *) dst;
615 }
616 #endif