source: trunk/essentials/net-misc/wget/src/mswindows.c

Last change on this file was 3440, checked in by bird, 18 years ago

wget 1.10.2

File size: 20.7 KB
Line 
1/* mswindows.c -- Windows-specific support
2 Copyright (C) 1995, 1996, 1997, 1998, 2004
3 Free Software Foundation, Inc.
4
5This file is part of GNU Wget.
6
7GNU Wget is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or
10(at your option) any later version.
11
12GNU Wget is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with Wget; if not, write to the Free Software
19Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21In addition, as a special exception, the Free Software Foundation
22gives permission to link the code of its release of Wget with the
23OpenSSL project's "OpenSSL" library (or with modified versions of it
24that use the same license as the "OpenSSL" library), and distribute
25the linked executables. You must obey the GNU General Public License
26in all respects for all of the code used other than "OpenSSL". If you
27modify this file, you may extend this exception to your version of the
28file, but you are not obligated to do so. If you do not wish to do
29so, 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#define INHIBIT_WRAP /* avoid wrapping of socket, bind, ... */
52
53#include "wget.h"
54#include "utils.h"
55#include "url.h"
56
57#ifndef errno
58extern int errno;
59#endif
60
61#ifndef ES_SYSTEM_REQUIRED
62#define ES_SYSTEM_REQUIRED 0x00000001
63#endif
64
65#ifndef ES_CONTINUOUS
66#define ES_CONTINUOUS 0x80000000
67#endif
68
69
70/* Defined in log.c. */
71void log_request_redirect_output PARAMS ((const char *));
72
73/* Windows version of xsleep in utils.c. */
74
75void
76xsleep (double seconds)
77{
78#ifdef HAVE_USLEEP
79 if (seconds > 1000)
80 {
81 /* Explained in utils.c. */
82 sleep (seconds);
83 seconds -= (long) seconds;
84 }
85 usleep (seconds * 1000000L);
86#else /* not HAVE_USLEEP */
87 SleepEx ((DWORD) (seconds * 1000 + .5), FALSE);
88#endif /* not HAVE_USLEEP */
89}
90
91void
92windows_main (int *argc, char **argv, char **exec_name)
93{
94 char *p;
95
96 /* Remove .EXE from filename if it has one. */
97 *exec_name = xstrdup (*exec_name);
98 p = strrchr (*exec_name, '.');
99 if (p)
100 *p = '\0';
101}
102
103
104static void
105ws_cleanup (void)
106{
107 WSACleanup ();
108}
109
110static void
111ws_hangup (const char *reason)
112{
113 fprintf (stderr, _("Continuing in background.\n"));
114 log_request_redirect_output (reason);
115
116 /* Detach process from the current console. Under Windows 9x, if we
117 were launched from a 16-bit process (which is usually the case;
118 command.com is 16-bit) the parent process should resume right away.
119 Under NT or if launched from a 32-process under 9x, this is a futile
120 gesture as the parent will wait for us to terminate before resuming. */
121 FreeConsole ();
122}
123
124/* Construct the name for a named section (a.k.a. `file mapping') object.
125 The returned string is dynamically allocated and needs to be xfree()'d. */
126static char *
127make_section_name (DWORD pid)
128{
129 return aprintf ("gnu_wget_fake_fork_%lu", pid);
130}
131
132/* This structure is used to hold all the data that is exchanged between
133 parent and child. */
134struct fake_fork_info
135{
136 HANDLE event;
137 int logfile_changed;
138 char lfilename[MAX_PATH + 1];
139};
140
141/* Determines if we are the child and if so performs the child logic.
142 Return values:
143 < 0 error
144 0 parent
145 > 0 child
146*/
147static int
148fake_fork_child (void)
149{
150 HANDLE section, event;
151 struct fake_fork_info *info;
152 char *name;
153
154 name = make_section_name (GetCurrentProcessId ());
155 section = OpenFileMapping (FILE_MAP_WRITE, FALSE, name);
156 xfree (name);
157 /* It seems that Windows 9x and NT set last-error inconsistently when
158 OpenFileMapping() fails; so we assume it failed because the section
159 object does not exist. */
160 if (!section)
161 return 0; /* We are the parent. */
162
163 info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
164 if (!info)
165 {
166 CloseHandle (section);
167 return -1;
168 }
169
170 event = info->event;
171
172 info->logfile_changed = 0;
173 if (!opt.lfilename)
174 {
175 /* See utils:fork_to_background for explanation. */
176 FILE *new_log_fp = unique_create (DEFAULT_LOGFILE, 0, &opt.lfilename);
177 if (new_log_fp)
178 {
179 info->logfile_changed = 1;
180 strncpy (info->lfilename, opt.lfilename, sizeof (info->lfilename));
181 info->lfilename[sizeof (info->lfilename) - 1] = '\0';
182 fclose (new_log_fp);
183 }
184 }
185
186 UnmapViewOfFile (info);
187 CloseHandle (section);
188
189 /* Inform the parent that we've done our part. */
190 if (!SetEvent (event))
191 return -1;
192
193 CloseHandle (event);
194 return 1; /* We are the child. */
195}
196
197/* Windows doesn't support the fork() call; so we fake it by invoking
198 another copy of Wget with the same arguments with which we were
199 invoked. The child copy of Wget should perform the same initialization
200 sequence as the parent; so we should have two processes that are
201 essentially identical. We create a specially named section object that
202 allows the child to distinguish itself from the parent and is used to
203 exchange information between the two processes. We use an event object
204 for synchronization. */
205static void
206fake_fork (void)
207{
208 char exe[MAX_PATH + 1];
209 DWORD exe_len, le;
210 SECURITY_ATTRIBUTES sa;
211 HANDLE section, event, h[2];
212 STARTUPINFO si;
213 PROCESS_INFORMATION pi;
214 struct fake_fork_info *info;
215 char *name;
216 BOOL rv;
217
218 section = pi.hProcess = pi.hThread = NULL;
219
220 /* Get the fully qualified name of our executable. This is more reliable
221 than using argv[0]. */
222 exe_len = GetModuleFileName (GetModuleHandle (NULL), exe, sizeof (exe));
223 if (!exe_len || (exe_len >= sizeof (exe)))
224 return;
225
226 sa.nLength = sizeof (sa);
227 sa.lpSecurityDescriptor = NULL;
228 sa.bInheritHandle = TRUE;
229
230 /* Create an anonymous inheritable event object that starts out
231 non-signaled. */
232 event = CreateEvent (&sa, FALSE, FALSE, NULL);
233 if (!event)
234 return;
235
236 /* Create the child process detached form the current console and in a
237 suspended state. */
238 xzero (si);
239 si.cb = sizeof (si);
240 rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE,
241 CREATE_SUSPENDED | DETACHED_PROCESS,
242 NULL, NULL, &si, &pi);
243 if (!rv)
244 goto cleanup;
245
246 /* Create a named section object with a name based on the process id of
247 the child. */
248 name = make_section_name (pi.dwProcessId);
249 section =
250 CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
251 sizeof (struct fake_fork_info), name);
252 le = GetLastError();
253 xfree (name);
254 /* Fail if the section object already exists (should not happen). */
255 if (!section || (le == ERROR_ALREADY_EXISTS))
256 {
257 rv = FALSE;
258 goto cleanup;
259 }
260
261 /* Copy the event handle into the section object. */
262 info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
263 if (!info)
264 {
265 rv = FALSE;
266 goto cleanup;
267 }
268
269 info->event = event;
270
271 UnmapViewOfFile (info);
272
273 /* Start the child process. */
274 rv = ResumeThread (pi.hThread);
275 if (!rv)
276 {
277 TerminateProcess (pi.hProcess, (DWORD) -1);
278 goto cleanup;
279 }
280
281 /* Wait for the child to signal to us that it has done its part. If it
282 terminates before signaling us it's an error. */
283
284 h[0] = event;
285 h[1] = pi.hProcess;
286 rv = WAIT_OBJECT_0 == WaitForMultipleObjects (2, h, FALSE, 5 * 60 * 1000);
287 if (!rv)
288 goto cleanup;
289
290 info = MapViewOfFile (section, FILE_MAP_READ, 0, 0, 0);
291 if (!info)
292 {
293 rv = FALSE;
294 goto cleanup;
295 }
296
297 /* Ensure string is properly terminated. */
298 if (info->logfile_changed &&
299 !memchr (info->lfilename, '\0', sizeof (info->lfilename)))
300 {
301 rv = FALSE;
302 goto cleanup;
303 }
304
305 printf (_("Continuing in background, pid %lu.\n"), pi.dwProcessId);
306 if (info->logfile_changed)
307 printf (_("Output will be written to `%s'.\n"), info->lfilename);
308
309 UnmapViewOfFile (info);
310
311cleanup:
312
313 if (event)
314 CloseHandle (event);
315 if (section)
316 CloseHandle (section);
317 if (pi.hThread)
318 CloseHandle (pi.hThread);
319 if (pi.hProcess)
320 CloseHandle (pi.hProcess);
321
322 /* We're the parent. If all is well, terminate. */
323 if (rv)
324 exit (0);
325
326 /* We failed, return. */
327}
328
329/* This is the corresponding Windows implementation of the
330 fork_to_background() function in utils.c. */
331void
332fork_to_background (void)
333{
334 int rv;
335
336 rv = fake_fork_child ();
337 if (rv < 0)
338 {
339 fprintf (stderr, "fake_fork_child() failed\n");
340 abort ();
341 }
342 else if (rv == 0)
343 {
344 /* We're the parent. */
345 fake_fork ();
346 /* If fake_fork() returns, it failed. */
347 fprintf (stderr, "fake_fork() failed\n");
348 abort ();
349 }
350 /* If we get here, we're the child. */
351}
352
353static BOOL WINAPI
354ws_handler (DWORD dwEvent)
355{
356 switch (dwEvent)
357 {
358#ifdef CTRLC_BACKGND
359 case CTRL_C_EVENT:
360 ws_hangup ("CTRL+C");
361 return TRUE;
362#endif
363#ifdef CTRLBREAK_BACKGND
364 case CTRL_BREAK_EVENT:
365 ws_hangup ("CTRL+Break");
366 return TRUE;
367#endif
368 default:
369 return FALSE;
370 }
371}
372
373static char *title_buf = NULL;
374static char *curr_url = NULL;
375static int old_percentage = -1;
376
377/* Updates the console title with the URL of the current file being
378 transferred. */
379void
380ws_changetitle (const char *url)
381{
382 xfree_null (title_buf);
383 xfree_null (curr_url);
384 title_buf = (char *)xmalloc (strlen (url) + 20);
385 curr_url = xstrdup (url);
386 old_percentage = -1;
387 sprintf (title_buf, "Wget %s", curr_url);
388 SetConsoleTitle (title_buf);
389}
390
391/* Updates the console title with the percentage of the current file
392 transferred. */
393void
394ws_percenttitle (double percentage_float)
395{
396 int percentage;
397
398 if (!title_buf || !curr_url)
399 return;
400
401 percentage = (int) percentage_float;
402
403 /* Clamp percentage value. */
404 if (percentage < 0)
405 percentage = 0;
406 if (percentage > 100)
407 percentage = 100;
408
409 /* Only update the title when the percentage has changed. */
410 if (percentage == old_percentage)
411 return;
412
413 old_percentage = percentage;
414
415 sprintf (title_buf, "Wget [%d%%] %s", percentage, curr_url);
416 SetConsoleTitle (title_buf);
417}
418
419/* Returns a pointer to the fully qualified name of the directory that
420 contains the Wget binary (wget.exe). The returned path does not have a
421 trailing path separator. Returns NULL on failure. */
422char *
423ws_mypath (void)
424{
425 static char *wspathsave = NULL;
426
427 if (!wspathsave)
428 {
429 char buf[MAX_PATH + 1];
430 char *p;
431 DWORD len;
432
433 len = GetModuleFileName (GetModuleHandle (NULL), buf, sizeof (buf));
434 if (!len || (len >= sizeof (buf)))
435 return NULL;
436
437 p = strrchr (buf, PATH_SEPARATOR);
438 if (!p)
439 return NULL;
440
441 *p = '\0';
442 wspathsave = xstrdup (buf);
443 }
444
445 return wspathsave;
446}
447
448/* Prevent Windows entering sleep/hibernation-mode while Wget is doing
449 a lengthy transfer. Windows does not, by default, consider network
450 activity in console-programs as activity! Works on Win-98/ME/2K
451 and up. */
452static void
453set_sleep_mode (void)
454{
455 typedef DWORD (WINAPI *func_t) (DWORD);
456 func_t set_exec_state;
457
458 set_exec_state =
459 (func_t) GetProcAddress (GetModuleHandle ("KERNEL32.DLL"),
460 "SetThreadExecutionState");
461
462 if (set_exec_state)
463 set_exec_state (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
464}
465
466/* Perform Windows specific initialization. */
467void
468ws_startup (void)
469{
470 WSADATA data;
471 WORD requested = MAKEWORD (1, 1);
472 int err = WSAStartup (requested, &data);
473 if (err != 0)
474 {
475 fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
476 exec_name);
477 exit (1);
478 }
479
480 if (data.wVersion < requested)
481 {
482 fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
483 exec_name);
484 WSACleanup ();
485 exit (1);
486 }
487
488 atexit (ws_cleanup);
489 set_sleep_mode ();
490 SetConsoleCtrlHandler (ws_handler, TRUE);
491}
492
493/* Replacement utime function for buggy Borland C++Builder 5.5 compiler.
494 (The Borland utime function only works on Windows NT.) */
495
496#ifdef HACK_BCC_UTIME_BUG
497int
498borland_utime (const char *path, const struct utimbuf *times)
499{
500 int fd;
501 int res;
502 struct ftime ft;
503 struct tm *ptr_tm;
504
505 if ((fd = open (path, O_RDWR)) < 0)
506 return -1;
507
508 ptr_tm = localtime (&times->modtime);
509 ft.ft_tsec = ptr_tm->tm_sec >> 1;
510 ft.ft_min = ptr_tm->tm_min;
511 ft.ft_hour = ptr_tm->tm_hour;
512 ft.ft_day = ptr_tm->tm_mday;
513 ft.ft_month = ptr_tm->tm_mon + 1;
514 ft.ft_year = ptr_tm->tm_year - 80;
515 res = setftime (fd, &ft);
516 close (fd);
517 return res;
518}
519#endif
520
521
522/* run_with_timeout Windows implementation. */
523
524/* Stack size 0 uses default thread stack-size (reserve+commit).
525 Determined by what's in the PE header. */
526#define THREAD_STACK_SIZE 0
527
528struct thread_data
529{
530 void (*fun) (void *);
531 void *arg;
532 DWORD ws_error;
533};
534
535/* The callback that runs FUN(ARG) in a separate thread. This
536 function exists for two reasons: a) to not require FUN to be
537 declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
538 which are per-thread. The latter is useful when FUN calls Winsock
539 functions, which is how run_with_timeout is used in Wget.
540
541 [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
542 the default (wcc386 -3r). */
543
544static DWORD WINAPI
545thread_helper (void *arg)
546{
547 struct thread_data *td = (struct thread_data *) arg;
548
549 /* Initialize Winsock error to what it was in the parent. That way
550 the subsequent call to WSAGetLastError will return the same value
551 if td->fun doesn't change Winsock error state. */
552 WSASetLastError (td->ws_error);
553
554 td->fun (td->arg);
555
556 /* Return Winsock error to the caller, in case FUN ran Winsock
557 code. */
558 td->ws_error = WSAGetLastError ();
559 return 0;
560}
561
562/* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
563 seconds. Returns non-zero if the function was interrupted with a
564 timeout, zero otherwise.
565
566 This works by running FUN in a separate thread and terminating the
567 thread if it doesn't finish in the specified time. */
568
569int
570run_with_timeout (double seconds, void (*fun) (void *), void *arg)
571{
572 static HANDLE thread_hnd = NULL;
573 struct thread_data thread_arg;
574 DWORD thread_id;
575 int rc;
576
577 DEBUGP (("seconds %.2f, ", seconds));
578
579 if (seconds == 0)
580 {
581 blocking_fallback:
582 fun (arg);
583 return 0;
584 }
585
586 /* Should never happen, but test for recursivety anyway. */
587 assert (thread_hnd == NULL);
588
589 thread_arg.fun = fun;
590 thread_arg.arg = arg;
591 thread_arg.ws_error = WSAGetLastError ();
592 thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
593 &thread_arg, 0, &thread_id);
594 if (!thread_hnd)
595 {
596 DEBUGP (("CreateThread() failed; %s\n", strerror (GetLastError ())));
597 goto blocking_fallback;
598 }
599
600 if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
601 == WAIT_OBJECT_0)
602 {
603 /* Propagate error state (which is per-thread) to this thread,
604 so the caller can inspect it. */
605 WSASetLastError (thread_arg.ws_error);
606 DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
607 rc = 0;
608 }
609 else
610 {
611 TerminateThread (thread_hnd, 1);
612 rc = 1;
613 }
614
615 CloseHandle (thread_hnd); /* Clear-up after TerminateThread(). */
616 thread_hnd = NULL;
617 return rc;
618}
619
620
621/* Wget expects network calls such as connect, recv, send, etc., to set
622 errno on failure. To achieve that, Winsock calls are wrapped with code
623 that, in case of error, sets errno to the value of WSAGetLastError().
624 In addition, we provide a wrapper around strerror, which recognizes
625 Winsock errors and prints the appropriate error message. */
626
627/* Define a macro that creates a function definition that wraps FUN into
628 a function that sets errno the way the rest of the code expects. */
629
630#define WRAP(fun, decl, call) int wrapped_##fun decl { \
631 int retval = fun call; \
632 if (retval < 0) \
633 errno = WSAGetLastError (); \
634 return retval; \
635}
636
637WRAP (socket, (int domain, int type, int protocol), (domain, type, protocol))
638WRAP (bind, (int s, struct sockaddr *a, int alen), (s, a, alen))
639WRAP (connect, (int s, const struct sockaddr *a, int alen), (s, a, alen))
640WRAP (listen, (int s, int backlog), (s, backlog))
641WRAP (accept, (int s, struct sockaddr *a, int *alen), (s, a, alen))
642WRAP (recv, (int s, void *buf, int len, int flags), (s, buf, len, flags))
643WRAP (send, (int s, const void *buf, int len, int flags), (s, buf, len, flags))
644WRAP (select, (int n, fd_set *r, fd_set *w, fd_set *e, const struct timeval *tm),
645 (n, r, w, e, tm))
646WRAP (getsockname, (int s, struct sockaddr *n, int *nlen), (s, n, nlen))
647WRAP (getpeername, (int s, struct sockaddr *n, int *nlen), (s, n, nlen))
648WRAP (setsockopt, (int s, int level, int opt, const void *val, int len),
649 (s, level, opt, val, len))
650WRAP (closesocket, (int s), (s))
651
652/* Return the text of the error message for Winsock error WSERR. */
653
654static const char *
655get_winsock_error (int wserr)
656{
657 switch (wserr) {
658 case WSAEINTR: return "Interrupted system call";
659 case WSAEBADF: return "Bad file number";
660 case WSAEACCES: return "Permission denied";
661 case WSAEFAULT: return "Bad address";
662 case WSAEINVAL: return "Invalid argument";
663 case WSAEMFILE: return "Too many open files";
664 case WSAEWOULDBLOCK: return "Resource temporarily unavailable";
665 case WSAEINPROGRESS: return "Operation now in progress";
666 case WSAEALREADY: return "Operation already in progress";
667 case WSAENOTSOCK: return "Socket operation on nonsocket";
668 case WSAEDESTADDRREQ: return "Destination address required";
669 case WSAEMSGSIZE: return "Message too long";
670 case WSAEPROTOTYPE: return "Protocol wrong type for socket";
671 case WSAENOPROTOOPT: return "Bad protocol option";
672 case WSAEPROTONOSUPPORT: return "Protocol not supported";
673 case WSAESOCKTNOSUPPORT: return "Socket type not supported";
674 case WSAEOPNOTSUPP: return "Operation not supported";
675 case WSAEPFNOSUPPORT: return "Protocol family not supported";
676 case WSAEAFNOSUPPORT: return "Address family not supported by protocol family";
677 case WSAEADDRINUSE: return "Address already in use";
678 case WSAEADDRNOTAVAIL: return "Cannot assign requested address";
679 case WSAENETDOWN: return "Network is down";
680 case WSAENETUNREACH: return "Network is unreachable";
681 case WSAENETRESET: return "Network dropped connection on reset";
682 case WSAECONNABORTED: return "Software caused connection abort";
683 case WSAECONNRESET: return "Connection reset by peer";
684 case WSAENOBUFS: return "No buffer space available";
685 case WSAEISCONN: return "Socket is already connected";
686 case WSAENOTCONN: return "Socket is not connected";
687 case WSAESHUTDOWN: return "Cannot send after socket shutdown";
688 case WSAETOOMANYREFS: return "Too many references";
689 case WSAETIMEDOUT: return "Connection timed out";
690 case WSAECONNREFUSED: return "Connection refused";
691 case WSAELOOP: return "Too many levels of symbolic links";
692 case WSAENAMETOOLONG: return "File name too long";
693 case WSAEHOSTDOWN: return "Host is down";
694 case WSAEHOSTUNREACH: return "No route to host";
695 case WSAENOTEMPTY: return "Not empty";
696 case WSAEPROCLIM: return "Too many processes";
697 case WSAEUSERS: return "Too many users";
698 case WSAEDQUOT: return "Bad quota";
699 case WSAESTALE: return "Something is stale";
700 case WSAEREMOTE: return "Remote error";
701 case WSAEDISCON: return "Disconnected";
702
703 /* Extended Winsock errors */
704 case WSASYSNOTREADY: return "Winsock library is not ready";
705 case WSANOTINITIALISED: return "Winsock library not initalised";
706 case WSAVERNOTSUPPORTED: return "Winsock version not supported";
707
708 case WSAHOST_NOT_FOUND: return "Host not found";
709 case WSATRY_AGAIN: return "Host not found, try again";
710 case WSANO_RECOVERY: return "Unrecoverable error in call to nameserver";
711 case WSANO_DATA: return "No data record of requested type";
712
713 default:
714 return NULL;
715 }
716}
717
718/* Return the error message corresponding to ERR. This is different
719 from Windows libc strerror() in that it handles Winsock errors
720 correctly. */
721
722const char *
723windows_strerror (int err)
724{
725 const char *p;
726 if (err >= 0 && err < sys_nerr)
727 return strerror (err);
728 else if ((p = get_winsock_error (err)) != NULL)
729 return p;
730 else
731 {
732 static char buf[32];
733 snprintf (buf, sizeof (buf), "Unknown error %d (%#x)", err, err);
734 return buf;
735 }
736}
Note: See TracBrowser for help on using the repository browser.