2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
8 * Copyright (C) 2002-2008 Telethra, Inc. <sales@openvpn.net>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program (see the file COPYING included with this
21 * distribution); if not, write to the Free Software Foundation, Inc.,
22 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * Win32-specific OpenVPN code, targetted at the mingw
27 * development environment.
43 * Windows internal socket API state (opaque).
45 static struct WSAData wsa_state
; /* GLOBAL */
48 * Should we call win32_pause() on program exit?
50 static bool pause_exit_enabled
= false; /* GLOBAL */
53 * win32_signal is used to get input from the keyboard
54 * if we are running in a console, or get input from an
55 * event object if we are running as a service.
58 struct win32_signal win32_signal
; /* GLOBAL */
61 * Save our old window title so we can restore
64 struct window_title window_title
; /* GLOBAL*/
67 * Special global semaphore used to protect network
68 * shell commands from simultaneous instantiation.
71 struct semaphore netcmd_semaphore
; /* GLOBAL */
74 * Windows system pathname such as c:\windows
76 static char *win_sys_path
= NULL
; /* GLOBAL */
81 if (WSAStartup(0x0101, &wsa_state
))
83 msg (M_ERR
, "WSAStartup failed");
85 window_title_clear (&window_title
);
86 win32_signal_clear (&win32_signal
);
87 netcmd_semaphore_init ();
93 netcmd_semaphore_close ();
94 if (pause_exit_enabled
)
96 if (win32_signal
.mode
== WSO_MODE_UNDEF
)
98 struct win32_signal w
;
99 win32_signal_open (&w
, WSO_FORCE_CONSOLE
, NULL
, false);
101 win32_signal_close (&w
);
104 win32_pause (&win32_signal
);
106 window_title_restore (&window_title
);
107 win32_signal_close (&win32_signal
);
113 set_pause_exit_win32 (void)
115 pause_exit_enabled
= true;
119 init_security_attributes_allow_all (struct security_attributes
*obj
)
123 obj
->sa
.nLength
= sizeof (SECURITY_ATTRIBUTES
);
124 obj
->sa
.lpSecurityDescriptor
= &obj
->sd
;
125 obj
->sa
.bInheritHandle
= FALSE
;
126 if (!InitializeSecurityDescriptor (&obj
->sd
, SECURITY_DESCRIPTOR_REVISION
))
128 if (!SetSecurityDescriptorDacl (&obj
->sd
, TRUE
, NULL
, FALSE
))
134 overlapped_io_init (struct overlapped_io
*o
,
135 const struct frame
*frame
,
137 bool tuntap_buffer
) /* if true: tuntap buffer, if false: socket buffer */
141 /* manual reset event, initially set according to event_state */
142 o
->overlapped
.hEvent
= CreateEvent (NULL
, TRUE
, event_state
, NULL
);
143 if (o
->overlapped
.hEvent
== NULL
)
144 msg (M_ERR
, "Error: overlapped_io_init: CreateEvent failed");
146 /* allocate buffer for overlapped I/O */
147 alloc_buf_sock_tun (&o
->buf_init
, frame
, tuntap_buffer
, 0);
151 overlapped_io_close (struct overlapped_io
*o
)
153 if (o
->overlapped
.hEvent
)
155 if (!CloseHandle (o
->overlapped
.hEvent
))
156 msg (M_WARN
| M_ERRNO
, "Warning: CloseHandle failed on overlapped I/O event object");
158 free_buf (&o
->buf_init
);
162 overlapped_io_state_ascii (const struct overlapped_io
*o
)
166 case IOSTATE_INITIAL
:
170 case IOSTATE_IMMEDIATE_RETURN
:
177 * Event-based notification of network events
181 init_net_event_win32 (struct rw_handle
*event
, long network_events
, socket_descriptor_t sd
, unsigned int flags
)
183 /* manual reset events, initially set to unsignaled */
185 /* initialize write event */
186 if (!(flags
& NE32_PERSIST_EVENT
) || !event
->write
)
188 if (flags
& NE32_WRITE_EVENT
)
190 event
->write
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
191 if (event
->write
== NULL
)
192 msg (M_ERR
, "Error: init_net_event_win32: CreateEvent (write) failed");
198 /* initialize read event */
199 if (!(flags
& NE32_PERSIST_EVENT
) || !event
->read
)
201 event
->read
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
202 if (event
->read
== NULL
)
203 msg (M_ERR
, "Error: init_net_event_win32: CreateEvent (read) failed");
206 /* setup network events to change read event state */
207 if (WSAEventSelect (sd
, event
->read
, network_events
) != 0)
208 msg (M_FATAL
| M_ERRNO_SOCK
, "Error: init_net_event_win32: WSAEventSelect call failed");
212 reset_net_event_win32 (struct rw_handle
*event
, socket_descriptor_t sd
)
214 WSANETWORKEVENTS wne
;
215 if (WSAEnumNetworkEvents (sd
, event
->read
, &wne
) != 0)
217 msg (M_FATAL
| M_ERRNO_SOCK
, "Error: reset_net_event_win32: WSAEnumNetworkEvents call failed");
218 return 0; /* NOTREACHED */
221 return wne
.lNetworkEvents
;
225 close_net_event_win32 (struct rw_handle
*event
, socket_descriptor_t sd
, unsigned int flags
)
229 if (socket_defined (sd
))
231 if (WSAEventSelect (sd
, event
->read
, 0) != 0)
232 msg (M_WARN
| M_ERRNO_SOCK
, "Warning: close_net_event_win32: WSAEventSelect call failed");
234 if (!ResetEvent (event
->read
))
235 msg (M_WARN
| M_ERRNO
, "Warning: ResetEvent (read) failed in close_net_event_win32");
236 if (!(flags
& NE32_PERSIST_EVENT
))
238 if (!CloseHandle (event
->read
))
239 msg (M_WARN
| M_ERRNO
, "Warning: CloseHandle (read) failed in close_net_event_win32");
246 if (!ResetEvent (event
->write
))
247 msg (M_WARN
| M_ERRNO
, "Warning: ResetEvent (write) failed in close_net_event_win32");
248 if (!(flags
& NE32_PERSIST_EVENT
))
250 if (!CloseHandle (event
->write
))
251 msg (M_WARN
| M_ERRNO
, "Warning: CloseHandle (write) failed in close_net_event_win32");
258 * struct net_event_win32
262 net_event_win32_init (struct net_event_win32
*ne
)
265 ne
->sd
= SOCKET_UNDEFINED
;
269 net_event_win32_start (struct net_event_win32
*ne
, long network_events
, socket_descriptor_t sd
)
271 ASSERT (!socket_defined (ne
->sd
));
274 init_net_event_win32 (&ne
->handle
, network_events
, sd
, NE32_PERSIST_EVENT
|NE32_WRITE_EVENT
);
278 net_event_win32_reset_write (struct net_event_win32
*ne
)
281 if (ne
->event_mask
& FD_WRITE
)
282 status
= SetEvent (ne
->handle
.write
);
284 status
= ResetEvent (ne
->handle
.write
);
286 msg (M_WARN
| M_ERRNO
, "Warning: SetEvent/ResetEvent failed in net_event_win32_reset_write");
290 net_event_win32_reset (struct net_event_win32
*ne
)
292 ne
->event_mask
|= reset_net_event_win32 (&ne
->handle
, ne
->sd
);
296 net_event_win32_stop (struct net_event_win32
*ne
)
298 if (net_event_win32_defined (ne
))
299 close_net_event_win32 (&ne
->handle
, ne
->sd
, NE32_PERSIST_EVENT
);
300 ne
->sd
= SOCKET_UNDEFINED
;
305 net_event_win32_close (struct net_event_win32
*ne
)
307 if (net_event_win32_defined (ne
))
308 close_net_event_win32 (&ne
->handle
, ne
->sd
, 0);
309 net_event_win32_init (ne
);
313 * Simulate *nix signals on Windows.
316 * (1) Console mode -- map keyboard function keys to signals
317 * (2) Service mode -- map Windows event object to SIGTERM
321 win32_signal_clear (struct win32_signal
*ws
)
327 win32_signal_open (struct win32_signal
*ws
,
329 const char *exit_event_name
,
330 bool exit_event_initial_state
)
334 ws
->mode
= WSO_MODE_UNDEF
;
335 ws
->in
.read
= INVALID_HANDLE_VALUE
;
336 ws
->in
.write
= INVALID_HANDLE_VALUE
;
337 ws
->console_mode_save
= 0;
338 ws
->console_mode_save_defined
= false;
340 if (force
== WSO_NOFORCE
|| force
== WSO_FORCE_CONSOLE
)
343 * Try to open console.
345 ws
->in
.read
= GetStdHandle (STD_INPUT_HANDLE
);
346 if (ws
->in
.read
!= INVALID_HANDLE_VALUE
)
348 if (GetConsoleMode (ws
->in
.read
, &ws
->console_mode_save
))
350 /* running on a console */
351 const DWORD new_console_mode
= ws
->console_mode_save
352 & ~(ENABLE_WINDOW_INPUT
353 | ENABLE_PROCESSED_INPUT
356 | ENABLE_MOUSE_INPUT
);
358 if (new_console_mode
!= ws
->console_mode_save
)
360 if (!SetConsoleMode (ws
->in
.read
, new_console_mode
))
361 msg (M_ERR
, "Error: win32_signal_open: SetConsoleMode failed");
362 ws
->console_mode_save_defined
= true;
364 ws
->mode
= WSO_MODE_CONSOLE
;
367 ws
->in
.read
= INVALID_HANDLE_VALUE
; /* probably running as a service */
372 * If console open failed, assume we are running
375 if ((force
== WSO_NOFORCE
|| force
== WSO_FORCE_SERVICE
)
376 && !HANDLE_DEFINED (ws
->in
.read
) && exit_event_name
)
378 struct security_attributes sa
;
380 if (!init_security_attributes_allow_all (&sa
))
381 msg (M_ERR
, "Error: win32_signal_open: init SA failed");
383 ws
->in
.read
= CreateEvent (&sa
.sa
,
385 exit_event_initial_state
? TRUE
: FALSE
,
387 if (ws
->in
.read
== NULL
)
389 msg (M_WARN
|M_ERRNO
, "NOTE: CreateEvent '%s' failed", exit_event_name
);
393 if (WaitForSingleObject (ws
->in
.read
, 0) != WAIT_TIMEOUT
)
394 msg (M_FATAL
, "ERROR: Exit Event ('%s') is signaled", exit_event_name
);
396 ws
->mode
= WSO_MODE_SERVICE
;
402 keyboard_input_available (struct win32_signal
*ws
)
404 ASSERT (ws
->mode
== WSO_MODE_CONSOLE
);
405 if (HANDLE_DEFINED (ws
->in
.read
))
408 if (GetNumberOfConsoleInputEvents (ws
->in
.read
, &n
))
415 keyboard_ir_to_key (INPUT_RECORD
*ir
)
417 if (ir
->Event
.KeyEvent
.uChar
.AsciiChar
== 0)
418 return ir
->Event
.KeyEvent
.wVirtualScanCode
;
420 if ((ir
->Event
.KeyEvent
.dwControlKeyState
421 & (LEFT_ALT_PRESSED
| RIGHT_ALT_PRESSED
))
422 && (ir
->Event
.KeyEvent
.wVirtualKeyCode
!= 18))
423 return ir
->Event
.KeyEvent
.wVirtualScanCode
* 256;
425 return ir
->Event
.KeyEvent
.uChar
.AsciiChar
;
429 win32_keyboard_get (struct win32_signal
*ws
)
431 ASSERT (ws
->mode
== WSO_MODE_CONSOLE
);
432 if (HANDLE_DEFINED (ws
->in
.read
))
437 if (!keyboard_input_available (ws
))
439 if (!ReadConsoleInput (ws
->in
.read
, &ir
, 1, &n
))
441 } while (ir
.EventType
!= KEY_EVENT
|| ir
.Event
.KeyEvent
.bKeyDown
!= TRUE
);
443 return keyboard_ir_to_key (&ir
);
450 win32_signal_close (struct win32_signal
*ws
)
452 if (ws
->mode
== WSO_MODE_SERVICE
&& HANDLE_DEFINED (ws
->in
.read
))
453 CloseHandle (ws
->in
.read
);
454 if (ws
->console_mode_save_defined
)
456 if (!SetConsoleMode (ws
->in
.read
, ws
->console_mode_save
))
457 msg (M_ERR
, "Error: win32_signal_close: SetConsoleMode failed");
463 * Return true if interrupt occurs in service mode.
466 win32_service_interrupt (struct win32_signal
*ws
)
468 if (ws
->mode
== WSO_MODE_SERVICE
)
470 if (HANDLE_DEFINED (ws
->in
.read
)
471 && WaitForSingleObject (ws
->in
.read
, 0) == WAIT_OBJECT_0
)
478 win32_signal_get (struct win32_signal
*ws
)
481 if (siginfo_static
.signal_received
)
483 ret
= siginfo_static
.signal_received
;
487 if (ws
->mode
== WSO_MODE_SERVICE
)
489 if (win32_service_interrupt (ws
))
492 else if (ws
->mode
== WSO_MODE_CONSOLE
)
494 switch (win32_keyboard_get (ws
))
496 case 0x3B: /* F1 -> USR1 */
499 case 0x3C: /* F2 -> USR2 */
502 case 0x3D: /* F3 -> HUP */
505 case 0x3E: /* F4 -> TERM */
512 siginfo_static
.signal_received
= ret
;
513 siginfo_static
.hard
= true;
520 win32_pause (struct win32_signal
*ws
)
522 if (ws
->mode
== WSO_MODE_CONSOLE
&& HANDLE_DEFINED (ws
->in
.read
))
525 msg (M_INFO
|M_NOPREFIX
, "Press any key to continue...");
527 status
= WaitForSingleObject (ws
->in
.read
, INFINITE
);
528 } while (!win32_keyboard_get (ws
));
532 /* window functions */
535 window_title_clear (struct window_title
*wt
)
541 window_title_save (struct window_title
*wt
)
545 if (!GetConsoleTitle (wt
->old_window_title
, sizeof (wt
->old_window_title
)))
547 wt
->old_window_title
[0] = 0;
556 window_title_restore (const struct window_title
*wt
)
559 SetConsoleTitle (wt
->old_window_title
);
563 window_title_generate (const char *title
)
565 struct gc_arena gc
= gc_new ();
566 struct buffer out
= alloc_buf_gc (256, &gc
);
569 buf_printf (&out
, "[%s] " PACKAGE_NAME
" " VERSION
" F4:EXIT F1:USR1 F2:USR2 F3:HUP", title
);
570 SetConsoleTitle (BSTR (&out
));
574 /* semaphore functions */
577 semaphore_clear (struct semaphore
*s
)
583 semaphore_open (struct semaphore
*s
, const char *name
)
585 struct security_attributes sa
;
591 if (init_security_attributes_allow_all (&sa
))
592 s
->hand
= CreateSemaphore(&sa
.sa
, 1, 1, name
);
595 msg (M_WARN
|M_ERRNO
, "WARNING: Cannot create Win32 semaphore '%s'", name
);
597 dmsg (D_SEMAPHORE
, "Created Win32 semaphore '%s'", s
->name
);
601 semaphore_lock (struct semaphore
*s
, int timeout_milliseconds
)
610 dmsg (D_SEMAPHORE_LOW
, "Attempting to lock Win32 semaphore '%s' prior to net shell command (timeout = %d sec)",
612 timeout_milliseconds
/ 1000);
613 status
= WaitForSingleObject (s
->hand
, timeout_milliseconds
);
614 if (status
== WAIT_FAILED
)
615 msg (M_ERR
, "Wait failed on Win32 semaphore '%s'", s
->name
);
616 ret
= (status
== WAIT_TIMEOUT
) ? false : true;
619 dmsg (D_SEMAPHORE
, "Locked Win32 semaphore '%s'", s
->name
);
624 dmsg (D_SEMAPHORE
, "Wait on Win32 semaphore '%s' timed out after %d milliseconds",
626 timeout_milliseconds
);
633 semaphore_release (struct semaphore
*s
)
638 dmsg (D_SEMAPHORE
, "Releasing Win32 semaphore '%s'", s
->name
);
639 if (!ReleaseSemaphore(s
->hand
, 1, NULL
))
640 msg (M_WARN
| M_ERRNO
, "ReleaseSemaphore failed on Win32 semaphore '%s'",
647 semaphore_close (struct semaphore
*s
)
652 semaphore_release (s
);
653 dmsg (D_SEMAPHORE
, "Closing Win32 semaphore '%s'", s
->name
);
654 CloseHandle (s
->hand
);
660 * Special global semaphore used to protect network
661 * shell commands from simultaneous instantiation.
665 netcmd_semaphore_init (void)
667 semaphore_open (&netcmd_semaphore
, PACKAGE
"_netcmd");
671 netcmd_semaphore_close (void)
673 semaphore_close (&netcmd_semaphore
);
677 netcmd_semaphore_lock (void)
679 const int timeout_seconds
= 600;
680 if (!semaphore_lock (&netcmd_semaphore
, timeout_seconds
* 1000))
681 msg (M_FATAL
, "Cannot lock net command semaphore");
685 netcmd_semaphore_release (void)
687 semaphore_release (&netcmd_semaphore
);
691 * Get input from console.
693 * Return false on input error, or if service
694 * exit event is signaled.
698 get_console_input_win32 (const char *prompt
, const bool echo
, char *input
, const int capacity
)
700 HANDLE in
= INVALID_HANDLE_VALUE
;
701 HANDLE err
= INVALID_HANDLE_VALUE
;
706 ASSERT (capacity
> 0);
710 in
= GetStdHandle (STD_INPUT_HANDLE
);
711 err
= get_orig_stderr ();
713 if (in
!= INVALID_HANDLE_VALUE
714 && err
!= INVALID_HANDLE_VALUE
715 && !win32_service_interrupt (&win32_signal
)
716 && WriteFile (err
, prompt
, strlen (prompt
), &len
, NULL
))
718 bool is_console
= (GetFileType (in
) == FILE_TYPE_CHAR
);
719 DWORD flags_save
= 0;
724 if (GetConsoleMode (in
, &flags_save
))
726 DWORD flags
= ENABLE_LINE_INPUT
| ENABLE_PROCESSED_INPUT
;
728 flags
|= ENABLE_ECHO_INPUT
;
729 SetConsoleMode (in
, flags
);
735 status
= ReadFile (in
, input
, capacity
, &len
, NULL
);
737 string_null_terminate (input
, (int)len
, capacity
);
741 WriteFile (err
, "\r\n", 2, &len
, NULL
);
743 SetConsoleMode (in
, flags_save
);
744 if (status
&& !win32_service_interrupt (&win32_signal
))
751 /* get password from console */
754 getpass (const char *prompt
)
756 static char line
[256];
757 if (get_console_input_win32 (prompt
, false, line
, sizeof (line
)))
764 * Return true if filename is safe to be used on Windows,
765 * by avoiding the following reserved names:
767 * CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9,
768 * LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, and CLOCK$
770 * See: http://msdn.microsoft.com/en-us/library/aa365247.aspx
771 * and http://msdn.microsoft.com/en-us/library/86k9f82k(VS.80).aspx
775 cmp_prefix (const char *str
, const bool n
, const char *pre
)
784 const int c1
= pre
[i
];
796 return c2
== '\0' || c2
== '.';
800 if (c1
!= tolower(c2
))
806 win_safe_filename (const char *fn
)
808 if (cmp_prefix (fn
, false, "con"))
810 if (cmp_prefix (fn
, false, "prn"))
812 if (cmp_prefix (fn
, false, "aux"))
814 if (cmp_prefix (fn
, false, "nul"))
816 if (cmp_prefix (fn
, true, "com"))
818 if (cmp_prefix (fn
, true, "lpt"))
820 if (cmp_prefix (fn
, false, "clock$"))
826 * Service functions for openvpn_execve
830 env_block (const struct env_set
*es
)
839 for (e
= es
->list
; e
!= NULL
; e
= e
->next
)
840 nchars
+= strlen (e
->string
) + 1;
842 ret
= (char *) malloc (nchars
);
843 check_malloc_return (ret
);
846 for (e
= es
->list
; e
!= NULL
; e
= e
->next
)
848 if (env_allowed (e
->string
))
850 strcpy (p
, e
->string
);
851 p
+= strlen (e
->string
) + 1;
862 cmd_line (const struct argv
*a
)
873 for (i
= 0; i
< a
->argc
; ++i
)
875 const char *arg
= a
->argv
[i
];
876 const size_t len
= strlen (arg
);
882 work
= (char *) malloc (maxlen
+ 1);
883 check_malloc_return (work
);
884 buf
= alloc_buf (nchars
);
886 for (i
= 0; i
< a
->argc
; ++i
)
888 const char *arg
= a
->argv
[i
];
890 string_mod (work
, CC_PRINT
, CC_DOUBLE_QUOTE
|CC_CRLF
, '_');
892 buf_printf (&buf
, " ");
893 if (string_class (work
, CC_ANY
, CC_SPACE
))
894 buf_printf (&buf
, "%s", work
);
896 buf_printf (&buf
, "\"%s\"", work
);
904 * Attempt to simulate fork/execve on Windows
907 openvpn_execve (const struct argv
*a
, const struct env_set
*es
, const unsigned int flags
)
912 if (openvpn_execve_allowed (flags
))
914 STARTUPINFO start_info
;
915 PROCESS_INFORMATION proc_info
;
917 char *env
= env_block (es
);
918 char *cl
= cmd_line (a
);
919 char *cmd
= a
->argv
[0];
924 /* fill in STARTUPINFO struct */
925 GetStartupInfo(&start_info
);
926 start_info
.cb
= sizeof(start_info
);
927 start_info
.dwFlags
= STARTF_USESTDHANDLES
|STARTF_USESHOWWINDOW
;
928 start_info
.wShowWindow
= SW_HIDE
;
929 start_info
.hStdInput
= GetStdHandle(STD_INPUT_HANDLE
);
930 start_info
.hStdOutput
= start_info
.hStdError
= GetStdHandle(STD_OUTPUT_HANDLE
);
932 if (CreateProcess (cmd
, cl
, NULL
, NULL
, FALSE
, 0, env
, NULL
, &start_info
, &proc_info
))
934 DWORD exit_status
= 0;
935 CloseHandle (proc_info
.hThread
);
936 WaitForSingleObject (proc_info
.hProcess
, INFINITE
);
937 if (GetExitCodeProcess (proc_info
.hProcess
, &exit_status
))
938 ret
= (int)exit_status
;
940 msg (M_WARN
|M_ERRNO
, "openvpn_execve: GetExitCodeProcess %s failed", cmd
);
941 CloseHandle (proc_info
.hProcess
);
945 msg (M_WARN
|M_ERRNO
, "openvpn_execve: CreateProcess %s failed", cmd
);
952 msg (M_WARN
, "openvpn_execve: external program may not be called due to setting of --script-security level");
957 msg (M_WARN
, "openvpn_execve: called with empty argv");
963 get_win_sys_path (void)
965 ASSERT (win_sys_path
);
970 set_win_sys_path (const char *newpath
, struct env_set
*es
)
973 win_sys_path
= string_alloc (newpath
, NULL
);
974 setenv_str (es
, SYS_PATH_ENV_VAR_NAME
, win_sys_path
); /* route.exe needs this */
978 set_win_sys_path_via_env (struct env_set
*es
)
981 DWORD status
= GetEnvironmentVariable (SYS_PATH_ENV_VAR_NAME
, buf
, sizeof(buf
));
983 msg (M_ERR
, "Cannot find environmental variable %s", SYS_PATH_ENV_VAR_NAME
);
984 if (status
> sizeof (buf
) - 1)
985 msg (M_FATAL
, "String overflow attempting to read environmental variable %s", SYS_PATH_ENV_VAR_NAME
);
986 set_win_sys_path (buf
, es
);
990 env_set_add_win32 (struct env_set
*es
)
992 set_win_sys_path (DEFAULT_WIN_SYS_PATH
, es
);