]> sjero.net Git - wget/blob - src/mswindows.c
[svn] Fix a possible race condition when opening files.
[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   fprintf (stderr, _("Continuing in background.\n"));
234   log_request_redirect_output (reason);
235
236   /* Detach process from the current console.  Under Windows 9x, if we
237      were launched from a 16-bit process (which is usually the case;
238      command.com is 16-bit) the parent process should resume right away.
239      Under NT or if launched from a 32-process under 9x, this is a futile
240      gesture as the parent will wait for us to terminate before resuming.  */
241   FreeConsole ();
242 }
243
244 /* Construct the name for a named section (a.k.a. `file mapping') object.
245    The returned string is dynamically allocated and needs to be xfree()'d.  */
246 static char *
247 make_section_name (DWORD pid)
248 {
249   return aprintf ("gnu_wget_fake_fork_%lu", pid);
250 }
251
252 /* This structure is used to hold all the data that is exchanged between
253    parent and child.  */
254 struct fake_fork_info
255 {
256   HANDLE event;
257   int logfile_changed;
258   char lfilename[MAX_PATH + 1];
259 };
260
261 /* Determines if we are the child and if so performs the child logic.
262    Return values:
263      < 0  error
264        0  parent
265      > 0  child
266 */
267 static int
268 fake_fork_child (void)
269 {
270   HANDLE section, event;
271   struct fake_fork_info *info;
272   char *name;
273
274   name = make_section_name (GetCurrentProcessId ());
275   section = OpenFileMapping (FILE_MAP_WRITE, FALSE, name);
276   xfree (name);
277   /* It seems that Windows 9x and NT set last-error inconsistently when
278      OpenFileMapping() fails; so we assume it failed because the section
279      object does not exist.  */
280   if (!section)
281     return 0;                   /* We are the parent.  */
282
283   info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
284   if (!info)
285     {
286       CloseHandle (section);
287       return -1;
288     }
289
290   event = info->event;
291
292   info->logfile_changed = 0;
293   if (!opt.lfilename)
294     {
295       /* See utils:fork_to_background for explanation. */
296       FILE *new_log_fp = unique_create (DEFAULT_LOGFILE, 0, &opt.lfilename);
297       if (new_log_fp)
298         {
299           info->logfile_changed = 1;
300           strncpy (info->lfilename, opt.lfilename, sizeof (info->lfilename));
301           info->lfilename[sizeof (info->lfilename) - 1] = '\0';
302           fclose (new_log_fp);
303         }
304     }
305
306   UnmapViewOfFile (info);
307   CloseHandle (section);
308
309   /* Inform the parent that we've done our part.  */
310   if (!SetEvent (event))
311     return -1;
312
313   CloseHandle (event);
314   return 1;                     /* We are the child.  */
315 }
316
317 /* Windows doesn't support the fork() call; so we fake it by invoking
318    another copy of Wget with the same arguments with which we were
319    invoked.  The child copy of Wget should perform the same initialization
320    sequence as the parent; so we should have two processes that are
321    essentially identical.  We create a specially named section object that
322    allows the child to distinguish itself from the parent and is used to
323    exchange information between the two processes.  We use an event object
324    for synchronization.  */
325 static void
326 fake_fork (void)
327 {
328   char exe[MAX_PATH + 1];
329   DWORD exe_len, le;
330   SECURITY_ATTRIBUTES sa;
331   HANDLE section, event, h[2];
332   STARTUPINFO si;
333   PROCESS_INFORMATION pi;
334   struct fake_fork_info *info;
335   char *name;
336   BOOL rv;
337
338   event = section = pi.hProcess = pi.hThread = NULL;
339
340   /* Get the fully qualified name of our executable.  This is more reliable
341      than using argv[0].  */
342   exe_len = GetModuleFileName (GetModuleHandle (NULL), exe, sizeof (exe));
343   if (!exe_len || (exe_len >= sizeof (exe)))
344     return;
345
346   sa.nLength = sizeof (sa);
347   sa.lpSecurityDescriptor = NULL;
348   sa.bInheritHandle = TRUE;
349
350   /* Create an anonymous inheritable event object that starts out
351      non-signaled.  */
352   event = CreateEvent (&sa, FALSE, FALSE, NULL);
353   if (!event)
354     return;
355
356   /* Create the child process detached form the current console and in a
357      suspended state.  */
358   memset (&si, 0, sizeof (si));
359   si.cb = sizeof (si);
360   rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE,
361                       CREATE_SUSPENDED | DETACHED_PROCESS,
362                       NULL, NULL, &si, &pi);
363   if (!rv)
364     goto cleanup;
365
366   /* Create a named section object with a name based on the process id of
367      the child.  */
368   name = make_section_name (pi.dwProcessId);
369   section =
370       CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
371                          sizeof (struct fake_fork_info), name);
372   le = GetLastError();
373   xfree (name);
374   /* Fail if the section object already exists (should not happen).  */
375   if (!section || (le == ERROR_ALREADY_EXISTS))
376     {
377       rv = FALSE;
378       goto cleanup;
379     }
380
381   /* Copy the event handle into the section object.  */
382   info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
383   if (!info)
384     {
385       rv = FALSE;
386       goto cleanup;
387     }
388
389   info->event = event;
390
391   UnmapViewOfFile (info);
392
393   /* Start the child process.  */
394   rv = ResumeThread (pi.hThread);
395   if (!rv)
396     {
397       TerminateProcess (pi.hProcess, (DWORD) -1);
398       goto cleanup;
399     }
400
401   /* Wait for the child to signal to us that it has done its part.  If it
402      terminates before signaling us it's an error.  */
403
404   h[0] = event;
405   h[1] = pi.hProcess;
406   rv = WAIT_OBJECT_0 == WaitForMultipleObjects (2, h, FALSE, 5 * 60 * 1000);
407   if (!rv)
408     goto cleanup;
409
410   info = MapViewOfFile (section, FILE_MAP_READ, 0, 0, 0);
411   if (!info)
412     {
413       rv = FALSE;
414       goto cleanup;
415     }
416
417   /* Ensure string is properly terminated.  */
418   if (info->logfile_changed &&
419       !memchr (info->lfilename, '\0', sizeof (info->lfilename)))
420     {
421       rv = FALSE;
422       goto cleanup;
423     }
424
425   printf (_("Continuing in background, pid %lu.\n"), pi.dwProcessId);
426   if (info->logfile_changed)
427     printf (_("Output will be written to `%s'.\n"), info->lfilename);
428
429   UnmapViewOfFile (info);
430
431 cleanup:
432
433   if (event)
434     CloseHandle (event);
435   if (section)
436     CloseHandle (section);
437   if (pi.hThread)
438     CloseHandle (pi.hThread);
439   if (pi.hProcess)
440     CloseHandle (pi.hProcess);
441
442   /* We're the parent.  If all is well, terminate.  */
443   if (rv)
444     exit (0);
445
446   /* We failed, return.  */
447 }
448
449 /* This is the corresponding Windows implementation of the
450    fork_to_background() function in utils.c.  */
451 void
452 fork_to_background (void)
453 {
454   int rv;
455
456   rv = fake_fork_child ();
457   if (rv < 0)
458     {
459       fprintf (stderr, "fake_fork_child() failed\n");
460       abort ();
461     }
462   else if (rv == 0)
463     {
464       /* We're the parent.  */
465       fake_fork ();
466       /* If fake_fork() returns, it failed.  */
467       fprintf (stderr, "fake_fork() failed\n");
468       abort ();
469     }
470   /* If we get here, we're the child.  */
471 }
472
473 static BOOL WINAPI
474 ws_handler (DWORD dwEvent)
475 {
476   switch (dwEvent)
477     {
478 #ifdef CTRLC_BACKGND
479     case CTRL_C_EVENT:
480       ws_hangup ("CTRL+C");
481       return TRUE;
482 #endif
483 #ifdef CTRLBREAK_BACKGND
484     case CTRL_BREAK_EVENT:
485       ws_hangup ("CTRL+Break");
486       return TRUE;
487 #endif
488     default:
489       return FALSE;
490     }
491 }
492
493 static char *title_buf = NULL;
494 static char *curr_url  = NULL;
495 static int old_percentage = -1;
496
497 /* Updates the console title with the URL of the current file being
498    transferred.  */
499 void
500 ws_changetitle (const char *url)
501 {
502   xfree_null (title_buf);
503   xfree_null (curr_url);
504   title_buf = (char *)xmalloc (strlen (url) + 20);
505   curr_url = xstrdup (url);
506   old_percentage = -1;
507   sprintf (title_buf, "Wget %s", curr_url);
508   SetConsoleTitle (title_buf);
509 }
510
511 /* Updates the console title with the percentage of the current file
512    transferred.  */
513 void
514 ws_percenttitle (double percentage_float)
515 {
516   int percentage;
517
518   if (!title_buf || !curr_url)
519     return;
520
521   percentage = (int) percentage_float;
522
523   /* Clamp percentage value.  */
524   if (percentage < 0)
525     percentage = 0;
526   if (percentage > 100)
527     percentage = 100;
528
529   /* Only update the title when the percentage has changed.  */
530   if (percentage == old_percentage)
531     return;
532
533   old_percentage = percentage;
534
535   sprintf (title_buf, "Wget [%d%%] %s", percentage, curr_url);
536   SetConsoleTitle (title_buf);
537 }
538
539 /* Returns a pointer to the fully qualified name of the directory that
540    contains the Wget binary (wget.exe).  The returned path does not have a
541    trailing path separator.  Returns NULL on failure.  */
542 char *
543 ws_mypath (void)
544 {
545   static char *wspathsave = NULL;
546
547   if (!wspathsave)
548     {
549       char buf[MAX_PATH + 1];
550       char *p;
551       DWORD len;
552
553       len = GetModuleFileName (GetModuleHandle (NULL), buf, sizeof (buf));
554       if (!len || (len >= sizeof (buf)))
555         return NULL;
556
557       p = strrchr (buf, PATH_SEPARATOR);
558       if (!p)
559         return NULL;
560
561       *p = '\0';
562       wspathsave = xstrdup (buf);
563     }
564
565   return wspathsave;
566 }
567
568 /* Prevent Windows entering sleep/hibernation-mode while Wget is doing
569    a lengthy transfer.  Windows does not, by default, consider network
570    activity in console-programs as activity!  Works on Win-98/ME/2K
571    and up.  */
572 static void
573 set_sleep_mode (void)
574 {
575   typedef DWORD (WINAPI *func_t) (DWORD);
576   func_t set_exec_state;
577
578   set_exec_state =
579       (func_t) GetProcAddress (GetModuleHandle ("KERNEL32.DLL"),
580                                "SetThreadExecutionState");
581
582   if (set_exec_state)
583     set_exec_state (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
584 }
585
586 /* Perform Windows specific initialization.  */
587 void
588 ws_startup (void)
589 {
590   WORD requested;
591   WSADATA data;
592   int err;
593
594   requested = MAKEWORD (1, 1);
595   err = WSAStartup (requested, &data);
596   if (err != 0)
597     {
598       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
599                exec_name);
600       exit (1);
601     }
602
603   if (data.wVersion < requested)
604     {
605       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
606                exec_name);
607       WSACleanup ();
608       exit (1);
609     }
610
611   atexit (ws_cleanup);
612   set_sleep_mode ();
613   SetConsoleCtrlHandler (ws_handler, TRUE);
614 }
615
616 /* Replacement utime function for buggy Borland C++Builder 5.5 compiler.
617    (The Borland utime function only works on Windows NT.)  */
618
619 #ifdef HACK_BCC_UTIME_BUG
620 int
621 borland_utime (const char *path, const struct utimbuf *times)
622 {
623   int fd;
624   int res;
625   struct ftime ft;
626   struct tm *ptr_tm;
627
628   if ((fd = open (path, O_RDWR)) < 0)
629     return -1;
630
631   ptr_tm = localtime (&times->modtime);
632   ft.ft_tsec = ptr_tm->tm_sec >> 1;
633   ft.ft_min = ptr_tm->tm_min;
634   ft.ft_hour = ptr_tm->tm_hour;
635   ft.ft_day = ptr_tm->tm_mday;
636   ft.ft_month = ptr_tm->tm_mon + 1;
637   ft.ft_year = ptr_tm->tm_year - 80;
638   res = setftime (fd, &ft);
639   close (fd);
640   return res;
641 }
642 #endif
643 \f
644 /* run_with_timeout Windows implementation.  */
645
646 /* Stack size 0 uses default thread stack-size (reserve+commit).
647    Determined by what's in the PE header.  */
648 #define THREAD_STACK_SIZE  0
649
650 struct thread_data
651 {
652   void (*fun) (void *);
653   void *arg;
654   DWORD ws_error;
655 };
656
657 /* The callback that runs FUN(ARG) in a separate thread.  This
658    function exists for two reasons: a) to not require FUN to be
659    declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
660    which are per-thread.  The latter is useful when FUN calls Winsock
661    functions, which is how run_with_timeout is used in Wget.
662
663    [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
664    the default (wcc386 -3r).  */
665
666 static DWORD WINAPI
667 thread_helper (void *arg)
668 {
669   struct thread_data *td = (struct thread_data *) arg;
670
671   /* Initialize Winsock error to what it was in the parent.  That way
672      the subsequent call to WSAGetLastError will return the same value
673      if td->fun doesn't change Winsock error state.  */
674   WSASetLastError (td->ws_error);
675
676   td->fun (td->arg);
677
678   /* Return Winsock error to the caller, in case FUN ran Winsock
679      code.  */
680   td->ws_error = WSAGetLastError ();
681   return 0;
682 }
683
684 /* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
685    seconds.  Returns non-zero if the function was interrupted with a
686    timeout, zero otherwise.
687
688    This works by running FUN in a separate thread and terminating the
689    thread if it doesn't finish in the specified time.  */
690
691 int
692 run_with_timeout (double seconds, void (*fun) (void *), void *arg)
693 {
694   static HANDLE thread_hnd = NULL;
695   struct thread_data thread_arg;
696   DWORD thread_id;
697   int rc = 0;
698
699   DEBUGP (("seconds %.2f, ", seconds));
700
701   if (seconds == 0)
702     {
703     blocking_fallback:
704       fun (arg);
705       return 0;
706     }
707
708   /* Should never happen, but test for recursivety anyway.  */
709   assert (thread_hnd == NULL);
710
711   thread_arg.fun = fun;
712   thread_arg.arg = arg;
713   thread_arg.ws_error = WSAGetLastError ();
714   thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
715                              &thread_arg, 0, &thread_id);
716   if (!thread_hnd)
717     {
718       DEBUGP (("CreateThread() failed; %s\n", strerror (GetLastError ())));
719       goto blocking_fallback;
720     }
721
722   if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
723       == WAIT_OBJECT_0)
724     {
725       /* Propagate error state (which is per-thread) to this thread,
726          so the caller can inspect it.  */
727       WSASetLastError (thread_arg.ws_error);
728       DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
729       rc = 0;
730     }
731   else
732     {
733       TerminateThread (thread_hnd, 1);
734       rc = 1;
735     }
736
737   CloseHandle (thread_hnd);     /* Clear-up after TerminateThread().  */
738   thread_hnd = NULL;
739   return rc;
740 }