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