]> sjero.net Git - wget/blob - src/mswindows.c
ca1f46a8e541abece471743897b4de5855419e04
[wget] / src / mswindows.c
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 #include "wget.h"
52 #include "utils.h"
53 #include "url.h"
54
55 #ifndef errno
56 extern int errno;
57 #endif
58
59 #ifndef ES_SYSTEM_REQUIRED
60 #define ES_SYSTEM_REQUIRED  0x00000001
61 #endif
62
63 #ifndef ES_CONTINUOUS
64 #define ES_CONTINUOUS       0x80000000
65 #endif
66
67
68 /* Defined in log.c.  */
69 void log_request_redirect_output PARAMS ((const char *));
70
71 /* Windows version of xsleep in utils.c.  */
72
73 void
74 xsleep (double seconds)
75 {
76 #ifdef HAVE_USLEEP
77   if (seconds > 1000)
78     {
79       /* Explained in utils.c. */
80       sleep (seconds);
81       seconds -= (long) seconds;
82     }
83   usleep (seconds * 1000000L);
84 #else  /* not HAVE_USLEEP */
85   SleepEx (seconds * 1000, FALSE);
86 #endif /* not HAVE_USLEEP */
87 }
88
89 #if defined(__BORLANDC__) || (defined(_MSC_VER) && _MSC_VER < 1300)
90
91 static inline int
92 char_value (char c, int base)
93 {
94   int value;
95   if (c < '0')
96     return -1;
97   if ('0' <= c && c <= '9')
98     value = c - '0';
99   else if ('a' <= c && c <= 'z')
100     value = c - 'a' + 10;
101   else if ('A' <= c && c <= 'Z')
102     value = c - 'A' + 10;
103   else
104     return -1;
105   if (value >= base)
106     return -1;
107   return value;
108 }
109
110 /* A fairly simple strtoll replacement for MS VC versions that don't
111    supply _strtoi64.  */
112
113 __int64
114 str_to_int64 (const char *nptr, char **endptr, int base)
115 {
116 #define OVERFLOW 9223372036854775807I64
117 #define UNDERFLOW (-OVERFLOW - 1)
118
119   __int64 result = 0;
120   int negative;
121
122   if (base != 0 && (base < 2 || base > 36))
123     {
124       errno = EINVAL;
125       return 0;
126     }
127
128   while (*nptr == ' ' || *nptr == '\t')
129     ++nptr;
130   if (*nptr == '-')
131     {
132       negative = 1;
133       ++nptr;
134     }
135   else if (*nptr == '+')
136     {
137       negative = 0;
138       ++nptr;
139     }
140   else
141     negative = 0;
142
143   /* If base is 0, determine the real base based on the beginning on
144      the number; octal numbers begin with "0", hexadecimal with "0x",
145      and the others are considered octal.  */
146   if (*nptr == '0')
147     {
148       if ((base == 0 || base == 16)
149           &&
150           (*(nptr + 1) == 'x' || *(nptr + 1) == 'X'))
151         {
152           base = 16;
153           nptr += 2;
154         }
155       else if (base == 0)
156         base = 8;
157     }
158   else if (base == 0)
159     base = 10;
160
161   if (!negative)
162     {
163       /* Parse positive number, checking for overflow. */
164       int val;
165       for (; (val = char_value (*nptr, base)) != -1; ++nptr)
166         {
167           __int64 newresult = base * result + val;
168           if (newresult < result)
169             {
170               result = OVERFLOW;
171               errno = ERANGE;
172               break;
173             }
174           result = newresult;
175         }
176     }
177   else
178     {
179       /* Parse negative number, checking for underflow. */
180       int val;
181       for (; (val = char_value (*nptr, base)) != -1; ++nptr)
182         {
183           __int64 newresult = base * result - val;
184           if (newresult > result)
185             {
186               result = UNDERFLOW;
187               errno = ERANGE;
188               break;
189             }
190           result = newresult;
191         }
192     }
193   if (endptr)
194     *endptr = (char *) nptr;
195   return result;
196 }
197
198 #else  /* !defined(__BORLANDC__) && (!defined(_MSC_VER) || _MSC_VER >= 1300) */
199
200 __int64
201 str_to_int64 (const char *nptr, char **endptr, int base)
202 {
203 #ifdef _MSC_VER
204   return _strtoi64 (nptr, endptr, base);
205 #else
206   return strtoll (nptr, endptr, base);
207 #endif
208 }
209
210 #endif /* !defined(__BORLANDC__) && (!defined(_MSC_VER) || _MSC_VER >= 1300) */
211
212 void
213 windows_main_junk (int *argc, char **argv, char **exec_name)
214 {
215   char *p;
216
217   /* Remove .EXE from filename if it has one.  */
218   *exec_name = xstrdup (*exec_name);
219   p = strrchr (*exec_name, '.');
220   if (p)
221     *p = '\0';
222 }
223 \f
224 static void
225 ws_cleanup (void)
226 {
227   WSACleanup ();
228 }
229
230 static void
231 ws_hangup (const char *reason)
232 {
233   /* Whether we arrange our own version of opt.lfilename here.  */
234   int changedp = 0;
235
236   if (!opt.lfilename)
237     {
238       opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
239       changedp = 1;
240     }
241   printf (_("Continuing in background.\n"));
242   if (changedp)
243     printf (_("Output will be written to `%s'.\n"), opt.lfilename);
244
245   log_request_redirect_output (reason);
246
247   /* Detach process from the current console.  Under Windows 9x, if we
248      were launched from a 16-bit process (which is usually the case;
249      command.com is 16-bit) the parent process should resume right away.
250      Under NT or if launched from a 32-process under 9x, this is a futile
251      gesture as the parent will wait for us to terminate before resuming.  */
252   FreeConsole ();
253 }
254
255 /* Construct the name for a named section (a.k.a. `file mapping') object.
256    The returned string is dynamically allocated and needs to be xfree()'d.  */
257 static char *
258 make_section_name (DWORD pid)
259 {
260   return aprintf ("gnu_wget_fake_fork_%lu", pid);
261 }
262
263 /* This structure is used to hold all the data that is exchanged between
264    parent and child.  */
265 struct fake_fork_info
266 {
267   HANDLE event;
268   int changedp;
269   char lfilename[MAX_PATH + 1];
270 };
271
272 /* Determines if we are the child and if so performs the child logic.
273    Return values:
274      < 0  error
275        0  parent
276      > 0  child
277 */
278 static int
279 fake_fork_child (void)
280 {
281   HANDLE section, event;
282   struct fake_fork_info *info;
283   char *name;
284
285   name = make_section_name (GetCurrentProcessId ());
286   section = OpenFileMapping (FILE_MAP_WRITE, FALSE, name);
287   xfree (name);
288   /* It seems that Windows 9x and NT set last-error inconsistently when
289      OpenFileMapping() fails; so we assume it failed because the section
290      object does not exist.  */
291   if (!section)
292     return 0;                   /* We are the parent.  */
293
294   info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
295   if (!info)
296     {
297       CloseHandle (section);
298       return -1;
299     }
300
301   event = info->event;
302
303   if (!opt.lfilename)
304     {
305       opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
306       info->changedp = 1;
307       strncpy (info->lfilename, opt.lfilename, sizeof (info->lfilename));
308       info->lfilename[sizeof (info->lfilename) - 1] = '\0';
309     }
310   else
311     info->changedp = 0;
312
313   UnmapViewOfFile (info);
314   CloseHandle (section);
315
316   /* Inform the parent that we've done our part.  */
317   if (!SetEvent (event))
318     return -1;
319
320   CloseHandle (event);
321   return 1;                     /* We are the child.  */
322 }
323
324 /* Windows doesn't support the fork() call; so we fake it by invoking
325    another copy of Wget with the same arguments with which we were
326    invoked.  The child copy of Wget should perform the same initialization
327    sequence as the parent; so we should have two processes that are
328    essentially identical.  We create a specially named section object that
329    allows the child to distinguish itself from the parent and is used to
330    exchange information between the two processes.  We use an event object
331    for synchronization.  */
332 static void
333 fake_fork (void)
334 {
335   char exe[MAX_PATH + 1];
336   DWORD exe_len, le;
337   SECURITY_ATTRIBUTES sa;
338   HANDLE section, event, h[2];
339   STARTUPINFO si;
340   PROCESS_INFORMATION pi;
341   struct fake_fork_info *info;
342   char *name;
343   BOOL rv;
344
345   event = section = pi.hProcess = pi.hThread = NULL;
346
347   /* Get the fully qualified name of our executable.  This is more reliable
348      than using argv[0].  */
349   exe_len = GetModuleFileName (GetModuleHandle (NULL), exe, sizeof (exe));
350   if (!exe_len || (exe_len >= sizeof (exe)))
351     return;
352
353   sa.nLength = sizeof (sa);
354   sa.lpSecurityDescriptor = NULL;
355   sa.bInheritHandle = TRUE;
356
357   /* Create an anonymous inheritable event object that starts out
358      non-signaled.  */
359   event = CreateEvent (&sa, FALSE, FALSE, NULL);
360   if (!event)
361     return;
362
363   /* Create the child process detached form the current console and in a
364      suspended state.  */
365   memset (&si, 0, sizeof (si));
366   si.cb = sizeof (si);
367   rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE,
368                       CREATE_SUSPENDED | DETACHED_PROCESS,
369                       NULL, NULL, &si, &pi);
370   if (!rv)
371     goto cleanup;
372
373   /* Create a named section object with a name based on the process id of
374      the child.  */
375   name = make_section_name (pi.dwProcessId);
376   section =
377       CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
378                          sizeof (struct fake_fork_info), name);
379   le = GetLastError();
380   xfree (name);
381   /* Fail if the section object already exists (should not happen).  */
382   if (!section || (le == ERROR_ALREADY_EXISTS))
383     {
384       rv = FALSE;
385       goto cleanup;
386     }
387
388   /* Copy the event handle into the section object.  */
389   info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
390   if (!info)
391     {
392       rv = FALSE;
393       goto cleanup;
394     }
395
396   info->event = event;
397
398   UnmapViewOfFile (info);
399
400   /* Start the child process.  */
401   rv = ResumeThread (pi.hThread);
402   if (!rv)
403     {
404       TerminateProcess (pi.hProcess, (DWORD) -1);
405       goto cleanup;
406     }
407
408   /* Wait for the child to signal to us that it has done its part.  If it
409      terminates before signaling us it's an error.  */
410
411   h[0] = event;
412   h[1] = pi.hProcess;
413   rv = WAIT_OBJECT_0 == WaitForMultipleObjects (2, h, FALSE, 5 * 60 * 1000);
414   if (!rv)
415     goto cleanup;
416
417   info = MapViewOfFile (section, FILE_MAP_READ, 0, 0, 0);
418   if (!info)
419     {
420       rv = FALSE;
421       goto cleanup;
422     }
423
424   /* Ensure string is properly terminated.  */
425   if (info->changedp &&
426       !memchr (info->lfilename, '\0', sizeof (info->lfilename)))
427     {
428       rv = FALSE;
429       goto cleanup;
430     }
431
432   printf (_("Continuing in background, pid %lu.\n"), pi.dwProcessId);
433   if (info->changedp)
434     printf (_("Output will be written to `%s'.\n"), info->lfilename);
435
436   UnmapViewOfFile (info);
437
438 cleanup:
439
440   if (event)
441     CloseHandle (event);
442   if (section)
443     CloseHandle (section);
444   if (pi.hThread)
445     CloseHandle (pi.hThread);
446   if (pi.hProcess)
447     CloseHandle (pi.hProcess);
448
449   /* We're the parent.  If all is well, terminate.  */
450   if (rv)
451     exit (0);
452
453   /* We failed, return.  */
454 }
455
456 /* This is the corresponding Windows implementation of the
457    fork_to_background() function in utils.c.  */
458 void
459 fork_to_background (void)
460 {
461   int rv;
462
463   rv = fake_fork_child ();
464   if (rv < 0)
465     {
466       fprintf (stderr, "fake_fork_child() failed\n");
467       abort ();
468     }
469   else if (rv == 0)
470     {
471       /* We're the parent.  */
472       fake_fork ();
473       /* If fake_fork() returns, it failed.  */
474       fprintf (stderr, "fake_fork() failed\n");
475       abort ();
476     }
477   /* If we get here, we're the child.  */
478 }
479
480 static BOOL WINAPI
481 ws_handler (DWORD dwEvent)
482 {
483   switch (dwEvent)
484     {
485 #ifdef CTRLC_BACKGND
486     case CTRL_C_EVENT:
487       ws_hangup ("CTRL+C");
488       return TRUE;
489 #endif
490 #ifdef CTRLBREAK_BACKGND
491     case CTRL_BREAK_EVENT:
492       ws_hangup ("CTRL+Break");
493       return TRUE;
494 #endif
495     default:
496       return FALSE;
497     }
498 }
499
500 static char *title_buf = NULL;
501 static char *curr_url  = NULL;
502 static int old_percentage = -1;
503
504 /* Updates the console title with the URL of the current file being
505    transferred.  */
506 void
507 ws_changetitle (const char *url)
508 {
509   xfree_null (title_buf);
510   xfree_null (curr_url);
511   title_buf = (char *)xmalloc (strlen (url) + 20);
512   curr_url = xstrdup (url);
513   old_percentage = -1;
514   sprintf (title_buf, "Wget %s", curr_url);
515   SetConsoleTitle (title_buf);
516 }
517
518 /* Updates the console title with the percentage of the current file
519    transferred.  */
520 void
521 ws_percenttitle (double percentage_float)
522 {
523   int percentage;
524
525   if (!title_buf || !curr_url)
526     return;
527
528   percentage = (int) percentage_float;
529
530   /* Clamp percentage value.  */
531   if (percentage < 0)
532     percentage = 0;
533   if (percentage > 100)
534     percentage = 100;
535
536   /* Only update the title when the percentage has changed.  */
537   if (percentage == old_percentage)
538     return;
539
540   old_percentage = percentage;
541
542   sprintf (title_buf, "Wget [%d%%] %s", percentage, curr_url);
543   SetConsoleTitle (title_buf);
544 }
545
546 /* Returns a pointer to the fully qualified name of the directory that
547    contains the Wget binary (wget.exe).  The returned path does not have a
548    trailing path separator.  Returns NULL on failure.  */
549 char *
550 ws_mypath (void)
551 {
552   static char *wspathsave = NULL;
553
554   if (!wspathsave)
555     {
556       char buf[MAX_PATH + 1];
557       char *p;
558       DWORD len;
559
560       len = GetModuleFileName (GetModuleHandle (NULL), buf, sizeof (buf));
561       if (!len || (len >= sizeof (buf)))
562         return NULL;
563
564       p = strrchr (buf, PATH_SEPARATOR);
565       if (!p)
566         return NULL;
567
568       *p = '\0';
569       wspathsave = xstrdup (buf);
570     }
571
572   return wspathsave;
573 }
574
575 /* Prevent Windows entering sleep/hibernation-mode while Wget is doing
576    a lengthy transfer.  Windows does not, by default, consider network
577    activity in console-programs as activity!  Works on Win-98/ME/2K
578    and up.  */
579 static void
580 set_sleep_mode (void)
581 {
582   typedef DWORD (WINAPI *func_t) (DWORD);
583   func_t set_exec_state;
584
585   set_exec_state =
586       (func_t) GetProcAddress (GetModuleHandle ("KERNEL32.DLL"),
587                                "SetThreadExecutionState");
588
589   if (set_exec_state)
590     set_exec_state (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
591 }
592
593 /* Perform Windows specific initialization.  */
594 void
595 ws_startup (void)
596 {
597   WORD requested;
598   WSADATA data;
599   int err;
600
601   requested = MAKEWORD (1, 1);
602   err = WSAStartup (requested, &data);
603   if (err != 0)
604     {
605       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
606                exec_name);
607       exit (1);
608     }
609
610   if (data.wVersion < requested)
611     {
612       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
613                exec_name);
614       WSACleanup ();
615       exit (1);
616     }
617
618   atexit (ws_cleanup);
619   set_sleep_mode ();
620   SetConsoleCtrlHandler (ws_handler, TRUE);
621 }
622
623 /* Replacement utime function for buggy Borland C++Builder 5.5 compiler.
624    (The Borland utime function only works on Windows NT.)  */
625
626 #ifdef HACK_BCC_UTIME_BUG
627 int
628 borland_utime (const char *path, const struct utimbuf *times)
629 {
630   int fd;
631   int res;
632   struct ftime ft;
633   struct tm *ptr_tm;
634
635   if ((fd = open (path, O_RDWR)) < 0)
636     return -1;
637
638   ptr_tm = localtime (&times->modtime);
639   ft.ft_tsec = ptr_tm->tm_sec >> 1;
640   ft.ft_min = ptr_tm->tm_min;
641   ft.ft_hour = ptr_tm->tm_hour;
642   ft.ft_day = ptr_tm->tm_mday;
643   ft.ft_month = ptr_tm->tm_mon + 1;
644   ft.ft_year = ptr_tm->tm_year - 80;
645   res = setftime (fd, &ft);
646   close (fd);
647   return res;
648 }
649 #endif
650 \f
651 /* run_with_timeout Windows implementation.  */
652
653 /* Stack size 0 uses default thread stack-size (reserve+commit).
654    Determined by what's in the PE header.  */
655 #define THREAD_STACK_SIZE  0
656
657 struct thread_data
658 {
659   void (*fun) (void *);
660   void *arg;
661   DWORD ws_error;
662 };
663
664 /* The callback that runs FUN(ARG) in a separate thread.  This
665    function exists for two reasons: a) to not require FUN to be
666    declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
667    which are per-thread.  The latter is useful when FUN calls Winsock
668    functions, which is how run_with_timeout is used in Wget.
669
670    [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
671    the default (wcc386 -3r).  */
672
673 static DWORD WINAPI
674 thread_helper (void *arg)
675 {
676   struct thread_data *td = (struct thread_data *) arg;
677
678   /* Initialize Winsock error to what it was in the parent.  That way
679      the subsequent call to WSAGetLastError will return the same value
680      if td->fun doesn't change Winsock error state.  */
681   WSASetLastError (td->ws_error);
682
683   td->fun (td->arg);
684
685   /* Return Winsock error to the caller, in case FUN ran Winsock
686      code.  */
687   td->ws_error = WSAGetLastError ();
688   return 0;
689 }
690
691 /* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
692    seconds.  Returns non-zero if the function was interrupted with a
693    timeout, zero otherwise.
694
695    This works by running FUN in a separate thread and terminating the
696    thread if it doesn't finish in the specified time.  */
697
698 int
699 run_with_timeout (double seconds, void (*fun) (void *), void *arg)
700 {
701   static HANDLE thread_hnd = NULL;
702   struct thread_data thread_arg;
703   DWORD thread_id;
704   int rc = 0;
705
706   DEBUGP (("seconds %.2f, ", seconds));
707
708   if (seconds == 0)
709     {
710     blocking_fallback:
711       fun (arg);
712       return 0;
713     }
714
715   /* Should never happen, but test for recursivety anyway.  */
716   assert (thread_hnd == NULL);
717
718   thread_arg.fun = fun;
719   thread_arg.arg = arg;
720   thread_arg.ws_error = WSAGetLastError ();
721   thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
722                              &thread_arg, 0, &thread_id);
723   if (!thread_hnd)
724     {
725       DEBUGP (("CreateThread() failed; %s\n", strerror (GetLastError ())));
726       goto blocking_fallback;
727     }
728
729   if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
730       == WAIT_OBJECT_0)
731     {
732       /* Propagate error state (which is per-thread) to this thread,
733          so the caller can inspect it.  */
734       WSASetLastError (thread_arg.ws_error);
735       DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
736       rc = 0;
737     }
738   else
739     {
740       TerminateThread (thread_hnd, 1);
741       rc = 1;
742     }
743
744   CloseHandle (thread_hnd);     /* Clear-up after TerminateThread().  */
745   thread_hnd = NULL;
746   return rc;
747 }