1 /* WIN32.C
2 *
3 * (c) 1995 Microsoft Corporation. All rights reserved.
4 * Developed by hip communications inc.
5 * Portions (c) 1993 Intergraph Corporation. All rights reserved.
6 *
7 * You may distribute under the terms of either the GNU General Public
8 * License or the Artistic License, as specified in the README file.
9 */
10 #define PERLIO_NOT_STDIO 0
11 #define WIN32_LEAN_AND_MEAN
12 #define WIN32IO_IS_STDIO
13 /* for CreateSymbolicLinkA() etc */
14 #define _WIN32_WINNT 0x0601
15 #include <tchar.h>
16
17 #ifdef __GNUC__
18 # define Win32_Winsock
19 #endif
20
21 #include <windows.h>
22
23 #ifndef HWND_MESSAGE
24 # define HWND_MESSAGE ((HWND)-3)
25 #endif
26
27 #ifndef PROCESSOR_ARCHITECTURE_AMD64
28 # define PROCESSOR_ARCHITECTURE_AMD64 9
29 #endif
30
31 #ifndef WC_NO_BEST_FIT_CHARS
32 # define WC_NO_BEST_FIT_CHARS 0x00000400
33 #endif
34
35 #include <winnt.h>
36 #include <commctrl.h>
37 #include <tlhelp32.h>
38 #include <io.h>
39 #include <signal.h>
40 #include <winioctl.h>
41 #include <winternl.h>
42
43 /* #include "config.h" */
44
45
46 #define PerlIO FILE
47
48 #include <sys/stat.h>
49 #include "EXTERN.h"
50 #include "perl.h"
51
52 #define NO_XSLOCKS
53 #define PERL_NO_GET_CONTEXT
54 #include "XSUB.h"
55
56 #include <fcntl.h>
57 #ifndef __GNUC__
58 /* assert.h conflicts with #define of assert in perl.h */
59 # include <assert.h>
60 #endif
61
62 #include <string.h>
63 #include <stdarg.h>
64 #include <float.h>
65 #include <time.h>
66 #include <sys/utime.h>
67 #include <wchar.h>
68
69 #ifdef __GNUC__
70 /* Mingw32 defaults to globing command line
71 * So we turn it off like this:
72 */
73 int _CRT_glob = 0;
74 #endif
75
76 #if defined(__MINGW32__) && (__MINGW32_MAJOR_VERSION==1)
77 /* Mingw32-1.1 is missing some prototypes */
78 START_EXTERN_C
79 FILE * _wfopen(LPCWSTR wszFileName, LPCWSTR wszMode);
80 FILE * _wfdopen(int nFd, LPCWSTR wszMode);
81 FILE * _freopen(LPCWSTR wszFileName, LPCWSTR wszMode, FILE * pOldStream);
82 int _flushall();
83 int _fcloseall();
84 END_EXTERN_C
85 #endif
86
87 #define EXECF_EXEC 1
88 #define EXECF_SPAWN 2
89 #define EXECF_SPAWN_NOWAIT 3
90
91 #if defined(PERL_IMPLICIT_SYS)
92 # undef getlogin
93 # define getlogin g_getlogin
94 #endif
95
96 #ifdef _MSC_VER
97 # define SET_INVALID_PARAMETER_HANDLER
98 #endif
99
100 #ifdef SET_INVALID_PARAMETER_HANDLER
101 static BOOL set_silent_invalid_parameter_handler(BOOL newvalue);
102 static void my_invalid_parameter_handler(const wchar_t* expression,
103 const wchar_t* function, const wchar_t* file,
104 unsigned int line, uintptr_t pReserved);
105 #endif
106
107 #ifndef WIN32_NO_REGISTRY
108 static char* get_regstr_from(HKEY hkey, const char *valuename, SV **svp);
109 static char* get_regstr(const char *valuename, SV **svp);
110 #endif
111
112 static char* get_emd_part(SV **prev_pathp, STRLEN *const len,
113 const char *trailing, ...);
114 static char* win32_get_xlib(const char *pl,
115 WIN32_NO_REGISTRY_M_(const char *xlib)
116 const char *libname, STRLEN *const len);
117
118 static BOOL has_shell_metachars(const char *ptr);
119 static long tokenize(const char *str, char **dest, char ***destv);
120 static int get_shell(void);
121 static char* find_next_space(const char *s);
122 static int do_spawn2(pTHX_ const char *cmd, int exectype);
123 static int do_spawn2_handles(pTHX_ const char *cmd, int exectype,
124 const int *handles);
125 static int do_spawnvp_handles(int mode, const char *cmdname,
126 const char * const *argv, const int *handles);
127 static PerlIO * do_popen(const char *mode, const char *command, IV narg,
128 SV **args);
129 static long find_pid(pTHX_ int pid);
130 static void remove_dead_process(long child);
131 static int terminate_process(DWORD pid, HANDLE process_handle, int sig);
132 static int my_killpg(int pid, int sig);
133 static int my_kill(int pid, int sig);
134 static void out_of_memory(void);
135 static char* wstr_to_str(const wchar_t* wstr);
136 static long filetime_to_clock(PFILETIME ft);
137 static BOOL filetime_from_time(PFILETIME ft, time_t t);
138 static char* create_command_line(char *cname, STRLEN clen,
139 const char * const *args);
140 static char* qualified_path(const char *cmd, bool other_exts);
141 static void ansify_path(void);
142 static LRESULT win32_process_message(HWND hwnd, UINT msg,
143 WPARAM wParam, LPARAM lParam);
144
145 #ifdef USE_ITHREADS
146 static long find_pseudo_pid(pTHX_ int pid);
147 static void remove_dead_pseudo_process(long child);
148 static HWND get_hwnd_delay(pTHX, long child, DWORD tries);
149 #endif
150
151 #ifdef HAVE_INTERP_INTERN
152 static void win32_csighandler(int sig);
153 #endif
154
155 static void translate_to_errno(void);
156
157 START_EXTERN_C
158 HANDLE w32_perldll_handle = INVALID_HANDLE_VALUE;
159 char w32_module_name[MAX_PATH+1];
160 END_EXTERN_C
161
162 static OSVERSIONINFO g_osver = {0, 0, 0, 0, 0, ""};
163
164 #ifndef WIN32_NO_REGISTRY
165 /* initialized by Perl_win32_init/PERL_SYS_INIT */
166 static HKEY HKCU_Perl_hnd;
167 static HKEY HKLM_Perl_hnd;
168 #endif
169
170 /* the time_t epoch start time as a filetime expressed as a large integer */
171 static ULARGE_INTEGER time_t_epoch_base_filetime;
172
173 static const SYSTEMTIME time_t_epoch_base_systemtime = {
174 1970, /* wYear */
175 1, /* wMonth */
176 0, /* wDayOfWeek */
177 1, /* wDay */
178 0, /* wHour */
179 0, /* wMinute */
180 0, /* wSecond */
181 0 /* wMilliseconds */
182 };
183
184 #define FILETIME_CHUNKS_PER_SECOND (10000000UL)
185
186 #ifdef USE_ITHREADS
187 static perl_mutex win32_read_console_mutex;
188 #endif
189
190 #ifdef SET_INVALID_PARAMETER_HANDLER
191 static BOOL silent_invalid_parameter_handler = FALSE;
192
193 static BOOL
set_silent_invalid_parameter_handler(BOOL newvalue)194 set_silent_invalid_parameter_handler(BOOL newvalue)
195 {
196 BOOL oldvalue = silent_invalid_parameter_handler;
197 # ifdef _DEBUG
198 silent_invalid_parameter_handler = newvalue;
199 # endif
200 return oldvalue;
201 }
202
203 static void
my_invalid_parameter_handler(const wchar_t * expression,const wchar_t * function,const wchar_t * file,unsigned int line,uintptr_t pReserved)204 my_invalid_parameter_handler(const wchar_t* expression,
205 const wchar_t* function,
206 const wchar_t* file,
207 unsigned int line,
208 uintptr_t pReserved)
209 {
210 # ifdef _DEBUG
211 char* ansi_expression;
212 char* ansi_function;
213 char* ansi_file;
214 if (silent_invalid_parameter_handler)
215 return;
216 ansi_expression = wstr_to_str(expression);
217 ansi_function = wstr_to_str(function);
218 ansi_file = wstr_to_str(file);
219 fprintf(stderr, "Invalid parameter detected in function %s. "
220 "File: %s, line: %d\n", ansi_function, ansi_file, line);
221 fprintf(stderr, "Expression: %s\n", ansi_expression);
222 free(ansi_expression);
223 free(ansi_function);
224 free(ansi_file);
225 # endif
226 }
227 #endif
228
229 EXTERN_C void
set_w32_module_name(void)230 set_w32_module_name(void)
231 {
232 /* this function may be called at DLL_PROCESS_ATTACH time */
233 char* ptr;
234 HMODULE module = (HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE)
235 ? GetModuleHandle(NULL)
236 : w32_perldll_handle);
237
238 WCHAR modulename[MAX_PATH];
239 WCHAR fullname[MAX_PATH];
240 char *ansi;
241
242 DWORD (__stdcall *pfnGetLongPathNameW)(LPCWSTR, LPWSTR, DWORD) =
243 (DWORD (__stdcall *)(LPCWSTR, LPWSTR, DWORD))
244 GetProcAddress(GetModuleHandle("kernel32.dll"), "GetLongPathNameW");
245
246 GetModuleFileNameW(module, modulename, sizeof(modulename)/sizeof(WCHAR));
247
248 /* Make sure we get an absolute pathname in case the module was loaded
249 * explicitly by LoadLibrary() with a relative path. */
250 GetFullPathNameW(modulename, sizeof(fullname)/sizeof(WCHAR), fullname, NULL);
251
252 /* Make sure we start with the long path name of the module because we
253 * later scan for pathname components to match "5.xx" to locate
254 * compatible sitelib directories, and the short pathname might mangle
255 * this path segment (e.g. by removing the dot on NTFS to something
256 * like "5xx~1.yy") */
257 if (pfnGetLongPathNameW)
258 pfnGetLongPathNameW(fullname, fullname, sizeof(fullname)/sizeof(WCHAR));
259
260 /* remove \\?\ prefix */
261 if (memcmp(fullname, L"\\\\?\\", 4*sizeof(WCHAR)) == 0)
262 memmove(fullname, fullname+4, (wcslen(fullname+4)+1)*sizeof(WCHAR));
263
264 ansi = win32_ansipath(fullname);
265 my_strlcpy(w32_module_name, ansi, sizeof(w32_module_name));
266 win32_free(ansi);
267
268 /* normalize to forward slashes */
269 ptr = w32_module_name;
270 while (*ptr) {
271 if (*ptr == '\\')
272 *ptr = '/';
273 ++ptr;
274 }
275 }
276
277 #ifndef WIN32_NO_REGISTRY
278 /* *svp (if non-NULL) is expected to be POK (valid allocated SvPVX(*svp)) */
279 static char*
get_regstr_from(HKEY handle,const char * valuename,SV ** svp)280 get_regstr_from(HKEY handle, const char *valuename, SV **svp)
281 {
282 /* Retrieve a REG_SZ or REG_EXPAND_SZ from the registry */
283 DWORD type;
284 char *str = NULL;
285 long retval;
286 DWORD datalen;
287
288 retval = RegQueryValueEx(handle, valuename, 0, &type, NULL, &datalen);
289 if (retval == ERROR_SUCCESS
290 && (type == REG_SZ || type == REG_EXPAND_SZ))
291 {
292 dTHX;
293 if (!*svp)
294 *svp = sv_2mortal(newSVpvs(""));
295 SvGROW(*svp, datalen);
296 retval = RegQueryValueEx(handle, valuename, 0, NULL,
297 (PBYTE)SvPVX(*svp), &datalen);
298 if (retval == ERROR_SUCCESS) {
299 str = SvPVX(*svp);
300 SvCUR_set(*svp,datalen-1);
301 }
302 }
303 return str;
304 }
305
306 /* *svp (if non-NULL) is expected to be POK (valid allocated SvPVX(*svp)) */
307 static char*
get_regstr(const char * valuename,SV ** svp)308 get_regstr(const char *valuename, SV **svp)
309 {
310 char *str;
311 if (HKCU_Perl_hnd) {
312 str = get_regstr_from(HKCU_Perl_hnd, valuename, svp);
313 if (!str)
314 goto try_HKLM;
315 }
316 else {
317 try_HKLM:
318 if (HKLM_Perl_hnd)
319 str = get_regstr_from(HKLM_Perl_hnd, valuename, svp);
320 else
321 str = NULL;
322 }
323 return str;
324 }
325 #endif /* ifndef WIN32_NO_REGISTRY */
326
327 /* *prev_pathp (if non-NULL) is expected to be POK (valid allocated SvPVX(sv)) */
328 static char *
get_emd_part(SV ** prev_pathp,STRLEN * const len,const char * trailing_path,...)329 get_emd_part(SV **prev_pathp, STRLEN *const len, const char *trailing_path, ...)
330 {
331 char base[10];
332 va_list ap;
333 char mod_name[MAX_PATH+1];
334 char *ptr;
335 char *optr;
336 char *strip;
337 STRLEN baselen;
338
339 va_start(ap, trailing_path);
340 strip = va_arg(ap, char *);
341
342 sprintf(base, "%d.%d", (int)PERL_REVISION, (int)PERL_VERSION);
343 baselen = strlen(base);
344
345 if (!*w32_module_name) {
346 set_w32_module_name();
347 }
348 strcpy(mod_name, w32_module_name);
349 ptr = strrchr(mod_name, '/');
350 while (ptr && strip) {
351 /* look for directories to skip back */
352 optr = ptr;
353 *ptr = '\0';
354 ptr = strrchr(mod_name, '/');
355 /* avoid stripping component if there is no slash,
356 * or it doesn't match ... */
357 if (!ptr || stricmp(ptr+1, strip) != 0) {
358 /* ... but not if component matches m|5\.$patchlevel.*| */
359 if (!ptr || !(*strip == '5' && *(ptr+1) == '5'
360 && strnEQ(strip, base, baselen)
361 && strnEQ(ptr+1, base, baselen)))
362 {
363 *optr = '/';
364 ptr = optr;
365 }
366 }
367 strip = va_arg(ap, char *);
368 }
369 if (!ptr) {
370 ptr = mod_name;
371 *ptr++ = '.';
372 *ptr = '/';
373 }
374 va_end(ap);
375 strcpy(++ptr, trailing_path);
376
377 /* only add directory if it exists */
378 if (GetFileAttributes(mod_name) != (DWORD) -1) {
379 /* directory exists */
380 dTHX;
381 if (!*prev_pathp)
382 *prev_pathp = sv_2mortal(newSVpvs(""));
383 else if (SvPVX(*prev_pathp))
384 sv_catpvs(*prev_pathp, ";");
385 sv_catpv(*prev_pathp, mod_name);
386 if(len)
387 *len = SvCUR(*prev_pathp);
388 return SvPVX(*prev_pathp);
389 }
390
391 return NULL;
392 }
393
394 EXTERN_C char *
win32_get_privlib(WIN32_NO_REGISTRY_M_ (const char * pl)STRLEN * const len)395 win32_get_privlib(WIN32_NO_REGISTRY_M_(const char *pl) STRLEN *const len)
396 {
397 const char *stdlib = "lib";
398 SV *sv = NULL;
399 #ifndef WIN32_NO_REGISTRY
400 char buffer[MAX_PATH+1];
401
402 /* $stdlib = $HKCU{"lib-$]"} || $HKLM{"lib-$]"} || $HKCU{"lib"} || $HKLM{"lib"} || ""; */
403 sprintf(buffer, "%s-%s", stdlib, pl);
404 if (!get_regstr(buffer, &sv))
405 (void)get_regstr(stdlib, &sv);
406 #endif
407
408 /* $stdlib .= ";$EMD/../../lib" */
409 return get_emd_part(&sv, len, stdlib, ARCHNAME, "bin", NULL);
410 }
411
412 static char *
win32_get_xlib(const char * pl,WIN32_NO_REGISTRY_M_ (const char * xlib)const char * libname,STRLEN * const len)413 win32_get_xlib(const char *pl, WIN32_NO_REGISTRY_M_(const char *xlib)
414 const char *libname, STRLEN *const len)
415 {
416 #ifndef WIN32_NO_REGISTRY
417 char regstr[40];
418 #endif
419 char pathstr[MAX_PATH+1];
420 SV *sv1 = NULL;
421 SV *sv2 = NULL;
422
423 #ifndef WIN32_NO_REGISTRY
424 /* $HKCU{"$xlib-$]"} || $HKLM{"$xlib-$]"} . ---; */
425 sprintf(regstr, "%s-%s", xlib, pl);
426 (void)get_regstr(regstr, &sv1);
427 #endif
428
429 /* $xlib .=
430 * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/$]/lib"; */
431 sprintf(pathstr, "%s/%s/lib", libname, pl);
432 (void)get_emd_part(&sv1, NULL, pathstr, ARCHNAME, "bin", pl, NULL);
433
434 #ifndef WIN32_NO_REGISTRY
435 /* $HKCU{$xlib} || $HKLM{$xlib} . ---; */
436 (void)get_regstr(xlib, &sv2);
437 #endif
438
439 /* $xlib .=
440 * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/lib"; */
441 sprintf(pathstr, "%s/lib", libname);
442 (void)get_emd_part(&sv2, NULL, pathstr, ARCHNAME, "bin", pl, NULL);
443
444 if (!sv1 && !sv2)
445 return NULL;
446 if (!sv1) {
447 sv1 = sv2;
448 } else if (sv2) {
449 dTHX;
450 sv_catpvs(sv1, ";");
451 sv_catsv(sv1, sv2);
452 }
453
454 if (len)
455 *len = SvCUR(sv1);
456 return SvPVX(sv1);
457 }
458
459 EXTERN_C char *
win32_get_sitelib(const char * pl,STRLEN * const len)460 win32_get_sitelib(const char *pl, STRLEN *const len)
461 {
462 return win32_get_xlib(pl, WIN32_NO_REGISTRY_M_("sitelib") "site", len);
463 }
464
465 #ifndef PERL_VENDORLIB_NAME
466 # define PERL_VENDORLIB_NAME "vendor"
467 #endif
468
469 EXTERN_C char *
win32_get_vendorlib(const char * pl,STRLEN * const len)470 win32_get_vendorlib(const char *pl, STRLEN *const len)
471 {
472 return win32_get_xlib(pl, WIN32_NO_REGISTRY_M_("vendorlib") PERL_VENDORLIB_NAME, len);
473 }
474
475 static BOOL
has_shell_metachars(const char * ptr)476 has_shell_metachars(const char *ptr)
477 {
478 int inquote = 0;
479 char quote = '\0';
480
481 /*
482 * Scan string looking for redirection (< or >) or pipe
483 * characters (|) that are not in a quoted string.
484 * Shell variable interpolation (%VAR%) can also happen inside strings.
485 */
486 while (*ptr) {
487 switch(*ptr) {
488 case '%':
489 return TRUE;
490 case '\'':
491 case '\"':
492 if (inquote) {
493 if (quote == *ptr) {
494 inquote = 0;
495 quote = '\0';
496 }
497 }
498 else {
499 quote = *ptr;
500 inquote++;
501 }
502 break;
503 case '>':
504 case '<':
505 case '|':
506 if (!inquote)
507 return TRUE;
508 default:
509 break;
510 }
511 ++ptr;
512 }
513 return FALSE;
514 }
515
516 #if !defined(PERL_IMPLICIT_SYS)
517 /* since the current process environment is being updated in util.c
518 * the library functions will get the correct environment
519 */
520 PerlIO *
Perl_my_popen(pTHX_ const char * cmd,const char * mode)521 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
522 {
523 PERL_FLUSHALL_FOR_CHILD;
524 return win32_popen(cmd, mode);
525 }
526
527 long
Perl_my_pclose(pTHX_ PerlIO * fp)528 Perl_my_pclose(pTHX_ PerlIO *fp)
529 {
530 return win32_pclose(fp);
531 }
532 #endif
533
534 DllExport unsigned long
win32_os_id(void)535 win32_os_id(void)
536 {
537 return (unsigned long)g_osver.dwPlatformId;
538 }
539
540 DllExport int
win32_getpid(void)541 win32_getpid(void)
542 {
543 #ifdef USE_ITHREADS
544 dTHX;
545 if (w32_pseudo_id)
546 return -((int)w32_pseudo_id);
547 #endif
548 return _getpid();
549 }
550
551 /* Tokenize a string. Words are null-separated, and the list
552 * ends with a doubled null. Any character (except null and
553 * including backslash) may be escaped by preceding it with a
554 * backslash (the backslash will be stripped).
555 * Returns number of words in result buffer.
556 */
557 static long
tokenize(const char * str,char ** dest,char *** destv)558 tokenize(const char *str, char **dest, char ***destv)
559 {
560 char *retstart = NULL;
561 char **retvstart = 0;
562 int items = -1;
563 if (str) {
564 int slen = strlen(str);
565 char *ret;
566 char **retv;
567 Newx(ret, slen+2, char);
568 Newx(retv, (slen+3)/2, char*);
569
570 retstart = ret;
571 retvstart = retv;
572 *retv = ret;
573 items = 0;
574 while (*str) {
575 *ret = *str++;
576 if (*ret == '\\' && *str)
577 *ret = *str++;
578 else if (*ret == ' ') {
579 while (*str == ' ')
580 str++;
581 if (ret == retstart)
582 ret--;
583 else {
584 *ret = '\0';
585 ++items;
586 if (*str)
587 *++retv = ret+1;
588 }
589 }
590 else if (!*str)
591 ++items;
592 ret++;
593 }
594 retvstart[items] = NULL;
595 *ret++ = '\0';
596 *ret = '\0';
597 }
598 *dest = retstart;
599 *destv = retvstart;
600 return items;
601 }
602
603 static const char
604 cmd_opts[] = "/x/d/c";
605
606 static const char
607 shell_cmd[] = "cmd.exe";
608
609 static int
get_shell(void)610 get_shell(void)
611 {
612 dTHX;
613 if (!w32_perlshell_tokens) {
614 /* we don't use COMSPEC here for two reasons:
615 * 1. the same reason perl on UNIX doesn't use SHELL--rampant and
616 * uncontrolled unportability of the ensuing scripts.
617 * 2. PERL5SHELL could be set to a shell that may not be fit for
618 * interactive use (which is what most programs look in COMSPEC
619 * for).
620 */
621 const char *shell = PerlEnv_getenv("PERL5SHELL");
622 if (shell) {
623 w32_perlshell_items = tokenize(shell,
624 &w32_perlshell_tokens,
625 &w32_perlshell_vec);
626 }
627 else {
628 /* tokenize does some Unix-ish like things like
629 \\ escaping that don't work well here
630 */
631 char shellbuf[MAX_PATH];
632 UINT len = GetSystemDirectoryA(shellbuf, sizeof(shellbuf));
633 if (len == 0) {
634 translate_to_errno();
635 return -1;
636 }
637 else if (len >= MAX_PATH) {
638 /* buffer too small */
639 errno = E2BIG;
640 return -1;
641 }
642 if (shellbuf[len-1] != '\\') {
643 my_strlcat(shellbuf, "\\", sizeof(shellbuf));
644 ++len;
645 }
646 if (len + sizeof(shell_cmd) > sizeof(shellbuf)) {
647 errno = E2BIG;
648 return -1;
649 }
650 my_strlcat(shellbuf, shell_cmd, sizeof(shellbuf));
651 len += sizeof(shell_cmd)-1;
652
653 Newx(w32_perlshell_vec, 3, char *);
654 Newx(w32_perlshell_tokens, len + 1 + sizeof(cmd_opts), char);
655
656 my_strlcpy(w32_perlshell_tokens, shellbuf, len+1);
657 my_strlcpy(w32_perlshell_tokens + len +1, cmd_opts,
658 sizeof(cmd_opts));
659
660 w32_perlshell_vec[0] = w32_perlshell_tokens;
661 w32_perlshell_vec[1] = w32_perlshell_tokens + len + 1;
662 w32_perlshell_vec[2] = NULL;
663
664 w32_perlshell_items = 2;
665 }
666 }
667 return 0;
668 }
669
670 int
Perl_do_aspawn(pTHX_ SV * really,SV ** mark,SV ** sp)671 Perl_do_aspawn(pTHX_ SV *really, SV **mark, SV **sp)
672 {
673 const char **argv;
674 char *str;
675 int status;
676 int flag = P_WAIT;
677 int index = 0;
678 int eno;
679
680 PERL_ARGS_ASSERT_DO_ASPAWN;
681
682 if (sp <= mark)
683 return -1;
684
685 if (get_shell() < 0)
686 return -1;
687
688 Newx(argv, (sp - mark) + w32_perlshell_items + 2, const char*);
689
690 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
691 ++mark;
692 flag = SvIVx(*mark);
693 }
694
695 while (++mark <= sp) {
696 if (*mark && (str = SvPV_nolen(*mark)))
697 argv[index++] = str;
698 else
699 argv[index++] = "";
700 }
701 argv[index++] = 0;
702
703 status = win32_spawnvp(flag,
704 (const char*)(really ? SvPV_nolen(really) : argv[0]),
705 (const char* const*)argv);
706
707 if (status < 0 && (eno = errno, (eno == ENOEXEC || eno == ENOENT))) {
708 /* possible shell-builtin, invoke with shell */
709 int sh_items;
710 sh_items = w32_perlshell_items;
711 while (--index >= 0)
712 argv[index+sh_items] = argv[index];
713 while (--sh_items >= 0)
714 argv[sh_items] = w32_perlshell_vec[sh_items];
715
716 status = win32_spawnvp(flag,
717 (const char*)(really ? SvPV_nolen(really) : argv[0]),
718 (const char* const*)argv);
719 }
720
721 if (flag == P_NOWAIT) {
722 PL_statusvalue = -1; /* >16bits hint for pp_system() */
723 }
724 else {
725 if (status < 0) {
726 if (ckWARN(WARN_EXEC))
727 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't spawn \"%s\": %s", argv[0], strerror(errno));
728 status = 255 * 256;
729 }
730 else
731 status *= 256;
732 PL_statusvalue = status;
733 }
734 Safefree(argv);
735 return (status);
736 }
737
738 /* returns pointer to the next unquoted space or the end of the string */
739 static char*
find_next_space(const char * s)740 find_next_space(const char *s)
741 {
742 bool in_quotes = FALSE;
743 while (*s) {
744 /* ignore doubled backslashes, or backslash+quote */
745 if (*s == '\\' && (s[1] == '\\' || s[1] == '"')) {
746 s += 2;
747 }
748 /* keep track of when we're within quotes */
749 else if (*s == '"') {
750 s++;
751 in_quotes = !in_quotes;
752 }
753 /* break it up only at spaces that aren't in quotes */
754 else if (!in_quotes && isSPACE(*s))
755 return (char*)s;
756 else
757 s++;
758 }
759 return (char*)s;
760 }
761
762 static int
do_spawn2(pTHX_ const char * cmd,int exectype)763 do_spawn2(pTHX_ const char *cmd, int exectype) {
764 return do_spawn2_handles(aTHX_ cmd, exectype, NULL);
765 }
766
767 static int
do_spawn2_handles(pTHX_ const char * cmd,int exectype,const int * handles)768 do_spawn2_handles(pTHX_ const char *cmd, int exectype, const int *handles)
769 {
770 char **a;
771 char *s;
772 char **argv;
773 int status = -1;
774 BOOL needToTry = TRUE;
775 char *cmd2;
776
777 /* Save an extra exec if possible. See if there are shell
778 * metacharacters in it */
779 if (!has_shell_metachars(cmd)) {
780 Newx(argv, strlen(cmd) / 2 + 2, char*);
781 Newx(cmd2, strlen(cmd) + 1, char);
782 strcpy(cmd2, cmd);
783 a = argv;
784 for (s = cmd2; *s;) {
785 while (*s && isSPACE(*s))
786 s++;
787 if (*s)
788 *(a++) = s;
789 s = find_next_space(s);
790 if (*s)
791 *s++ = '\0';
792 }
793 *a = NULL;
794 if (argv[0]) {
795 switch (exectype) {
796 case EXECF_SPAWN:
797 status = win32_spawnvp(P_WAIT, argv[0],
798 (const char* const*)argv);
799 break;
800 case EXECF_SPAWN_NOWAIT:
801 status = do_spawnvp_handles(P_NOWAIT, argv[0],
802 (const char* const*)argv, handles);
803 break;
804 case EXECF_EXEC:
805 status = win32_execvp(argv[0], (const char* const*)argv);
806 break;
807 }
808 if (status != -1 || errno == 0)
809 needToTry = FALSE;
810 }
811 Safefree(argv);
812 Safefree(cmd2);
813 }
814 if (needToTry) {
815 char **argv;
816 int i = -1;
817 if (get_shell() < 0)
818 return -1;
819 Newx(argv, w32_perlshell_items + 2, char*);
820 while (++i < w32_perlshell_items)
821 argv[i] = w32_perlshell_vec[i];
822 argv[i++] = (char *)cmd;
823 argv[i] = NULL;
824 switch (exectype) {
825 case EXECF_SPAWN:
826 status = win32_spawnvp(P_WAIT, argv[0],
827 (const char* const*)argv);
828 break;
829 case EXECF_SPAWN_NOWAIT:
830 status = do_spawnvp_handles(P_NOWAIT, argv[0],
831 (const char* const*)argv, handles);
832 break;
833 case EXECF_EXEC:
834 status = win32_execvp(argv[0], (const char* const*)argv);
835 break;
836 }
837 cmd = argv[0];
838 Safefree(argv);
839 }
840 if (exectype == EXECF_SPAWN_NOWAIT) {
841 PL_statusvalue = -1; /* >16bits hint for pp_system() */
842 }
843 else {
844 if (status < 0) {
845 if (ckWARN(WARN_EXEC))
846 Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s",
847 (exectype == EXECF_EXEC ? "exec" : "spawn"),
848 cmd, strerror(errno));
849 status = 255 * 256;
850 }
851 else
852 status *= 256;
853 PL_statusvalue = status;
854 }
855 return (status);
856 }
857
858 int
Perl_do_spawn(pTHX_ char * cmd)859 Perl_do_spawn(pTHX_ char *cmd)
860 {
861 PERL_ARGS_ASSERT_DO_SPAWN;
862
863 return do_spawn2(aTHX_ cmd, EXECF_SPAWN);
864 }
865
866 int
Perl_do_spawn_nowait(pTHX_ char * cmd)867 Perl_do_spawn_nowait(pTHX_ char *cmd)
868 {
869 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
870
871 return do_spawn2(aTHX_ cmd, EXECF_SPAWN_NOWAIT);
872 }
873
874 bool
Perl_do_exec(pTHX_ const char * cmd)875 Perl_do_exec(pTHX_ const char *cmd)
876 {
877 PERL_ARGS_ASSERT_DO_EXEC;
878
879 do_spawn2(aTHX_ cmd, EXECF_EXEC);
880 return FALSE;
881 }
882
883 /* The idea here is to read all the directory names into a string table
884 * (separated by nulls) and when one of the other dir functions is called
885 * return the pointer to the current file name.
886 */
887 DllExport DIR *
win32_opendir(const char * filename)888 win32_opendir(const char *filename)
889 {
890 dTHXa(NULL);
891 DIR *dirp;
892 long len;
893 long idx;
894 char scanname[MAX_PATH+3];
895 WCHAR wscanname[sizeof(scanname)];
896 WIN32_FIND_DATAW wFindData;
897 char buffer[MAX_PATH*2];
898 BOOL use_default;
899
900 len = strlen(filename);
901 if (len == 0) {
902 errno = ENOENT;
903 return NULL;
904 }
905 if (len > MAX_PATH) {
906 errno = ENAMETOOLONG;
907 return NULL;
908 }
909
910 /* Get us a DIR structure */
911 Newxz(dirp, 1, DIR);
912
913 /* Create the search pattern */
914 strcpy(scanname, filename);
915
916 /* bare drive name means look in cwd for drive */
917 if (len == 2 && isALPHA(scanname[0]) && scanname[1] == ':') {
918 scanname[len++] = '.';
919 scanname[len++] = '/';
920 }
921 else if (scanname[len-1] != '/' && scanname[len-1] != '\\') {
922 scanname[len++] = '/';
923 }
924 scanname[len++] = '*';
925 scanname[len] = '\0';
926
927 /* do the FindFirstFile call */
928 MultiByteToWideChar(CP_ACP, 0, scanname, -1, wscanname, sizeof(wscanname)/sizeof(WCHAR));
929 aTHXa(PERL_GET_THX);
930 dirp->handle = FindFirstFileW(PerlDir_mapW(wscanname), &wFindData);
931
932 if (dirp->handle == INVALID_HANDLE_VALUE) {
933 DWORD err = GetLastError();
934 /* FindFirstFile() fails on empty drives! */
935 switch (err) {
936 case ERROR_FILE_NOT_FOUND:
937 return dirp;
938 case ERROR_NO_MORE_FILES:
939 case ERROR_PATH_NOT_FOUND:
940 errno = ENOENT;
941 break;
942 case ERROR_NOT_ENOUGH_MEMORY:
943 errno = ENOMEM;
944 break;
945 default:
946 errno = EINVAL;
947 break;
948 }
949 Safefree(dirp);
950 return NULL;
951 }
952
953 use_default = FALSE;
954 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
955 wFindData.cFileName, -1,
956 buffer, sizeof(buffer), NULL, &use_default);
957 if (use_default && *wFindData.cAlternateFileName) {
958 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
959 wFindData.cAlternateFileName, -1,
960 buffer, sizeof(buffer), NULL, NULL);
961 }
962
963 /* now allocate the first part of the string table for
964 * the filenames that we find.
965 */
966 idx = strlen(buffer)+1;
967 if (idx < 256)
968 dirp->size = 256;
969 else
970 dirp->size = idx;
971 Newx(dirp->start, dirp->size, char);
972 strcpy(dirp->start, buffer);
973 dirp->nfiles++;
974 dirp->end = dirp->curr = dirp->start;
975 dirp->end += idx;
976 return dirp;
977 }
978
979
980 /* Readdir just returns the current string pointer and bumps the
981 * string pointer to the nDllExport entry.
982 */
983 DllExport struct direct *
win32_readdir(DIR * dirp)984 win32_readdir(DIR *dirp)
985 {
986 long len;
987
988 if (dirp->curr) {
989 /* first set up the structure to return */
990 len = strlen(dirp->curr);
991 strcpy(dirp->dirstr.d_name, dirp->curr);
992 dirp->dirstr.d_namlen = len;
993
994 /* Fake an inode */
995 dirp->dirstr.d_ino = dirp->curr - dirp->start;
996
997 /* Now set up for the next call to readdir */
998 dirp->curr += len + 1;
999 if (dirp->curr >= dirp->end) {
1000 BOOL res;
1001 char buffer[MAX_PATH*2];
1002
1003 if (dirp->handle == INVALID_HANDLE_VALUE) {
1004 res = 0;
1005 }
1006 /* finding the next file that matches the wildcard
1007 * (which should be all of them in this directory!).
1008 */
1009 else {
1010 WIN32_FIND_DATAW wFindData;
1011 res = FindNextFileW(dirp->handle, &wFindData);
1012 if (res) {
1013 BOOL use_default = FALSE;
1014 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
1015 wFindData.cFileName, -1,
1016 buffer, sizeof(buffer), NULL, &use_default);
1017 if (use_default && *wFindData.cAlternateFileName) {
1018 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
1019 wFindData.cAlternateFileName, -1,
1020 buffer, sizeof(buffer), NULL, NULL);
1021 }
1022 }
1023 }
1024 if (res) {
1025 long endpos = dirp->end - dirp->start;
1026 long newsize = endpos + strlen(buffer) + 1;
1027 /* bump the string table size by enough for the
1028 * new name and its null terminator */
1029 while (newsize > dirp->size) {
1030 long curpos = dirp->curr - dirp->start;
1031 Renew(dirp->start, dirp->size * 2, char);
1032 dirp->size *= 2;
1033 dirp->curr = dirp->start + curpos;
1034 }
1035 strcpy(dirp->start + endpos, buffer);
1036 dirp->end = dirp->start + newsize;
1037 dirp->nfiles++;
1038 }
1039 else {
1040 dirp->curr = NULL;
1041 if (dirp->handle != INVALID_HANDLE_VALUE) {
1042 FindClose(dirp->handle);
1043 dirp->handle = INVALID_HANDLE_VALUE;
1044 }
1045 }
1046 }
1047 return &(dirp->dirstr);
1048 }
1049 else
1050 return NULL;
1051 }
1052
1053 /* Telldir returns the current string pointer position */
1054 DllExport long
win32_telldir(DIR * dirp)1055 win32_telldir(DIR *dirp)
1056 {
1057 return dirp->curr ? (dirp->curr - dirp->start) : -1;
1058 }
1059
1060
1061 /* Seekdir moves the string pointer to a previously saved position
1062 * (returned by telldir).
1063 */
1064 DllExport void
win32_seekdir(DIR * dirp,long loc)1065 win32_seekdir(DIR *dirp, long loc)
1066 {
1067 /* Ensure dirp->curr remains within `dirp->start` buffer. */
1068 if (loc >= 0 && dirp->end - dirp->start > (ptrdiff_t) loc) {
1069 dirp->curr = dirp->start + loc;
1070 } else {
1071 dirp->curr = NULL;
1072 }
1073 }
1074
1075 /* Rewinddir resets the string pointer to the start */
1076 DllExport void
win32_rewinddir(DIR * dirp)1077 win32_rewinddir(DIR *dirp)
1078 {
1079 dirp->curr = dirp->start;
1080 }
1081
1082 /* free the memory allocated by opendir */
1083 DllExport int
win32_closedir(DIR * dirp)1084 win32_closedir(DIR *dirp)
1085 {
1086 if (dirp->handle != INVALID_HANDLE_VALUE)
1087 FindClose(dirp->handle);
1088 Safefree(dirp->start);
1089 Safefree(dirp);
1090 return 1;
1091 }
1092
1093 /* duplicate a open DIR* for interpreter cloning */
1094 DllExport DIR *
win32_dirp_dup(DIR * const dirp,CLONE_PARAMS * const param)1095 win32_dirp_dup(DIR *const dirp, CLONE_PARAMS *const param)
1096 {
1097 PerlInterpreter *const from = param->proto_perl;
1098 PerlInterpreter *const to = (PerlInterpreter *)PERL_GET_THX;
1099
1100 long pos;
1101 DIR *dup;
1102
1103 /* switch back to original interpreter because win32_readdir()
1104 * might Renew(dirp->start).
1105 */
1106 if (from != to) {
1107 PERL_SET_THX(from);
1108 }
1109
1110 /* mark current position; read all remaining entries into the
1111 * cache, and then restore to current position.
1112 */
1113 pos = win32_telldir(dirp);
1114 while (win32_readdir(dirp)) {
1115 /* read all entries into cache */
1116 }
1117 win32_seekdir(dirp, pos);
1118
1119 /* switch back to new interpreter to allocate new DIR structure */
1120 if (from != to) {
1121 PERL_SET_THX(to);
1122 }
1123
1124 Newx(dup, 1, DIR);
1125 memcpy(dup, dirp, sizeof(DIR));
1126
1127 Newx(dup->start, dirp->size, char);
1128 memcpy(dup->start, dirp->start, dirp->size);
1129
1130 dup->end = dup->start + (dirp->end - dirp->start);
1131 if (dirp->curr)
1132 dup->curr = dup->start + (dirp->curr - dirp->start);
1133
1134 return dup;
1135 }
1136
1137 /*
1138 * various stubs
1139 */
1140
1141
1142 /* Ownership
1143 *
1144 * Just pretend that everyone is a superuser. NT will let us know if
1145 * we don\'t really have permission to do something.
1146 */
1147
1148 #define ROOT_UID ((uid_t)0)
1149 #define ROOT_GID ((gid_t)0)
1150
1151 uid_t
getuid(void)1152 getuid(void)
1153 {
1154 return ROOT_UID;
1155 }
1156
1157 uid_t
geteuid(void)1158 geteuid(void)
1159 {
1160 return ROOT_UID;
1161 }
1162
1163 gid_t
getgid(void)1164 getgid(void)
1165 {
1166 return ROOT_GID;
1167 }
1168
1169 gid_t
getegid(void)1170 getegid(void)
1171 {
1172 return ROOT_GID;
1173 }
1174
1175 int
setuid(uid_t auid)1176 setuid(uid_t auid)
1177 {
1178 return (auid == ROOT_UID ? 0 : -1);
1179 }
1180
1181 int
setgid(gid_t agid)1182 setgid(gid_t agid)
1183 {
1184 return (agid == ROOT_GID ? 0 : -1);
1185 }
1186
1187 EXTERN_C char *
getlogin(void)1188 getlogin(void)
1189 {
1190 dTHX;
1191 char *buf = w32_getlogin_buffer;
1192 DWORD size = sizeof(w32_getlogin_buffer);
1193 if (GetUserName(buf,&size))
1194 return buf;
1195 return (char*)NULL;
1196 }
1197
1198 int
chown(const char * path,uid_t owner,gid_t group)1199 chown(const char *path, uid_t owner, gid_t group)
1200 {
1201 /* XXX noop */
1202 return 0;
1203 }
1204
1205 /*
1206 * XXX this needs strengthening (for PerlIO)
1207 * -- BKS, 11-11-200
1208 */
1209 #if((!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 4) && \
1210 (!defined(__MINGW32_MAJOR_VERSION) || __MINGW32_MAJOR_VERSION < 3 || \
1211 (__MINGW32_MAJOR_VERSION == 3 && __MINGW32_MINOR_VERSION < 21)))
mkstemp(const char * path)1212 int mkstemp(const char *path)
1213 {
1214 dTHX;
1215 char buf[MAX_PATH+1];
1216 int i = 0, fd = -1;
1217
1218 retry:
1219 if (i++ > 10) { /* give up */
1220 errno = ENOENT;
1221 return -1;
1222 }
1223 if (!GetTempFileNameA((LPCSTR)path, "plr", 1, buf)) {
1224 errno = ENOENT;
1225 return -1;
1226 }
1227 fd = PerlLIO_open3(buf, O_CREAT|O_RDWR|O_EXCL, 0600);
1228 if (fd == -1)
1229 goto retry;
1230 return fd;
1231 }
1232 #endif
1233
1234 static long
find_pid(pTHX_ int pid)1235 find_pid(pTHX_ int pid)
1236 {
1237 long child = w32_num_children;
1238 while (--child >= 0) {
1239 if ((int)w32_child_pids[child] == pid)
1240 return child;
1241 }
1242 return -1;
1243 }
1244
1245 static void
remove_dead_process(long child)1246 remove_dead_process(long child)
1247 {
1248 if (child >= 0) {
1249 dTHX;
1250 CloseHandle(w32_child_handles[child]);
1251 Move(&w32_child_handles[child+1], &w32_child_handles[child],
1252 (w32_num_children-child-1), HANDLE);
1253 Move(&w32_child_pids[child+1], &w32_child_pids[child],
1254 (w32_num_children-child-1), DWORD);
1255 w32_num_children--;
1256 }
1257 }
1258
1259 #ifdef USE_ITHREADS
1260 static long
find_pseudo_pid(pTHX_ int pid)1261 find_pseudo_pid(pTHX_ int pid)
1262 {
1263 long child = w32_num_pseudo_children;
1264 while (--child >= 0) {
1265 if ((int)w32_pseudo_child_pids[child] == pid)
1266 return child;
1267 }
1268 return -1;
1269 }
1270
1271 static void
remove_dead_pseudo_process(long child)1272 remove_dead_pseudo_process(long child)
1273 {
1274 if (child >= 0) {
1275 dTHX;
1276 CloseHandle(w32_pseudo_child_handles[child]);
1277 Move(&w32_pseudo_child_handles[child+1], &w32_pseudo_child_handles[child],
1278 (w32_num_pseudo_children-child-1), HANDLE);
1279 Move(&w32_pseudo_child_pids[child+1], &w32_pseudo_child_pids[child],
1280 (w32_num_pseudo_children-child-1), DWORD);
1281 Move(&w32_pseudo_child_message_hwnds[child+1], &w32_pseudo_child_message_hwnds[child],
1282 (w32_num_pseudo_children-child-1), HWND);
1283 Move(&w32_pseudo_child_sigterm[child+1], &w32_pseudo_child_sigterm[child],
1284 (w32_num_pseudo_children-child-1), char);
1285 w32_num_pseudo_children--;
1286 }
1287 }
1288
1289 void
win32_wait_for_children(pTHX)1290 win32_wait_for_children(pTHX)
1291 {
1292 if (w32_pseudo_children && w32_num_pseudo_children) {
1293 long child = 0;
1294 long count = 0;
1295 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
1296
1297 for (child = 0; child < w32_num_pseudo_children; ++child) {
1298 if (!w32_pseudo_child_sigterm[child])
1299 handles[count++] = w32_pseudo_child_handles[child];
1300 }
1301 /* XXX should use MsgWaitForMultipleObjects() to continue
1302 * XXX processing messages while we wait.
1303 */
1304 WaitForMultipleObjects(count, handles, TRUE, INFINITE);
1305
1306 while (w32_num_pseudo_children)
1307 CloseHandle(w32_pseudo_child_handles[--w32_num_pseudo_children]);
1308 }
1309 }
1310 #endif
1311
1312 static int
terminate_process(DWORD pid,HANDLE process_handle,int sig)1313 terminate_process(DWORD pid, HANDLE process_handle, int sig)
1314 {
1315 switch(sig) {
1316 case 0:
1317 /* "Does process exist?" use of kill */
1318 return 1;
1319 case 2:
1320 if (GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid))
1321 return 1;
1322 break;
1323 case SIGBREAK:
1324 case SIGTERM:
1325 if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid))
1326 return 1;
1327 break;
1328 default: /* For now be backwards compatible with perl 5.6 */
1329 case 9:
1330 /* Note that we will only be able to kill processes owned by the
1331 * current process owner, even when we are running as an administrator.
1332 * To kill processes of other owners we would need to set the
1333 * 'SeDebugPrivilege' privilege before obtaining the process handle.
1334 */
1335 if (TerminateProcess(process_handle, sig))
1336 return 1;
1337 break;
1338 }
1339 return 0;
1340 }
1341
1342 /* returns number of processes killed */
1343 static int
my_killpg(int pid,int sig)1344 my_killpg(int pid, int sig)
1345 {
1346 HANDLE process_handle;
1347 HANDLE snapshot_handle;
1348 int killed = 0;
1349
1350 process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
1351 if (process_handle == NULL)
1352 return 0;
1353
1354 killed += terminate_process(pid, process_handle, sig);
1355
1356 snapshot_handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
1357 if (snapshot_handle != INVALID_HANDLE_VALUE) {
1358 PROCESSENTRY32 entry;
1359
1360 entry.dwSize = sizeof(entry);
1361 if (Process32First(snapshot_handle, &entry)) {
1362 do {
1363 if (entry.th32ParentProcessID == (DWORD)pid)
1364 killed += my_killpg(entry.th32ProcessID, sig);
1365 entry.dwSize = sizeof(entry);
1366 }
1367 while (Process32Next(snapshot_handle, &entry));
1368 }
1369 CloseHandle(snapshot_handle);
1370 }
1371 CloseHandle(process_handle);
1372 return killed;
1373 }
1374
1375 /* returns number of processes killed */
1376 static int
my_kill(int pid,int sig)1377 my_kill(int pid, int sig)
1378 {
1379 int retval = 0;
1380 HANDLE process_handle;
1381
1382 if (sig < 0)
1383 return my_killpg(pid, -sig);
1384
1385 process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
1386 /* OpenProcess() returns NULL on error, *not* INVALID_HANDLE_VALUE */
1387 if (process_handle != NULL) {
1388 retval = terminate_process(pid, process_handle, sig);
1389 CloseHandle(process_handle);
1390 }
1391 return retval;
1392 }
1393
1394 #ifdef USE_ITHREADS
1395 /* Get a child pseudo-process HWND, with retrying and delaying/yielding.
1396 * The "tries" parameter is the number of retries to make, with a Sleep(1)
1397 * (waiting and yielding the time slot) between each try. Specifying 0 causes
1398 * only Sleep(0) (no waiting and potentially no yielding) to be used, so is not
1399 * recommended
1400 * Returns an hwnd != INVALID_HANDLE_VALUE (so be aware that NULL can be
1401 * returned) or croaks if the child pseudo-process doesn't schedule and deliver
1402 * a HWND in the time period allowed.
1403 */
1404 static HWND
get_hwnd_delay(pTHX,long child,DWORD tries)1405 get_hwnd_delay(pTHX, long child, DWORD tries)
1406 {
1407 HWND hwnd = w32_pseudo_child_message_hwnds[child];
1408 if (hwnd != INVALID_HANDLE_VALUE) return hwnd;
1409
1410 /* Pseudo-process has not yet properly initialized since hwnd isn't set.
1411 * Fast sleep: On some NT kernels/systems, a Sleep(0) won't deschedule a
1412 * thread 100% of the time since threads are attached to a CPU for NUMA and
1413 * caching reasons, and the child thread was attached to a different CPU
1414 * therefore there is no workload on that CPU and Sleep(0) returns control
1415 * without yielding the time slot.
1416 * https://github.com/Perl/perl5/issues/11267
1417 */
1418 Sleep(0);
1419 win32_async_check(aTHX);
1420 hwnd = w32_pseudo_child_message_hwnds[child];
1421 if (hwnd != INVALID_HANDLE_VALUE) return hwnd;
1422
1423 {
1424 unsigned int count = 0;
1425 /* No Sleep(1) if tries==0, just fail instead if we get this far. */
1426 while (count++ < tries) {
1427 Sleep(1);
1428 win32_async_check(aTHX);
1429 hwnd = w32_pseudo_child_message_hwnds[child];
1430 if (hwnd != INVALID_HANDLE_VALUE) return hwnd;
1431 }
1432 }
1433
1434 Perl_croak(aTHX_ "panic: child pseudo-process was never scheduled");
1435 }
1436 #endif
1437
1438 DllExport int
win32_kill(int pid,int sig)1439 win32_kill(int pid, int sig)
1440 {
1441 dTHX;
1442 long child;
1443 #ifdef USE_ITHREADS
1444 if (pid < 0) {
1445 /* it is a pseudo-forked child */
1446 child = find_pseudo_pid(aTHX_ -pid);
1447 if (child >= 0) {
1448 HANDLE hProcess = w32_pseudo_child_handles[child];
1449 switch (sig) {
1450 case 0:
1451 /* "Does process exist?" use of kill */
1452 return 0;
1453
1454 case 9: {
1455 /* kill -9 style un-graceful exit */
1456 /* Do a wait to make sure child starts and isn't in DLL
1457 * Loader Lock */
1458 HWND hwnd = get_hwnd_delay(aTHX, child, 5);
1459 if (TerminateThread(hProcess, sig)) {
1460 /* Allow the scheduler to finish cleaning up the other
1461 * thread.
1462 * Otherwise, if we ExitProcess() before another context
1463 * switch happens we will end up with a process exit
1464 * code of "sig" instead of our own exit status.
1465 * https://rt.cpan.org/Ticket/Display.html?id=66016#txn-908976
1466 */
1467 Sleep(0);
1468 remove_dead_pseudo_process(child);
1469 return 0;
1470 }
1471 break;
1472 }
1473
1474 default: {
1475 HWND hwnd = get_hwnd_delay(aTHX, child, 5);
1476 /* We fake signals to pseudo-processes using Win32
1477 * message queue. */
1478 if ((hwnd != NULL && PostMessage(hwnd, WM_USER_KILL, sig, 0)) ||
1479 PostThreadMessage(-pid, WM_USER_KILL, sig, 0))
1480 {
1481 /* Don't wait for child process to terminate after we send a
1482 * SIGTERM because the child may be blocked in a system call
1483 * and never receive the signal.
1484 */
1485 if (sig == SIGTERM) {
1486 Sleep(0);
1487 w32_pseudo_child_sigterm[child] = 1;
1488 }
1489 /* It might be us ... */
1490 PERL_ASYNC_CHECK();
1491 return 0;
1492 }
1493 break;
1494 }
1495 } /* switch */
1496 }
1497 }
1498 else
1499 #endif
1500 {
1501 child = find_pid(aTHX_ pid);
1502 if (child >= 0) {
1503 if (my_kill(pid, sig)) {
1504 DWORD exitcode = 0;
1505 if (GetExitCodeProcess(w32_child_handles[child], &exitcode) &&
1506 exitcode != STILL_ACTIVE)
1507 {
1508 remove_dead_process(child);
1509 }
1510 return 0;
1511 }
1512 }
1513 else {
1514 if (my_kill(pid, sig))
1515 return 0;
1516 }
1517 }
1518 errno = EINVAL;
1519 return -1;
1520 }
1521
1522 PERL_STATIC_INLINE
1523 time_t
translate_ft_to_time_t(FILETIME ft)1524 translate_ft_to_time_t(FILETIME ft) {
1525 SYSTEMTIME st;
1526 struct tm pt;
1527 time_t retval;
1528 dTHX;
1529
1530 if (!FileTimeToSystemTime(&ft, &st))
1531 return -1;
1532
1533 Zero(&pt, 1, struct tm);
1534 pt.tm_year = st.wYear - 1900;
1535 pt.tm_mon = st.wMonth - 1;
1536 pt.tm_mday = st.wDay;
1537 pt.tm_hour = st.wHour;
1538 pt.tm_min = st.wMinute;
1539 pt.tm_sec = st.wSecond;
1540
1541 MKTIME_LOCK;
1542 retval = _mkgmtime(&pt);
1543 MKTIME_UNLOCK;
1544
1545 return retval;
1546 }
1547
1548 typedef DWORD (__stdcall *pGetFinalPathNameByHandleA_t)(HANDLE, LPSTR, DWORD, DWORD);
1549
1550 /* Adapted from:
1551
1552 https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_reparse_data_buffer
1553
1554 Renamed to avoid conflicts, apparently some SDKs define this
1555 structure.
1556
1557 Hoisted the symlink and mount point data into a new type to allow us
1558 to make a pointer to it, and to avoid C++ scoping issues.
1559
1560 */
1561
1562 typedef struct {
1563 USHORT SubstituteNameOffset;
1564 USHORT SubstituteNameLength;
1565 USHORT PrintNameOffset;
1566 USHORT PrintNameLength;
1567 ULONG Flags;
1568 WCHAR PathBuffer[MAX_PATH*3];
1569 } MY_SYMLINK_REPARSE_BUFFER, *PMY_SYMLINK_REPARSE_BUFFER;
1570
1571 typedef struct {
1572 USHORT SubstituteNameOffset;
1573 USHORT SubstituteNameLength;
1574 USHORT PrintNameOffset;
1575 USHORT PrintNameLength;
1576 WCHAR PathBuffer[MAX_PATH*3];
1577 } MY_MOUNT_POINT_REPARSE_BUFFER;
1578
1579 typedef struct {
1580 ULONG ReparseTag;
1581 USHORT ReparseDataLength;
1582 USHORT Reserved;
1583 union {
1584 MY_SYMLINK_REPARSE_BUFFER SymbolicLinkReparseBuffer;
1585 MY_MOUNT_POINT_REPARSE_BUFFER MountPointReparseBuffer;
1586 struct {
1587 UCHAR DataBuffer[1];
1588 } GenericReparseBuffer;
1589 } Data;
1590 } MY_REPARSE_DATA_BUFFER, *PMY_REPARSE_DATA_BUFFER;
1591
1592 #ifndef IO_REPARSE_TAG_SYMLINK
1593 # define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
1594 #endif
1595 #ifndef IO_REPARSE_TAG_AF_UNIX
1596 # define IO_REPARSE_TAG_AF_UNIX 0x80000023
1597 #endif
1598 #ifndef IO_REPARSE_TAG_LX_FIFO
1599 # define IO_REPARSE_TAG_LX_FIFO 0x80000024
1600 #endif
1601 #ifndef IO_REPARSE_TAG_LX_CHR
1602 # define IO_REPARSE_TAG_LX_CHR 0x80000025
1603 #endif
1604 #ifndef IO_REPARSE_TAG_LX_BLK
1605 # define IO_REPARSE_TAG_LX_BLK 0x80000026
1606 #endif
1607
1608 static int
win32_stat_low(HANDLE handle,const char * path,STRLEN len,Stat_t * sbuf,DWORD reparse_type)1609 win32_stat_low(HANDLE handle, const char *path, STRLEN len, Stat_t *sbuf,
1610 DWORD reparse_type) {
1611 DWORD type = GetFileType(handle);
1612 BY_HANDLE_FILE_INFORMATION bhi;
1613
1614 Zero(sbuf, 1, Stat_t);
1615
1616 if (reparse_type) {
1617 /* Lie to get to the right place */
1618 type = FILE_TYPE_DISK;
1619 }
1620
1621 type &= ~FILE_TYPE_REMOTE;
1622
1623 switch (type) {
1624 case FILE_TYPE_DISK:
1625 if (GetFileInformationByHandle(handle, &bhi)) {
1626 sbuf->st_dev = bhi.dwVolumeSerialNumber;
1627 sbuf->st_ino = bhi.nFileIndexHigh;
1628 sbuf->st_ino <<= 32;
1629 sbuf->st_ino |= bhi.nFileIndexLow;
1630 sbuf->st_nlink = bhi.nNumberOfLinks;
1631 sbuf->st_uid = 0;
1632 sbuf->st_gid = 0;
1633 /* ucrt sets this to the drive letter for
1634 stat(), lets not reproduce that mistake */
1635 sbuf->st_rdev = 0;
1636 sbuf->st_size = bhi.nFileSizeHigh;
1637 sbuf->st_size <<= 32;
1638 sbuf->st_size |= bhi.nFileSizeLow;
1639
1640 sbuf->st_atime = translate_ft_to_time_t(bhi.ftLastAccessTime);
1641 sbuf->st_mtime = translate_ft_to_time_t(bhi.ftLastWriteTime);
1642 sbuf->st_ctime = translate_ft_to_time_t(bhi.ftCreationTime);
1643
1644 if (reparse_type) {
1645 /* https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/c8e77b37-3909-4fe6-a4ea-2b9d423b1ee4
1646 describes all of these as WSL only, but the AF_UNIX tag
1647 is known to be used for AF_UNIX sockets without WSL.
1648 */
1649 switch (reparse_type) {
1650 case IO_REPARSE_TAG_AF_UNIX:
1651 sbuf->st_mode = _S_IFSOCK;
1652 break;
1653
1654 case IO_REPARSE_TAG_LX_FIFO:
1655 sbuf->st_mode = _S_IFIFO;
1656 break;
1657
1658 case IO_REPARSE_TAG_LX_CHR:
1659 sbuf->st_mode = _S_IFCHR;
1660 break;
1661
1662 case IO_REPARSE_TAG_LX_BLK:
1663 sbuf->st_mode = _S_IFBLK;
1664 break;
1665
1666 default:
1667 /* Is there anything else we can do here? */
1668 errno = EINVAL;
1669 return -1;
1670 }
1671 }
1672 else if (bhi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1673 sbuf->st_mode = _S_IFDIR | _S_IREAD | _S_IEXEC;
1674 /* duplicate the logic from the end of the old win32_stat() */
1675 if (!(bhi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
1676 sbuf->st_mode |= S_IWRITE;
1677 }
1678 }
1679 else {
1680 char path_buf[MAX_PATH+1];
1681 sbuf->st_mode = _S_IFREG;
1682
1683 if (!path) {
1684 pGetFinalPathNameByHandleA_t pGetFinalPathNameByHandleA =
1685 (pGetFinalPathNameByHandleA_t)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetFinalPathNameByHandleA");
1686 if (pGetFinalPathNameByHandleA) {
1687 len = pGetFinalPathNameByHandleA(handle, path_buf, sizeof(path_buf), 0);
1688 }
1689 else {
1690 len = 0;
1691 }
1692
1693 /* < to ensure there's space for the \0 */
1694 if (len && len < sizeof(path_buf)) {
1695 path = path_buf;
1696 }
1697 }
1698
1699 if (path && len > 4 &&
1700 (_stricmp(path + len - 4, ".exe") == 0 ||
1701 _stricmp(path + len - 4, ".bat") == 0 ||
1702 _stricmp(path + len - 4, ".cmd") == 0 ||
1703 _stricmp(path + len - 4, ".com") == 0)) {
1704 sbuf->st_mode |= _S_IEXEC;
1705 }
1706 if (!(bhi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
1707 sbuf->st_mode |= _S_IWRITE;
1708 }
1709 sbuf->st_mode |= _S_IREAD;
1710 }
1711 }
1712 else {
1713 translate_to_errno();
1714 return -1;
1715 }
1716 break;
1717
1718 case FILE_TYPE_CHAR:
1719 case FILE_TYPE_PIPE:
1720 sbuf->st_mode = (type == FILE_TYPE_CHAR) ? _S_IFCHR : _S_IFIFO;
1721 if (handle == GetStdHandle(STD_INPUT_HANDLE) ||
1722 handle == GetStdHandle(STD_OUTPUT_HANDLE) ||
1723 handle == GetStdHandle(STD_ERROR_HANDLE)) {
1724 sbuf->st_mode |= _S_IWRITE | _S_IREAD;
1725 }
1726 break;
1727
1728 default:
1729 return -1;
1730 }
1731
1732 /* owner == user == group */
1733 sbuf->st_mode |= (sbuf->st_mode & 0700) >> 3;
1734 sbuf->st_mode |= (sbuf->st_mode & 0700) >> 6;
1735
1736 return 0;
1737 }
1738
1739 /* https://docs.microsoft.com/en-us/windows/win32/fileio/reparse-points */
1740 #define SYMLINK_FOLLOW_LIMIT 63
1741
1742 /*
1743
1744 Given a pathname, required to be a symlink, follow it until we find a
1745 non-symlink path.
1746
1747 This should only be called when the symlink() chain doesn't lead to a
1748 normal file, which should have been caught earlier.
1749
1750 On success, returns a HANDLE to the target and sets *reparse_type to
1751 the ReparseTag of the target.
1752
1753 Returns INVALID_HANDLE_VALUE on error, which might be that the symlink
1754 chain is broken, or requires too many links to resolve.
1755
1756 */
1757
1758 static HANDLE
S_follow_symlinks_to(pTHX_ const char * pathname,DWORD * reparse_type)1759 S_follow_symlinks_to(pTHX_ const char *pathname, DWORD *reparse_type) {
1760 char link_target[MAX_PATH];
1761 SV *work_path = newSVpvn(pathname, strlen(pathname));
1762 int link_count = 0;
1763 int link_len;
1764 HANDLE handle;
1765
1766 *reparse_type = 0;
1767
1768 while ((link_len = win32_readlink(SvPVX(work_path), link_target,
1769 sizeof(link_target))) > 0) {
1770 if (link_count++ >= SYMLINK_FOLLOW_LIMIT) {
1771 /* Windows doesn't appear to ever return ELOOP,
1772 let's do better ourselves
1773 */
1774 SvREFCNT_dec(work_path);
1775 errno = ELOOP;
1776 return INVALID_HANDLE_VALUE;
1777 }
1778 /* Adjust the linktarget based on the link source or current
1779 directory as needed.
1780 */
1781 if (link_target[0] == '\\'
1782 || link_target[0] == '/'
1783 || (link_len >=2 && link_target[1] == ':')) {
1784 /* link is absolute */
1785 sv_setpvn(work_path, link_target, link_len);
1786 }
1787 else {
1788 STRLEN work_len;
1789 const char *workp = SvPV(work_path, work_len);
1790 const char *final_bslash =
1791 (const char *)my_memrchr(workp, '\\', work_len);
1792 const char *final_slash =
1793 (const char *)my_memrchr(workp, '/', work_len);
1794 const char *path_sep = NULL;
1795 if (final_bslash && final_slash)
1796 path_sep = final_bslash > final_slash ? final_bslash : final_slash;
1797 else if (final_bslash)
1798 path_sep = final_bslash;
1799 else if (final_slash)
1800 path_sep = final_slash;
1801
1802 if (path_sep) {
1803 SV *new_path = newSVpv(workp, path_sep - workp + 1);
1804 sv_catpvn(new_path, link_target, link_len);
1805 SvREFCNT_dec(work_path);
1806 work_path = new_path;
1807 }
1808 else {
1809 /* should only get here the first time around */
1810 assert(link_count == 1);
1811 char path_temp[MAX_PATH];
1812 DWORD path_len = GetCurrentDirectoryA(sizeof(path_temp), path_temp);
1813 if (!path_len || path_len > sizeof(path_temp)) {
1814 SvREFCNT_dec(work_path);
1815 errno = EINVAL;
1816 return INVALID_HANDLE_VALUE;
1817 }
1818
1819 SV *new_path = newSVpvn(path_temp, path_len);
1820 if (path_temp[path_len-1] != '\\') {
1821 sv_catpvs(new_path, "\\");
1822 }
1823 sv_catpvn(new_path, link_target, link_len);
1824 SvREFCNT_dec(work_path);
1825 work_path = new_path;
1826 }
1827 }
1828 }
1829
1830 handle =
1831 CreateFileA(SvPVX(work_path), GENERIC_READ, 0, NULL, OPEN_EXISTING,
1832 FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, 0);
1833 SvREFCNT_dec(work_path);
1834 if (handle != INVALID_HANDLE_VALUE) {
1835 MY_REPARSE_DATA_BUFFER linkdata;
1836 DWORD linkdata_returned;
1837
1838 if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
1839 &linkdata, sizeof(linkdata),
1840 &linkdata_returned, NULL)) {
1841 translate_to_errno();
1842 CloseHandle(handle);
1843 return INVALID_HANDLE_VALUE;
1844 }
1845 *reparse_type = linkdata.ReparseTag;
1846 return handle;
1847 }
1848 else {
1849 translate_to_errno();
1850 }
1851
1852 return handle;
1853 }
1854
1855 DllExport int
win32_stat(const char * path,Stat_t * sbuf)1856 win32_stat(const char *path, Stat_t *sbuf)
1857 {
1858 dTHX;
1859 BOOL expect_dir = FALSE;
1860 int result;
1861 HANDLE handle;
1862 DWORD reparse_type = 0;
1863
1864 path = PerlDir_mapA(path);
1865
1866 handle =
1867 CreateFileA(path, FILE_READ_ATTRIBUTES,
1868 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
1869 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1870 if (handle == INVALID_HANDLE_VALUE) {
1871 /* AF_UNIX sockets need to be opened as a reparse point, but
1872 that will also open symlinks rather than following them.
1873
1874 There may be other reparse points that need similar
1875 treatment.
1876 */
1877 handle = S_follow_symlinks_to(aTHX_ path, &reparse_type);
1878 if (handle == INVALID_HANDLE_VALUE) {
1879 /* S_follow_symlinks_to() will set errno */
1880 return -1;
1881 }
1882 }
1883 if (handle != INVALID_HANDLE_VALUE) {
1884 result = win32_stat_low(handle, path, strlen(path), sbuf, reparse_type);
1885 CloseHandle(handle);
1886 }
1887 else {
1888 translate_to_errno();
1889 result = -1;
1890 }
1891
1892 return result;
1893 }
1894
1895 static void
translate_to_errno(void)1896 translate_to_errno(void)
1897 {
1898 /* This isn't perfect, eg. Win32 returns ERROR_ACCESS_DENIED for
1899 both permissions errors and if the source is a directory, while
1900 POSIX wants EACCES and EPERM respectively.
1901 */
1902 switch (GetLastError()) {
1903 case ERROR_BAD_NET_NAME:
1904 case ERROR_BAD_NETPATH:
1905 case ERROR_BAD_PATHNAME:
1906 case ERROR_FILE_NOT_FOUND:
1907 case ERROR_FILENAME_EXCED_RANGE:
1908 case ERROR_INVALID_DRIVE:
1909 case ERROR_PATH_NOT_FOUND:
1910 errno = ENOENT;
1911 break;
1912 case ERROR_ALREADY_EXISTS:
1913 errno = EEXIST;
1914 break;
1915 case ERROR_ACCESS_DENIED:
1916 errno = EACCES;
1917 break;
1918 case ERROR_PRIVILEGE_NOT_HELD:
1919 errno = EPERM;
1920 break;
1921 case ERROR_NOT_SAME_DEVICE:
1922 errno = EXDEV;
1923 break;
1924 case ERROR_DISK_FULL:
1925 errno = ENOSPC;
1926 break;
1927 case ERROR_NOT_ENOUGH_QUOTA:
1928 errno = EDQUOT;
1929 break;
1930 default:
1931 /* ERROR_INVALID_FUNCTION - eg. symlink on a FAT volume */
1932 errno = EINVAL;
1933 break;
1934 }
1935 }
1936
1937 static BOOL
is_symlink(HANDLE h)1938 is_symlink(HANDLE h) {
1939 MY_REPARSE_DATA_BUFFER linkdata;
1940 const MY_SYMLINK_REPARSE_BUFFER * const sd =
1941 &linkdata.Data.SymbolicLinkReparseBuffer;
1942 DWORD linkdata_returned;
1943
1944 if (!DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, &linkdata, sizeof(linkdata), &linkdata_returned, NULL)) {
1945 return FALSE;
1946 }
1947
1948 if (linkdata_returned < offsetof(MY_REPARSE_DATA_BUFFER, Data.SymbolicLinkReparseBuffer.PathBuffer)
1949 || (linkdata.ReparseTag != IO_REPARSE_TAG_SYMLINK
1950 && linkdata.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)) {
1951 /* some other type of reparse point */
1952 return FALSE;
1953 }
1954
1955 return TRUE;
1956 }
1957
1958 static BOOL
is_symlink_name(const char * name)1959 is_symlink_name(const char *name) {
1960 HANDLE f = CreateFileA(name, GENERIC_READ, 0, NULL, OPEN_EXISTING,
1961 FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, 0);
1962 BOOL result;
1963
1964 if (f == INVALID_HANDLE_VALUE) {
1965 return FALSE;
1966 }
1967 result = is_symlink(f);
1968 CloseHandle(f);
1969
1970 return result;
1971 }
1972
1973 static int
do_readlink_handle(HANDLE hlink,char * buf,size_t bufsiz,bool * is_symlink)1974 do_readlink_handle(HANDLE hlink, char *buf, size_t bufsiz, bool *is_symlink) {
1975 MY_REPARSE_DATA_BUFFER linkdata;
1976 DWORD linkdata_returned;
1977
1978 if (is_symlink)
1979 *is_symlink = FALSE;
1980
1981 if (!DeviceIoControl(hlink, FSCTL_GET_REPARSE_POINT, NULL, 0, &linkdata, sizeof(linkdata), &linkdata_returned, NULL)) {
1982 translate_to_errno();
1983 return -1;
1984 }
1985
1986 int bytes_out;
1987 BOOL used_default;
1988 switch (linkdata.ReparseTag) {
1989 case IO_REPARSE_TAG_SYMLINK:
1990 {
1991 const MY_SYMLINK_REPARSE_BUFFER * const sd =
1992 &linkdata.Data.SymbolicLinkReparseBuffer;
1993 if (linkdata_returned < offsetof(MY_REPARSE_DATA_BUFFER, Data.SymbolicLinkReparseBuffer.PathBuffer)) {
1994 errno = EINVAL;
1995 return -1;
1996 }
1997 bytes_out =
1998 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
1999 sd->PathBuffer + sd->PrintNameOffset/2,
2000 sd->PrintNameLength/2,
2001 buf, (int)bufsiz, NULL, &used_default);
2002 if (is_symlink)
2003 *is_symlink = TRUE;
2004 }
2005 break;
2006 case IO_REPARSE_TAG_MOUNT_POINT:
2007 {
2008 const MY_MOUNT_POINT_REPARSE_BUFFER * const rd =
2009 &linkdata.Data.MountPointReparseBuffer;
2010 if (linkdata_returned < offsetof(MY_REPARSE_DATA_BUFFER, Data.MountPointReparseBuffer.PathBuffer)) {
2011 errno = EINVAL;
2012 return -1;
2013 }
2014 bytes_out =
2015 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
2016 rd->PathBuffer + rd->PrintNameOffset/2,
2017 rd->PrintNameLength/2,
2018 buf, (int)bufsiz, NULL, &used_default);
2019 if (is_symlink)
2020 *is_symlink = TRUE;
2021 }
2022 break;
2023
2024 default:
2025 errno = EINVAL;
2026 return -1;
2027 }
2028
2029 if (bytes_out == 0 || used_default) {
2030 /* failed conversion from unicode to ANSI or otherwise failed */
2031 errno = EINVAL;
2032 return -1;
2033 }
2034
2035 return bytes_out;
2036 }
2037
2038 DllExport int
win32_readlink(const char * pathname,char * buf,size_t bufsiz)2039 win32_readlink(const char *pathname, char *buf, size_t bufsiz) {
2040 if (pathname == NULL || buf == NULL) {
2041 errno = EFAULT;
2042 return -1;
2043 }
2044 if (bufsiz <= 0) {
2045 errno = EINVAL;
2046 return -1;
2047 }
2048
2049 DWORD fileattr = GetFileAttributes(pathname);
2050 if (fileattr == INVALID_FILE_ATTRIBUTES) {
2051 translate_to_errno();
2052 return -1;
2053 }
2054
2055 if (!(fileattr & FILE_ATTRIBUTE_REPARSE_POINT)) {
2056 /* not a symbolic link */
2057 errno = EINVAL;
2058 return -1;
2059 }
2060
2061 HANDLE hlink =
2062 CreateFileA(pathname, GENERIC_READ, 0, NULL, OPEN_EXISTING,
2063 FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, 0);
2064 if (hlink == INVALID_HANDLE_VALUE) {
2065 translate_to_errno();
2066 return -1;
2067 }
2068 int bytes_out = do_readlink_handle(hlink, buf, bufsiz, NULL);
2069 CloseHandle(hlink);
2070 if (bytes_out < 0) {
2071 /* errno already set */
2072 return -1;
2073 }
2074
2075 if ((size_t)bytes_out > bufsiz) {
2076 errno = EINVAL;
2077 return -1;
2078 }
2079
2080 return bytes_out;
2081 }
2082
2083 DllExport int
win32_lstat(const char * path,Stat_t * sbuf)2084 win32_lstat(const char *path, Stat_t *sbuf)
2085 {
2086 HANDLE f;
2087 int result;
2088 DWORD attr = GetFileAttributes(path); /* doesn't follow symlinks */
2089
2090 if (attr == INVALID_FILE_ATTRIBUTES) {
2091 translate_to_errno();
2092 return -1;
2093 }
2094
2095 if (!(attr & FILE_ATTRIBUTE_REPARSE_POINT)) {
2096 return win32_stat(path, sbuf);
2097 }
2098
2099 f = CreateFileA(path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
2100 FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, 0);
2101 if (f == INVALID_HANDLE_VALUE) {
2102 translate_to_errno();
2103 return -1;
2104 }
2105 bool is_symlink;
2106 int size = do_readlink_handle(f, NULL, 0, &is_symlink);
2107 if (!is_symlink) {
2108 /* it isn't a symlink, fallback to normal stat */
2109 CloseHandle(f);
2110 return win32_stat(path, sbuf);
2111 }
2112 else if (size < 0) {
2113 /* some other error, errno already set */
2114 CloseHandle(f);
2115 return -1;
2116 }
2117 result = win32_stat_low(f, NULL, 0, sbuf, 0);
2118
2119 if (result != -1){
2120 sbuf->st_mode = (sbuf->st_mode & ~_S_IFMT) | _S_IFLNK;
2121 sbuf->st_size = size;
2122 }
2123 CloseHandle(f);
2124
2125 return result;
2126 }
2127
2128 #define isSLASH(c) ((c) == '/' || (c) == '\\')
2129 #define SKIP_SLASHES(s) \
2130 STMT_START { \
2131 while (*(s) && isSLASH(*(s))) \
2132 ++(s); \
2133 } STMT_END
2134 #define COPY_NONSLASHES(d,s) \
2135 STMT_START { \
2136 while (*(s) && !isSLASH(*(s))) \
2137 *(d)++ = *(s)++; \
2138 } STMT_END
2139
2140 /* Find the longname of a given path. path is destructively modified.
2141 * It should have space for at least MAX_PATH characters. */
2142 DllExport char *
win32_longpath(char * path)2143 win32_longpath(char *path)
2144 {
2145 WIN32_FIND_DATA fdata;
2146 HANDLE fhand;
2147 char tmpbuf[MAX_PATH+1];
2148 char *tmpstart = tmpbuf;
2149 char *start = path;
2150 char sep;
2151 if (!path)
2152 return NULL;
2153
2154 /* drive prefix */
2155 if (isALPHA(path[0]) && path[1] == ':') {
2156 start = path + 2;
2157 *tmpstart++ = path[0];
2158 *tmpstart++ = ':';
2159 }
2160 /* UNC prefix */
2161 else if (isSLASH(path[0]) && isSLASH(path[1])) {
2162 start = path + 2;
2163 *tmpstart++ = path[0];
2164 *tmpstart++ = path[1];
2165 SKIP_SLASHES(start);
2166 COPY_NONSLASHES(tmpstart,start); /* copy machine name */
2167 if (*start) {
2168 *tmpstart++ = *start++;
2169 SKIP_SLASHES(start);
2170 COPY_NONSLASHES(tmpstart,start); /* copy share name */
2171 }
2172 }
2173 *tmpstart = '\0';
2174 while (*start) {
2175 /* copy initial slash, if any */
2176 if (isSLASH(*start)) {
2177 *tmpstart++ = *start++;
2178 *tmpstart = '\0';
2179 SKIP_SLASHES(start);
2180 }
2181
2182 /* FindFirstFile() expands "." and "..", so we need to pass
2183 * those through unmolested */
2184 if (*start == '.'
2185 && (!start[1] || isSLASH(start[1])
2186 || (start[1] == '.' && (!start[2] || isSLASH(start[2])))))
2187 {
2188 COPY_NONSLASHES(tmpstart,start); /* copy "." or ".." */
2189 *tmpstart = '\0';
2190 continue;
2191 }
2192
2193 /* if this is the end, bust outta here */
2194 if (!*start)
2195 break;
2196
2197 /* now we're at a non-slash; walk up to next slash */
2198 while (*start && !isSLASH(*start))
2199 ++start;
2200
2201 /* stop and find full name of component */
2202 sep = *start;
2203 *start = '\0';
2204 fhand = FindFirstFile(path,&fdata);
2205 *start = sep;
2206 if (fhand != INVALID_HANDLE_VALUE) {
2207 STRLEN len = strlen(fdata.cFileName);
2208 if ((STRLEN)(tmpbuf + sizeof(tmpbuf) - tmpstart) > len) {
2209 strcpy(tmpstart, fdata.cFileName);
2210 tmpstart += len;
2211 FindClose(fhand);
2212 }
2213 else {
2214 FindClose(fhand);
2215 errno = ERANGE;
2216 return NULL;
2217 }
2218 }
2219 else {
2220 /* failed a step, just return without side effects */
2221 errno = EINVAL;
2222 return NULL;
2223 }
2224 }
2225 strcpy(path,tmpbuf);
2226 return path;
2227 }
2228
2229 static void
out_of_memory(void)2230 out_of_memory(void)
2231 {
2232
2233 if (PL_curinterp)
2234 croak_no_mem();
2235 exit(1);
2236 }
2237
2238 void
win32_croak_not_implemented(const char * fname)2239 win32_croak_not_implemented(const char * fname)
2240 {
2241 PERL_ARGS_ASSERT_WIN32_CROAK_NOT_IMPLEMENTED;
2242
2243 Perl_croak_nocontext("%s not implemented!\n", fname);
2244 }
2245
2246 /* Converts a wide character (UTF-16) string to the Windows ANSI code page,
2247 * potentially using the system's default replacement character for any
2248 * unrepresentable characters. The caller must free() the returned string. */
2249 static char*
wstr_to_str(const wchar_t * wstr)2250 wstr_to_str(const wchar_t* wstr)
2251 {
2252 BOOL used_default = FALSE;
2253 size_t wlen = wcslen(wstr) + 1;
2254 int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen,
2255 NULL, 0, NULL, NULL);
2256 char* str = (char*)malloc(len);
2257 if (!str)
2258 out_of_memory();
2259 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen,
2260 str, len, NULL, &used_default);
2261 return str;
2262 }
2263
2264 /* The win32_ansipath() function takes a Unicode filename and converts it
2265 * into the current Windows codepage. If some characters cannot be mapped,
2266 * then it will convert the short name instead.
2267 *
2268 * The buffer to the ansi pathname must be freed with win32_free() when it
2269 * is no longer needed.
2270 *
2271 * The argument to win32_ansipath() must exist before this function is
2272 * called; otherwise there is no way to determine the short path name.
2273 *
2274 * Ideas for future refinement:
2275 * - Only convert those segments of the path that are not in the current
2276 * codepage, but leave the other segments in their long form.
2277 * - If the resulting name is longer than MAX_PATH, start converting
2278 * additional path segments into short names until the full name
2279 * is shorter than MAX_PATH. Shorten the filename part last!
2280 */
2281 DllExport char *
win32_ansipath(const WCHAR * widename)2282 win32_ansipath(const WCHAR *widename)
2283 {
2284 char *name;
2285 BOOL use_default = FALSE;
2286 size_t widelen = wcslen(widename)+1;
2287 int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,
2288 NULL, 0, NULL, NULL);
2289 name = (char*)win32_malloc(len);
2290 if (!name)
2291 out_of_memory();
2292
2293 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,
2294 name, len, NULL, &use_default);
2295 if (use_default) {
2296 DWORD shortlen = GetShortPathNameW(widename, NULL, 0);
2297 if (shortlen) {
2298 WCHAR *shortname = (WCHAR*)win32_malloc(shortlen*sizeof(WCHAR));
2299 if (!shortname)
2300 out_of_memory();
2301 shortlen = GetShortPathNameW(widename, shortname, shortlen)+1;
2302
2303 len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,
2304 NULL, 0, NULL, NULL);
2305 name = (char*)win32_realloc(name, len);
2306 if (!name)
2307 out_of_memory();
2308 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,
2309 name, len, NULL, NULL);
2310 win32_free(shortname);
2311 }
2312 }
2313 return name;
2314 }
2315
2316 /* the returned string must be freed with win32_freeenvironmentstrings which is
2317 * implemented as a macro
2318 * void win32_freeenvironmentstrings(void* block)
2319 */
2320 DllExport char *
win32_getenvironmentstrings(void)2321 win32_getenvironmentstrings(void)
2322 {
2323 LPWSTR lpWStr, lpWTmp;
2324 LPSTR lpStr, lpTmp;
2325 DWORD env_len, wenvstrings_len = 0, aenvstrings_len = 0;
2326
2327 /* Get the process environment strings */
2328 lpWTmp = lpWStr = (LPWSTR) GetEnvironmentStringsW();
2329 for (wenvstrings_len = 1; *lpWTmp != '\0'; lpWTmp += env_len + 1) {
2330 env_len = wcslen(lpWTmp);
2331 /* calculate the size of the environment strings */
2332 wenvstrings_len += env_len + 1;
2333 }
2334
2335 /* Get the number of bytes required to store the ACP encoded string */
2336 aenvstrings_len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS,
2337 lpWStr, wenvstrings_len, NULL, 0, NULL, NULL);
2338 lpTmp = lpStr = (char *)win32_calloc(aenvstrings_len, sizeof(char));
2339 if(!lpTmp)
2340 out_of_memory();
2341
2342 /* Convert the string from UTF-16 encoding to ACP encoding */
2343 WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, lpWStr, wenvstrings_len, lpStr,
2344 aenvstrings_len, NULL, NULL);
2345
2346 FreeEnvironmentStringsW(lpWStr);
2347
2348 return(lpStr);
2349 }
2350
2351 DllExport char *
win32_getenv(const char * name)2352 win32_getenv(const char *name)
2353 {
2354 dTHX;
2355 DWORD needlen;
2356 SV *curitem = NULL;
2357 DWORD last_err;
2358
2359 needlen = GetEnvironmentVariableA(name,NULL,0);
2360 if (needlen != 0) {
2361 curitem = sv_2mortal(newSVpvs(""));
2362 do {
2363 SvGROW(curitem, needlen+1);
2364 needlen = GetEnvironmentVariableA(name,SvPVX(curitem),
2365 needlen);
2366 } while (needlen >= SvLEN(curitem));
2367 SvCUR_set(curitem, needlen);
2368 }
2369 else {
2370 last_err = GetLastError();
2371 if (last_err == ERROR_NOT_ENOUGH_MEMORY) {
2372 /* It appears the variable is in the env, but the Win32 API
2373 doesn't have a canned way of getting it. So we fall back to
2374 grabbing the whole env and pulling this value out if possible */
2375 char *envv = GetEnvironmentStrings();
2376 char *cur = envv;
2377 STRLEN len;
2378 while (*cur) {
2379 char *end = strchr(cur,'=');
2380 if (end && end != cur) {
2381 *end = '\0';
2382 if (strEQ(cur,name)) {
2383 curitem = sv_2mortal(newSVpv(end+1,0));
2384 *end = '=';
2385 break;
2386 }
2387 *end = '=';
2388 cur = end + strlen(end+1)+2;
2389 }
2390 else if ((len = strlen(cur)))
2391 cur += len+1;
2392 }
2393 FreeEnvironmentStrings(envv);
2394 }
2395 #ifndef WIN32_NO_REGISTRY
2396 else {
2397 /* last ditch: allow any environment variables that begin with 'PERL'
2398 to be obtained from the registry, if found there */
2399 if (strBEGINs(name, "PERL"))
2400 (void)get_regstr(name, &curitem);
2401 }
2402 #endif
2403 }
2404 if (curitem && SvCUR(curitem))
2405 return SvPVX(curitem);
2406
2407 return NULL;
2408 }
2409
2410 DllExport int
win32_putenv(const char * name)2411 win32_putenv(const char *name)
2412 {
2413 char* curitem;
2414 char* val;
2415 int relval = -1;
2416
2417 if (name) {
2418 curitem = (char *) win32_malloc(strlen(name)+1);
2419 strcpy(curitem, name);
2420 val = strchr(curitem, '=');
2421 if (val) {
2422 /* The sane way to deal with the environment.
2423 * Has these advantages over putenv() & co.:
2424 * * enables us to store a truly empty value in the
2425 * environment (like in UNIX).
2426 * * we don't have to deal with RTL globals, bugs and leaks
2427 * (specifically, see http://support.microsoft.com/kb/235601).
2428 * * Much faster.
2429 * Why you may want to use the RTL environment handling
2430 * (previously enabled by USE_WIN32_RTL_ENV):
2431 * * environ[] and RTL functions will not reflect changes,
2432 * which might be an issue if extensions want to access
2433 * the env. via RTL. This cuts both ways, since RTL will
2434 * not see changes made by extensions that call the Win32
2435 * functions directly, either.
2436 * GSAR 97-06-07
2437 */
2438 *val++ = '\0';
2439 if (SetEnvironmentVariableA(curitem, *val ? val : NULL))
2440 relval = 0;
2441 }
2442 win32_free(curitem);
2443 }
2444 return relval;
2445 }
2446
2447 static long
filetime_to_clock(PFILETIME ft)2448 filetime_to_clock(PFILETIME ft)
2449 {
2450 __int64 qw = ft->dwHighDateTime;
2451 qw <<= 32;
2452 qw |= ft->dwLowDateTime;
2453 qw /= 10000; /* File time ticks at 0.1uS, clock at 1mS */
2454 return (long) qw;
2455 }
2456
2457 DllExport int
win32_times(struct tms * timebuf)2458 win32_times(struct tms *timebuf)
2459 {
2460 FILETIME user;
2461 FILETIME kernel;
2462 FILETIME dummy;
2463 clock_t process_time_so_far = clock();
2464 if (GetProcessTimes(GetCurrentProcess(), &dummy, &dummy,
2465 &kernel,&user)) {
2466 timebuf->tms_utime = filetime_to_clock(&user);
2467 timebuf->tms_stime = filetime_to_clock(&kernel);
2468 timebuf->tms_cutime = 0;
2469 timebuf->tms_cstime = 0;
2470 } else {
2471 /* That failed - e.g. Win95 fallback to clock() */
2472 timebuf->tms_utime = process_time_so_far;
2473 timebuf->tms_stime = 0;
2474 timebuf->tms_cutime = 0;
2475 timebuf->tms_cstime = 0;
2476 }
2477 return process_time_so_far;
2478 }
2479
2480 static BOOL
filetime_from_time(PFILETIME pFileTime,time_t Time)2481 filetime_from_time(PFILETIME pFileTime, time_t Time)
2482 {
2483 struct tm *pt;
2484 SYSTEMTIME st;
2485 dTHX;
2486
2487 GMTIME_LOCK;
2488 pt = gmtime(&Time);
2489 if (!pt) {
2490 GMTIME_UNLOCK;
2491 pFileTime->dwLowDateTime = 0;
2492 pFileTime->dwHighDateTime = 0;
2493 return FALSE;
2494 }
2495
2496 st.wYear = pt->tm_year + 1900;
2497 st.wMonth = pt->tm_mon + 1;
2498 st.wDay = pt->tm_mday;
2499 st.wHour = pt->tm_hour;
2500 st.wMinute = pt->tm_min;
2501 st.wSecond = pt->tm_sec;
2502 st.wMilliseconds = 0;
2503
2504 GMTIME_UNLOCK;
2505
2506 if (!SystemTimeToFileTime(&st, pFileTime)) {
2507 pFileTime->dwLowDateTime = 0;
2508 pFileTime->dwHighDateTime = 0;
2509 return FALSE;
2510 }
2511
2512 return TRUE;
2513 }
2514
2515 DllExport int
win32_unlink(const char * filename)2516 win32_unlink(const char *filename)
2517 {
2518 dTHX;
2519 int ret;
2520 DWORD attrs;
2521
2522 filename = PerlDir_mapA(filename);
2523 attrs = GetFileAttributesA(filename);
2524 if (attrs == 0xFFFFFFFF) {
2525 errno = ENOENT;
2526 return -1;
2527 }
2528 if (attrs & FILE_ATTRIBUTE_READONLY) {
2529 (void)SetFileAttributesA(filename, attrs & ~FILE_ATTRIBUTE_READONLY);
2530 ret = unlink(filename);
2531 if (ret == -1)
2532 (void)SetFileAttributesA(filename, attrs);
2533 }
2534 else if ((attrs & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
2535 == (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY)
2536 && is_symlink_name(filename)) {
2537 ret = rmdir(filename);
2538 }
2539 else {
2540 ret = unlink(filename);
2541 }
2542 return ret;
2543 }
2544
2545 DllExport int
win32_utime(const char * filename,struct utimbuf * times)2546 win32_utime(const char *filename, struct utimbuf *times)
2547 {
2548 dTHX;
2549 HANDLE handle;
2550 FILETIME ftAccess;
2551 FILETIME ftWrite;
2552 struct utimbuf TimeBuffer;
2553 int rc = -1;
2554
2555 filename = PerlDir_mapA(filename);
2556 /* This will (and should) still fail on readonly files */
2557 handle = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE,
2558 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
2559 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
2560 if (handle == INVALID_HANDLE_VALUE) {
2561 translate_to_errno();
2562 return -1;
2563 }
2564
2565 if (times == NULL) {
2566 times = &TimeBuffer;
2567 time(×->actime);
2568 times->modtime = times->actime;
2569 }
2570
2571 if (filetime_from_time(&ftAccess, times->actime) &&
2572 filetime_from_time(&ftWrite, times->modtime)) {
2573 if (SetFileTime(handle, NULL, &ftAccess, &ftWrite)) {
2574 rc = 0;
2575 }
2576 else {
2577 translate_to_errno();
2578 }
2579 }
2580 else {
2581 errno = EINVAL; /* bad time? */
2582 }
2583
2584 CloseHandle(handle);
2585 return rc;
2586 }
2587
2588 typedef union {
2589 unsigned __int64 ft_i64;
2590 FILETIME ft_val;
2591 } FT_t;
2592
2593 #ifdef __GNUC__
2594 #define Const64(x) x##LL
2595 #else
2596 #define Const64(x) x##i64
2597 #endif
2598 /* Number of 100 nanosecond units from 1/1/1601 to 1/1/1970 */
2599 #define EPOCH_BIAS Const64(116444736000000000)
2600
2601 /* NOTE: This does not compute the timezone info (doing so can be expensive,
2602 * and appears to be unsupported even by glibc) */
2603 DllExport int
win32_gettimeofday(struct timeval * tp,void * not_used)2604 win32_gettimeofday(struct timeval *tp, void *not_used)
2605 {
2606 FT_t ft;
2607
2608 /* this returns time in 100-nanosecond units (i.e. tens of usecs) */
2609 GetSystemTimeAsFileTime(&ft.ft_val);
2610
2611 /* seconds since epoch */
2612 tp->tv_sec = (long)((ft.ft_i64 - EPOCH_BIAS) / Const64(10000000));
2613
2614 /* microseconds remaining */
2615 tp->tv_usec = (long)((ft.ft_i64 / Const64(10)) % Const64(1000000));
2616
2617 return 0;
2618 }
2619
2620 DllExport int
win32_uname(struct utsname * name)2621 win32_uname(struct utsname *name)
2622 {
2623 struct hostent *hep;
2624 STRLEN nodemax = sizeof(name->nodename)-1;
2625
2626 /* sysname */
2627 switch (g_osver.dwPlatformId) {
2628 case VER_PLATFORM_WIN32_WINDOWS:
2629 strcpy(name->sysname, "Windows");
2630 break;
2631 case VER_PLATFORM_WIN32_NT:
2632 strcpy(name->sysname, "Windows NT");
2633 break;
2634 case VER_PLATFORM_WIN32s:
2635 strcpy(name->sysname, "Win32s");
2636 break;
2637 default:
2638 strcpy(name->sysname, "Win32 Unknown");
2639 break;
2640 }
2641
2642 /* release */
2643 sprintf(name->release, "%d.%d",
2644 g_osver.dwMajorVersion, g_osver.dwMinorVersion);
2645
2646 /* version */
2647 sprintf(name->version, "Build %d",
2648 g_osver.dwPlatformId == VER_PLATFORM_WIN32_NT
2649 ? g_osver.dwBuildNumber : (g_osver.dwBuildNumber & 0xffff));
2650 if (g_osver.szCSDVersion[0]) {
2651 char *buf = name->version + strlen(name->version);
2652 sprintf(buf, " (%s)", g_osver.szCSDVersion);
2653 }
2654
2655 /* nodename */
2656 hep = win32_gethostbyname("localhost");
2657 if (hep) {
2658 STRLEN len = strlen(hep->h_name);
2659 if (len <= nodemax) {
2660 strcpy(name->nodename, hep->h_name);
2661 }
2662 else {
2663 strncpy(name->nodename, hep->h_name, nodemax);
2664 name->nodename[nodemax] = '\0';
2665 }
2666 }
2667 else {
2668 DWORD sz = nodemax;
2669 if (!GetComputerName(name->nodename, &sz))
2670 *name->nodename = '\0';
2671 }
2672
2673 /* machine (architecture) */
2674 {
2675 SYSTEM_INFO info;
2676 DWORD procarch;
2677 const char *arch;
2678 GetSystemInfo(&info);
2679
2680 #if (defined(__MINGW32__) && !defined(_ANONYMOUS_UNION) && !defined(__MINGW_EXTENSION))
2681 procarch = info.u.s.wProcessorArchitecture;
2682 #else
2683 procarch = info.wProcessorArchitecture;
2684 #endif
2685 switch (procarch) {
2686 case PROCESSOR_ARCHITECTURE_INTEL:
2687 arch = "x86"; break;
2688 case PROCESSOR_ARCHITECTURE_IA64:
2689 arch = "ia64"; break;
2690 case PROCESSOR_ARCHITECTURE_AMD64:
2691 arch = "amd64"; break;
2692 case PROCESSOR_ARCHITECTURE_UNKNOWN:
2693 arch = "unknown"; break;
2694 default:
2695 sprintf(name->machine, "unknown(0x%x)", procarch);
2696 arch = name->machine;
2697 break;
2698 }
2699 if (name->machine != arch)
2700 strcpy(name->machine, arch);
2701 }
2702 return 0;
2703 }
2704
2705 /* Timing related stuff */
2706
2707 int
do_raise(pTHX_ int sig)2708 do_raise(pTHX_ int sig)
2709 {
2710 if (sig < SIG_SIZE) {
2711 Sighandler_t handler = w32_sighandler[sig];
2712 if (handler == SIG_IGN) {
2713 return 0;
2714 }
2715 else if (handler != SIG_DFL) {
2716 (*handler)(sig);
2717 return 0;
2718 }
2719 else {
2720 /* Choose correct default behaviour */
2721 switch (sig) {
2722 #ifdef SIGCLD
2723 case SIGCLD:
2724 #endif
2725 #ifdef SIGCHLD
2726 case SIGCHLD:
2727 #endif
2728 case 0:
2729 return 0;
2730 case SIGTERM:
2731 default:
2732 break;
2733 }
2734 }
2735 }
2736 /* Tell caller to exit thread/process as appropriate */
2737 return 1;
2738 }
2739
2740 void
sig_terminate(pTHX_ int sig)2741 sig_terminate(pTHX_ int sig)
2742 {
2743 Perl_warn(aTHX_ "Terminating on signal SIG%s(%d)\n",PL_sig_name[sig], sig);
2744 /* exit() seems to be safe, my_exit() or die() is a problem in ^C
2745 thread
2746 */
2747 exit(sig);
2748 }
2749
2750 DllExport int
win32_async_check(pTHX)2751 win32_async_check(pTHX)
2752 {
2753 MSG msg;
2754 HWND hwnd = w32_message_hwnd;
2755
2756 /* Reset w32_poll_count before doing anything else, in case we dispatch
2757 * messages that end up calling back into perl */
2758 w32_poll_count = 0;
2759
2760 if (hwnd != INVALID_HANDLE_VALUE) {
2761 /* Passing PeekMessage -1 as HWND (2nd arg) only gets PostThreadMessage() messages
2762 * and ignores window messages - should co-exist better with windows apps e.g. Tk
2763 */
2764 if (hwnd == NULL)
2765 hwnd = (HWND)-1;
2766
2767 while (PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE|PM_NOYIELD) ||
2768 PeekMessage(&msg, hwnd, WM_USER_MIN, WM_USER_MAX, PM_REMOVE|PM_NOYIELD))
2769 {
2770 /* re-post a WM_QUIT message (we'll mark it as read later) */
2771 if(msg.message == WM_QUIT) {
2772 PostQuitMessage((int)msg.wParam);
2773 break;
2774 }
2775
2776 if(!CallMsgFilter(&msg, MSGF_USER))
2777 {
2778 TranslateMessage(&msg);
2779 DispatchMessage(&msg);
2780 }
2781 }
2782 }
2783
2784 /* Call PeekMessage() to mark all pending messages in the queue as "old".
2785 * This is necessary when we are being called by win32_msgwait() to
2786 * make sure MsgWaitForMultipleObjects() stops reporting the same waiting
2787 * message over and over. An example how this can happen is when
2788 * Perl is calling win32_waitpid() inside a GUI application and the GUI
2789 * is generating messages before the process terminated.
2790 */
2791 PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE|PM_NOYIELD);
2792
2793 /* Above or other stuff may have set a signal flag */
2794 if (PL_sig_pending)
2795 despatch_signals();
2796
2797 return 1;
2798 }
2799
2800 /* This function will not return until the timeout has elapsed, or until
2801 * one of the handles is ready. */
2802 DllExport DWORD
win32_msgwait(pTHX_ DWORD count,LPHANDLE handles,DWORD timeout,LPDWORD resultp)2803 win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD resultp)
2804 {
2805 /* We may need several goes at this - so compute when we stop */
2806 FT_t ticks = {0};
2807 unsigned __int64 endtime = timeout;
2808 if (timeout != INFINITE) {
2809 GetSystemTimeAsFileTime(&ticks.ft_val);
2810 ticks.ft_i64 /= 10000;
2811 endtime += ticks.ft_i64;
2812 }
2813 /* This was a race condition. Do not let a non INFINITE timeout to
2814 * MsgWaitForMultipleObjects roll under 0 creating a near
2815 * infinity/~(UINT32)0 timeout which will appear as a deadlock to the
2816 * user who did a CORE perl function with a non infinity timeout,
2817 * sleep for example. This is 64 to 32 truncation minefield.
2818 *
2819 * This scenario can only be created if the timespan from the return of
2820 * MsgWaitForMultipleObjects to GetSystemTimeAsFileTime exceeds 1 ms. To
2821 * generate the scenario, manual breakpoints in a C debugger are required,
2822 * or a context switch occurred in win32_async_check in PeekMessage, or random
2823 * messages are delivered to the *thread* message queue of the Perl thread
2824 * from another process (msctf.dll doing IPC among its instances, VS debugger
2825 * causes msctf.dll to be loaded into Perl by kernel), see [perl #33096].
2826 */
2827 while (ticks.ft_i64 <= endtime) {
2828 /* if timeout's type is lengthened, remember to split 64b timeout
2829 * into multiple non-infinity runs of MWFMO */
2830 DWORD result = MsgWaitForMultipleObjects(count, handles, FALSE,
2831 (DWORD)(endtime - ticks.ft_i64),
2832 QS_POSTMESSAGE|QS_TIMER|QS_SENDMESSAGE);
2833 if (resultp)
2834 *resultp = result;
2835 if (result == WAIT_TIMEOUT) {
2836 /* Ran out of time - explicit return of zero to avoid -ve if we
2837 have scheduling issues
2838 */
2839 return 0;
2840 }
2841 if (timeout != INFINITE) {
2842 GetSystemTimeAsFileTime(&ticks.ft_val);
2843 ticks.ft_i64 /= 10000;
2844 }
2845 if (result == WAIT_OBJECT_0 + count) {
2846 /* Message has arrived - check it */
2847 (void)win32_async_check(aTHX);
2848
2849 /* retry */
2850 if (ticks.ft_i64 > endtime)
2851 endtime = ticks.ft_i64;
2852
2853 continue;
2854 }
2855 else {
2856 /* Not timeout or message - one of handles is ready */
2857 break;
2858 }
2859 }
2860 /* If we are past the end say zero */
2861 if (!ticks.ft_i64 || ticks.ft_i64 > endtime)
2862 return 0;
2863 /* compute time left to wait */
2864 ticks.ft_i64 = endtime - ticks.ft_i64;
2865 /* if more ms than DWORD, then return max DWORD */
2866 return ticks.ft_i64 <= UINT_MAX ? (DWORD)ticks.ft_i64 : UINT_MAX;
2867 }
2868
2869 int
win32_internal_wait(pTHX_ int * status,DWORD timeout)2870 win32_internal_wait(pTHX_ int *status, DWORD timeout)
2871 {
2872 /* XXX this wait emulation only knows about processes
2873 * spawned via win32_spawnvp(P_NOWAIT, ...).
2874 */
2875 int i, retval;
2876 DWORD exitcode, waitcode;
2877
2878 #ifdef USE_ITHREADS
2879 if (w32_num_pseudo_children) {
2880 win32_msgwait(aTHX_ w32_num_pseudo_children, w32_pseudo_child_handles,
2881 timeout, &waitcode);
2882 /* Time out here if there are no other children to wait for. */
2883 if (waitcode == WAIT_TIMEOUT) {
2884 if (!w32_num_children) {
2885 return 0;
2886 }
2887 }
2888 else if (waitcode != WAIT_FAILED) {
2889 if (waitcode >= WAIT_ABANDONED_0
2890 && waitcode < WAIT_ABANDONED_0 + w32_num_pseudo_children)
2891 i = waitcode - WAIT_ABANDONED_0;
2892 else
2893 i = waitcode - WAIT_OBJECT_0;
2894 if (GetExitCodeThread(w32_pseudo_child_handles[i], &exitcode)) {
2895 *status = (int)(((U8) exitcode) << 8);
2896 retval = (int)w32_pseudo_child_pids[i];
2897 remove_dead_pseudo_process(i);
2898 return -retval;
2899 }
2900 }
2901 }
2902 #endif
2903
2904 if (!w32_num_children) {
2905 errno = ECHILD;
2906 return -1;
2907 }
2908
2909 /* if a child exists, wait for it to die */
2910 win32_msgwait(aTHX_ w32_num_children, w32_child_handles, timeout, &waitcode);
2911 if (waitcode == WAIT_TIMEOUT) {
2912 return 0;
2913 }
2914 if (waitcode != WAIT_FAILED) {
2915 if (waitcode >= WAIT_ABANDONED_0
2916 && waitcode < WAIT_ABANDONED_0 + w32_num_children)
2917 i = waitcode - WAIT_ABANDONED_0;
2918 else
2919 i = waitcode - WAIT_OBJECT_0;
2920 if (GetExitCodeProcess(w32_child_handles[i], &exitcode) ) {
2921 *status = (int)(((U8) exitcode) << 8);
2922 retval = (int)w32_child_pids[i];
2923 remove_dead_process(i);
2924 return retval;
2925 }
2926 }
2927
2928 errno = GetLastError();
2929 return -1;
2930 }
2931
2932 DllExport int
win32_waitpid(int pid,int * status,int flags)2933 win32_waitpid(int pid, int *status, int flags)
2934 {
2935 dTHX;
2936 DWORD timeout = (flags & WNOHANG) ? 0 : INFINITE;
2937 int retval = -1;
2938 long child;
2939 if (pid == -1) /* XXX threadid == 1 ? */
2940 return win32_internal_wait(aTHX_ status, timeout);
2941 #ifdef USE_ITHREADS
2942 else if (pid < 0) {
2943 child = find_pseudo_pid(aTHX_ -pid);
2944 if (child >= 0) {
2945 HANDLE hThread = w32_pseudo_child_handles[child];
2946 DWORD waitcode;
2947 win32_msgwait(aTHX_ 1, &hThread, timeout, &waitcode);
2948 if (waitcode == WAIT_TIMEOUT) {
2949 return 0;
2950 }
2951 else if (waitcode == WAIT_OBJECT_0) {
2952 if (GetExitCodeThread(hThread, &waitcode)) {
2953 *status = (int)(((U8) waitcode) << 8);
2954 retval = (int)w32_pseudo_child_pids[child];
2955 remove_dead_pseudo_process(child);
2956 return -retval;
2957 }
2958 }
2959 else
2960 errno = ECHILD;
2961 }
2962 }
2963 #endif
2964 else {
2965 HANDLE hProcess;
2966 DWORD waitcode;
2967 child = find_pid(aTHX_ pid);
2968 if (child >= 0) {
2969 hProcess = w32_child_handles[child];
2970 win32_msgwait(aTHX_ 1, &hProcess, timeout, &waitcode);
2971 if (waitcode == WAIT_TIMEOUT) {
2972 return 0;
2973 }
2974 else if (waitcode == WAIT_OBJECT_0) {
2975 if (GetExitCodeProcess(hProcess, &waitcode)) {
2976 *status = (int)(((U8) waitcode) << 8);
2977 retval = (int)w32_child_pids[child];
2978 remove_dead_process(child);
2979 return retval;
2980 }
2981 }
2982 else
2983 errno = ECHILD;
2984 }
2985 else {
2986 hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
2987 if (hProcess) {
2988 win32_msgwait(aTHX_ 1, &hProcess, timeout, &waitcode);
2989 if (waitcode == WAIT_TIMEOUT) {
2990 CloseHandle(hProcess);
2991 return 0;
2992 }
2993 else if (waitcode == WAIT_OBJECT_0) {
2994 if (GetExitCodeProcess(hProcess, &waitcode)) {
2995 *status = (int)(((U8) waitcode) << 8);
2996 CloseHandle(hProcess);
2997 return pid;
2998 }
2999 }
3000 CloseHandle(hProcess);
3001 }
3002 else
3003 errno = ECHILD;
3004 }
3005 }
3006 return retval >= 0 ? pid : retval;
3007 }
3008
3009 DllExport int
win32_wait(int * status)3010 win32_wait(int *status)
3011 {
3012 dTHX;
3013 return win32_internal_wait(aTHX_ status, INFINITE);
3014 }
3015
3016 DllExport unsigned int
win32_sleep(unsigned int t)3017 win32_sleep(unsigned int t)
3018 {
3019 dTHX;
3020 /* Win32 times are in ms so *1000 in and /1000 out */
3021 if (t > UINT_MAX / 1000) {
3022 Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
3023 "sleep(%lu) too large", t);
3024 }
3025 return win32_msgwait(aTHX_ 0, NULL, t * 1000, NULL) / 1000;
3026 }
3027
3028 DllExport int
win32_pause(void)3029 win32_pause(void)
3030 {
3031 dTHX;
3032 win32_msgwait(aTHX_ 0, NULL, INFINITE, NULL);
3033 return -1;
3034 }
3035
3036 DllExport unsigned int
win32_alarm(unsigned int sec)3037 win32_alarm(unsigned int sec)
3038 {
3039 /*
3040 * the 'obvious' implementation is SetTimer() with a callback
3041 * which does whatever receiving SIGALRM would do
3042 * we cannot use SIGALRM even via raise() as it is not
3043 * one of the supported codes in <signal.h>
3044 */
3045 dTHX;
3046
3047 if (w32_message_hwnd == INVALID_HANDLE_VALUE)
3048 w32_message_hwnd = win32_create_message_window();
3049
3050 if (sec) {
3051 if (w32_message_hwnd == NULL)
3052 w32_timerid = SetTimer(NULL, w32_timerid, sec*1000, NULL);
3053 else {
3054 w32_timerid = 1;
3055 SetTimer(w32_message_hwnd, w32_timerid, sec*1000, NULL);
3056 }
3057 }
3058 else {
3059 if (w32_timerid) {
3060 KillTimer(w32_message_hwnd, w32_timerid);
3061 w32_timerid = 0;
3062 }
3063 }
3064 return 0;
3065 }
3066
3067 extern char * des_fcrypt(const char *txt, const char *salt, char *cbuf);
3068
3069 DllExport char *
win32_crypt(const char * txt,const char * salt)3070 win32_crypt(const char *txt, const char *salt)
3071 {
3072 dTHX;
3073 return des_fcrypt(txt, salt, w32_crypt_buffer);
3074 }
3075
3076 /* simulate flock by locking a range on the file */
3077
3078 #define LK_LEN 0xffff0000
3079
3080 DllExport int
win32_flock(int fd,int oper)3081 win32_flock(int fd, int oper)
3082 {
3083 OVERLAPPED o;
3084 int i = -1;
3085 HANDLE fh;
3086
3087 fh = (HANDLE)_get_osfhandle(fd);
3088 if (fh == (HANDLE)-1) /* _get_osfhandle() already sets errno to EBADF */
3089 return -1;
3090
3091 memset(&o, 0, sizeof(o));
3092
3093 switch(oper) {
3094 case LOCK_SH: /* shared lock */
3095 if (LockFileEx(fh, 0, 0, LK_LEN, 0, &o))
3096 i = 0;
3097 break;
3098 case LOCK_EX: /* exclusive lock */
3099 if (LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o))
3100 i = 0;
3101 break;
3102 case LOCK_SH|LOCK_NB: /* non-blocking shared lock */
3103 if (LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o))
3104 i = 0;
3105 break;
3106 case LOCK_EX|LOCK_NB: /* non-blocking exclusive lock */
3107 if (LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
3108 0, LK_LEN, 0, &o))
3109 i = 0;
3110 break;
3111 case LOCK_UN: /* unlock lock */
3112 if (UnlockFileEx(fh, 0, LK_LEN, 0, &o))
3113 i = 0;
3114 break;
3115 default: /* unknown */
3116 errno = EINVAL;
3117 return -1;
3118 }
3119 if (i == -1) {
3120 if (GetLastError() == ERROR_LOCK_VIOLATION)
3121 errno = EWOULDBLOCK;
3122 else
3123 errno = EINVAL;
3124 }
3125 return i;
3126 }
3127
3128 #undef LK_LEN
3129
3130 extern int convert_wsa_error_to_errno(int wsaerr); /* in win32sck.c */
3131
3132 /* Get the errno value corresponding to the given err. This function is not
3133 * intended to handle conversion of general GetLastError() codes. It only exists
3134 * to translate Windows sockets error codes from WSAGetLastError(). Such codes
3135 * used to be assigned to errno/$! in earlier versions of perl; this function is
3136 * used to catch any old Perl code which is still trying to assign such values
3137 * to $! and convert them to errno values instead.
3138 */
3139 int
win32_get_errno(int err)3140 win32_get_errno(int err)
3141 {
3142 return convert_wsa_error_to_errno(err);
3143 }
3144
3145 /*
3146 * redirected io subsystem for all XS modules
3147 *
3148 */
3149
3150 DllExport int *
win32_errno(void)3151 win32_errno(void)
3152 {
3153 return (&errno);
3154 }
3155
3156 DllExport char ***
win32_environ(void)3157 win32_environ(void)
3158 {
3159 return (&(_environ));
3160 }
3161
3162 /* the rest are the remapped stdio routines */
3163 DllExport FILE *
win32_stderr(void)3164 win32_stderr(void)
3165 {
3166 return (stderr);
3167 }
3168
3169 DllExport FILE *
win32_stdin(void)3170 win32_stdin(void)
3171 {
3172 return (stdin);
3173 }
3174
3175 DllExport FILE *
win32_stdout(void)3176 win32_stdout(void)
3177 {
3178 return (stdout);
3179 }
3180
3181 DllExport int
win32_ferror(FILE * fp)3182 win32_ferror(FILE *fp)
3183 {
3184 return (ferror(fp));
3185 }
3186
3187
3188 DllExport int
win32_feof(FILE * fp)3189 win32_feof(FILE *fp)
3190 {
3191 return (feof(fp));
3192 }
3193
3194 #ifdef ERRNO_HAS_POSIX_SUPPLEMENT
3195 extern int convert_errno_to_wsa_error(int err); /* in win32sck.c */
3196 #endif
3197
3198 /*
3199 * Since the errors returned by the socket error function
3200 * WSAGetLastError() are not known by the library routine strerror
3201 * we have to roll our own to cover the case of socket errors
3202 * that could not be converted to regular errno values by
3203 * get_last_socket_error() in win32/win32sck.c.
3204 */
3205
3206 DllExport char *
win32_strerror(int e)3207 win32_strerror(int e)
3208 {
3209 #if !defined __MINGW32__ /* compiler intolerance */
3210 extern int sys_nerr;
3211 #endif
3212
3213 if (e < 0 || e > sys_nerr) {
3214 dTHXa(NULL);
3215 if (e < 0)
3216 e = GetLastError();
3217 #ifdef ERRNO_HAS_POSIX_SUPPLEMENT
3218 /* VC10+ and some MinGW/gcc-4.8+ define a "POSIX supplement" of errno
3219 * values ranging from EADDRINUSE (100) to EWOULDBLOCK (140), but
3220 * sys_nerr is still 43 and strerror() returns "Unknown error" for them.
3221 * We must therefore still roll our own messages for these codes, and
3222 * additionally map them to corresponding Windows (sockets) error codes
3223 * first to avoid getting the wrong system message.
3224 */
3225 else if (inRANGE(e, EADDRINUSE, EWOULDBLOCK)) {
3226 e = convert_errno_to_wsa_error(e);
3227 }
3228 #endif
3229
3230 aTHXa(PERL_GET_THX);
3231 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
3232 |FORMAT_MESSAGE_IGNORE_INSERTS, NULL, e, 0,
3233 w32_strerror_buffer, sizeof(w32_strerror_buffer),
3234 NULL) == 0)
3235 {
3236 strcpy(w32_strerror_buffer, "Unknown Error");
3237 }
3238 return w32_strerror_buffer;
3239 }
3240 #undef strerror
3241 return strerror(e);
3242 #define strerror win32_strerror
3243 }
3244
3245 DllExport void
win32_str_os_error(void * sv,DWORD dwErr)3246 win32_str_os_error(void *sv, DWORD dwErr)
3247 {
3248 DWORD dwLen;
3249 char *sMsg;
3250 dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
3251 |FORMAT_MESSAGE_IGNORE_INSERTS
3252 |FORMAT_MESSAGE_FROM_SYSTEM, NULL,
3253 dwErr, 0, (char *)&sMsg, 1, NULL);
3254 /* strip trailing whitespace and period */
3255 if (0 < dwLen) {
3256 do {
3257 --dwLen; /* dwLen doesn't include trailing null */
3258 } while (0 < dwLen && isSPACE(sMsg[dwLen]));
3259 if ('.' != sMsg[dwLen])
3260 dwLen++;
3261 sMsg[dwLen] = '\0';
3262 }
3263 if (0 == dwLen) {
3264 sMsg = (char*)LocalAlloc(0, 64/**sizeof(TCHAR)*/);
3265 if (sMsg)
3266 dwLen = sprintf(sMsg,
3267 "Unknown error #0x%lX (lookup 0x%lX)",
3268 dwErr, GetLastError());
3269 }
3270 if (sMsg) {
3271 dTHX;
3272 sv_setpvn((SV*)sv, sMsg, dwLen);
3273 LocalFree(sMsg);
3274 }
3275 }
3276
3277 DllExport int
win32_fprintf(FILE * fp,const char * format,...)3278 win32_fprintf(FILE *fp, const char *format, ...)
3279 {
3280 va_list marker;
3281 va_start(marker, format); /* Initialize variable arguments. */
3282
3283 return (vfprintf(fp, format, marker));
3284 }
3285
3286 DllExport int
win32_printf(const char * format,...)3287 win32_printf(const char *format, ...)
3288 {
3289 va_list marker;
3290 va_start(marker, format); /* Initialize variable arguments. */
3291
3292 return (vprintf(format, marker));
3293 }
3294
3295 DllExport int
win32_vfprintf(FILE * fp,const char * format,va_list args)3296 win32_vfprintf(FILE *fp, const char *format, va_list args)
3297 {
3298 return (vfprintf(fp, format, args));
3299 }
3300
3301 DllExport int
win32_vprintf(const char * format,va_list args)3302 win32_vprintf(const char *format, va_list args)
3303 {
3304 return (vprintf(format, args));
3305 }
3306
3307 DllExport size_t
win32_fread(void * buf,size_t size,size_t count,FILE * fp)3308 win32_fread(void *buf, size_t size, size_t count, FILE *fp)
3309 {
3310 return fread(buf, size, count, fp);
3311 }
3312
3313 DllExport size_t
win32_fwrite(const void * buf,size_t size,size_t count,FILE * fp)3314 win32_fwrite(const void *buf, size_t size, size_t count, FILE *fp)
3315 {
3316 return fwrite(buf, size, count, fp);
3317 }
3318
3319 #define MODE_SIZE 10
3320
3321 DllExport FILE *
win32_fopen(const char * filename,const char * mode)3322 win32_fopen(const char *filename, const char *mode)
3323 {
3324 dTHXa(NULL);
3325 FILE *f;
3326
3327 if (!*filename)
3328 return NULL;
3329
3330 if (stricmp(filename, "/dev/null")==0)
3331 filename = "NUL";
3332
3333 aTHXa(PERL_GET_THX);
3334 f = fopen(PerlDir_mapA(filename), mode);
3335 /* avoid buffering headaches for child processes */
3336 if (f && *mode == 'a')
3337 win32_fseek(f, 0, SEEK_END);
3338 return f;
3339 }
3340
3341 DllExport FILE *
win32_fdopen(int handle,const char * mode)3342 win32_fdopen(int handle, const char *mode)
3343 {
3344 FILE *f;
3345 f = fdopen(handle, (char *) mode);
3346 /* avoid buffering headaches for child processes */
3347 if (f && *mode == 'a')
3348 win32_fseek(f, 0, SEEK_END);
3349 return f;
3350 }
3351
3352 DllExport FILE *
win32_freopen(const char * path,const char * mode,FILE * stream)3353 win32_freopen(const char *path, const char *mode, FILE *stream)
3354 {
3355 dTHXa(NULL);
3356 if (stricmp(path, "/dev/null")==0)
3357 path = "NUL";
3358
3359 aTHXa(PERL_GET_THX);
3360 return freopen(PerlDir_mapA(path), mode, stream);
3361 }
3362
3363 DllExport int
win32_fclose(FILE * pf)3364 win32_fclose(FILE *pf)
3365 {
3366 return fclose(pf);
3367 }
3368
3369 DllExport int
win32_fputs(const char * s,FILE * pf)3370 win32_fputs(const char *s,FILE *pf)
3371 {
3372 return fputs(s, pf);
3373 }
3374
3375 DllExport int
win32_fputc(int c,FILE * pf)3376 win32_fputc(int c,FILE *pf)
3377 {
3378 return fputc(c,pf);
3379 }
3380
3381 DllExport int
win32_ungetc(int c,FILE * pf)3382 win32_ungetc(int c,FILE *pf)
3383 {
3384 return ungetc(c,pf);
3385 }
3386
3387 DllExport int
win32_getc(FILE * pf)3388 win32_getc(FILE *pf)
3389 {
3390 return getc(pf);
3391 }
3392
3393 DllExport int
win32_fileno(FILE * pf)3394 win32_fileno(FILE *pf)
3395 {
3396 return fileno(pf);
3397 }
3398
3399 DllExport void
win32_clearerr(FILE * pf)3400 win32_clearerr(FILE *pf)
3401 {
3402 clearerr(pf);
3403 return;
3404 }
3405
3406 DllExport int
win32_fflush(FILE * pf)3407 win32_fflush(FILE *pf)
3408 {
3409 return fflush(pf);
3410 }
3411
3412 DllExport Off_t
win32_ftell(FILE * pf)3413 win32_ftell(FILE *pf)
3414 {
3415 fpos_t pos;
3416 if (fgetpos(pf, &pos))
3417 return -1;
3418 return (Off_t)pos;
3419 }
3420
3421 DllExport int
win32_fseek(FILE * pf,Off_t offset,int origin)3422 win32_fseek(FILE *pf, Off_t offset,int origin)
3423 {
3424 fpos_t pos;
3425 switch (origin) {
3426 case SEEK_CUR:
3427 if (fgetpos(pf, &pos))
3428 return -1;
3429 offset += pos;
3430 break;
3431 case SEEK_END:
3432 fseek(pf, 0, SEEK_END);
3433 pos = _telli64(fileno(pf));
3434 offset += pos;
3435 break;
3436 case SEEK_SET:
3437 break;
3438 default:
3439 errno = EINVAL;
3440 return -1;
3441 }
3442 return fsetpos(pf, &offset);
3443 }
3444
3445 DllExport int
win32_fgetpos(FILE * pf,fpos_t * p)3446 win32_fgetpos(FILE *pf,fpos_t *p)
3447 {
3448 return fgetpos(pf, p);
3449 }
3450
3451 DllExport int
win32_fsetpos(FILE * pf,const fpos_t * p)3452 win32_fsetpos(FILE *pf,const fpos_t *p)
3453 {
3454 return fsetpos(pf, p);
3455 }
3456
3457 DllExport void
win32_rewind(FILE * pf)3458 win32_rewind(FILE *pf)
3459 {
3460 rewind(pf);
3461 return;
3462 }
3463
3464 DllExport int
win32_tmpfd(void)3465 win32_tmpfd(void)
3466 {
3467 return win32_tmpfd_mode(0);
3468 }
3469
3470 DllExport int
win32_tmpfd_mode(int mode)3471 win32_tmpfd_mode(int mode)
3472 {
3473 char prefix[MAX_PATH+1];
3474 char filename[MAX_PATH+1];
3475 DWORD len = GetTempPath(MAX_PATH, prefix);
3476 mode &= ~( O_ACCMODE | O_CREAT | O_EXCL );
3477 mode |= O_RDWR;
3478 if (len && len < MAX_PATH) {
3479 if (GetTempFileName(prefix, "plx", 0, filename)) {
3480 HANDLE fh = CreateFile(filename,
3481 DELETE | GENERIC_READ | GENERIC_WRITE,
3482 0,
3483 NULL,
3484 CREATE_ALWAYS,
3485 FILE_ATTRIBUTE_NORMAL
3486 | FILE_FLAG_DELETE_ON_CLOSE,
3487 NULL);
3488 if (fh != INVALID_HANDLE_VALUE) {
3489 int fd = win32_open_osfhandle((intptr_t)fh, mode);
3490 if (fd >= 0) {
3491 PERL_DEB(dTHX;)
3492 DEBUG_p(PerlIO_printf(Perl_debug_log,
3493 "Created tmpfile=%s\n",filename));
3494 return fd;
3495 }
3496 }
3497 }
3498 }
3499 return -1;
3500 }
3501
3502 DllExport FILE*
win32_tmpfile(void)3503 win32_tmpfile(void)
3504 {
3505 int fd = win32_tmpfd();
3506 if (fd >= 0)
3507 return win32_fdopen(fd, "w+b");
3508 return NULL;
3509 }
3510
3511 DllExport void
win32_abort(void)3512 win32_abort(void)
3513 {
3514 abort();
3515 return;
3516 }
3517
3518 DllExport int
win32_fstat(int fd,Stat_t * sbufptr)3519 win32_fstat(int fd, Stat_t *sbufptr)
3520 {
3521 HANDLE handle = (HANDLE)win32_get_osfhandle(fd);
3522
3523 return win32_stat_low(handle, NULL, 0, sbufptr, 0);
3524 }
3525
3526 DllExport int
win32_pipe(int * pfd,unsigned int size,int mode)3527 win32_pipe(int *pfd, unsigned int size, int mode)
3528 {
3529 return _pipe(pfd, size, mode);
3530 }
3531
3532 DllExport PerlIO*
win32_popenlist(const char * mode,IV narg,SV ** args)3533 win32_popenlist(const char *mode, IV narg, SV **args)
3534 {
3535 if (get_shell() < 0)
3536 return NULL;
3537
3538 return do_popen(mode, NULL, narg, args);
3539 }
3540
3541 STATIC PerlIO*
do_popen(const char * mode,const char * command,IV narg,SV ** args)3542 do_popen(const char *mode, const char *command, IV narg, SV **args) {
3543 int p[2];
3544 int handles[3];
3545 int parent, child;
3546 int stdfd;
3547 int ourmode;
3548 int childpid;
3549 DWORD nhandle;
3550 int lock_held = 0;
3551 const char **args_pvs = NULL;
3552
3553 /* establish which ends read and write */
3554 if (strchr(mode,'w')) {
3555 stdfd = 0; /* stdin */
3556 parent = 1;
3557 child = 0;
3558 nhandle = STD_INPUT_HANDLE;
3559 }
3560 else if (strchr(mode,'r')) {
3561 stdfd = 1; /* stdout */
3562 parent = 0;
3563 child = 1;
3564 nhandle = STD_OUTPUT_HANDLE;
3565 }
3566 else
3567 return NULL;
3568
3569 /* set the correct mode */
3570 if (strchr(mode,'b'))
3571 ourmode = O_BINARY;
3572 else if (strchr(mode,'t'))
3573 ourmode = O_TEXT;
3574 else
3575 ourmode = _fmode & (O_TEXT | O_BINARY);
3576
3577 /* the child doesn't inherit handles */
3578 ourmode |= O_NOINHERIT;
3579
3580 if (win32_pipe(p, 512, ourmode) == -1)
3581 return NULL;
3582
3583 /* Previously this code redirected stdin/out temporarily so the
3584 child process inherited those handles, this caused race
3585 conditions when another thread was writing/reading those
3586 handles.
3587
3588 To avoid that we just feed the handles to CreateProcess() so
3589 the handles are redirected only in the child.
3590 */
3591 handles[child] = p[child];
3592 handles[parent] = -1;
3593 handles[2] = -1;
3594
3595 /* CreateProcess() requires inheritable handles */
3596 if (!SetHandleInformation((HANDLE)_get_osfhandle(p[child]), HANDLE_FLAG_INHERIT,
3597 HANDLE_FLAG_INHERIT)) {
3598 goto cleanup;
3599 }
3600
3601 /* start the child */
3602 {
3603 dTHX;
3604
3605 if (command) {
3606 if ((childpid = do_spawn2_handles(aTHX_ command, EXECF_SPAWN_NOWAIT, handles)) == -1)
3607 goto cleanup;
3608
3609 }
3610 else {
3611 int i;
3612 const char *exe_name;
3613
3614 Newx(args_pvs, narg + 1 + w32_perlshell_items, const char *);
3615 SAVEFREEPV(args_pvs);
3616 for (i = 0; i < narg; ++i)
3617 args_pvs[i] = SvPV_nolen(args[i]);
3618 args_pvs[i] = NULL;
3619 exe_name = qualified_path(args_pvs[0], TRUE);
3620 if (!exe_name)
3621 /* let CreateProcess() try to find it instead */
3622 exe_name = args_pvs[0];
3623
3624 if ((childpid = do_spawnvp_handles(P_NOWAIT, exe_name, args_pvs, handles)) == -1) {
3625 goto cleanup;
3626 }
3627 }
3628
3629 win32_close(p[child]);
3630
3631 sv_setiv(*av_fetch(w32_fdpid, p[parent], TRUE), childpid);
3632
3633 /* set process id so that it can be returned by perl's open() */
3634 PL_forkprocess = childpid;
3635 }
3636
3637 /* we have an fd, return a file stream */
3638 return (PerlIO_fdopen(p[parent], (char *)mode));
3639
3640 cleanup:
3641 /* we don't need to check for errors here */
3642 win32_close(p[0]);
3643 win32_close(p[1]);
3644
3645 return (NULL);
3646 }
3647
3648 /*
3649 * a popen() clone that respects PERL5SHELL
3650 *
3651 * changed to return PerlIO* rather than FILE * by BKS, 11-11-2000
3652 */
3653
3654 DllExport PerlIO*
win32_popen(const char * command,const char * mode)3655 win32_popen(const char *command, const char *mode)
3656 {
3657 #ifdef USE_RTL_POPEN
3658 return _popen(command, mode);
3659 #else
3660 return do_popen(mode, command, 0, NULL);
3661 #endif /* USE_RTL_POPEN */
3662 }
3663
3664 /*
3665 * pclose() clone
3666 */
3667
3668 DllExport int
win32_pclose(PerlIO * pf)3669 win32_pclose(PerlIO *pf)
3670 {
3671 #ifdef USE_RTL_POPEN
3672 return _pclose(pf);
3673 #else
3674 dTHX;
3675 int childpid, status;
3676 SV *sv;
3677
3678 sv = *av_fetch(w32_fdpid, PerlIO_fileno(pf), TRUE);
3679
3680 if (SvIOK(sv))
3681 childpid = SvIVX(sv);
3682 else
3683 childpid = 0;
3684
3685 if (!childpid) {
3686 errno = EBADF;
3687 return -1;
3688 }
3689
3690 #ifdef USE_PERLIO
3691 PerlIO_close(pf);
3692 #else
3693 fclose(pf);
3694 #endif
3695 SvIVX(sv) = 0;
3696
3697 if (win32_waitpid(childpid, &status, 0) == -1)
3698 return -1;
3699
3700 return status;
3701
3702 #endif /* USE_RTL_POPEN */
3703 }
3704
3705 DllExport int
win32_link(const char * oldname,const char * newname)3706 win32_link(const char *oldname, const char *newname)
3707 {
3708 dTHXa(NULL);
3709 WCHAR wOldName[MAX_PATH+1];
3710 WCHAR wNewName[MAX_PATH+1];
3711
3712 if (MultiByteToWideChar(CP_ACP, 0, oldname, -1, wOldName, MAX_PATH+1) &&
3713 MultiByteToWideChar(CP_ACP, 0, newname, -1, wNewName, MAX_PATH+1) &&
3714 ((aTHXa(PERL_GET_THX)), wcscpy(wOldName, PerlDir_mapW(wOldName)),
3715 CreateHardLinkW(PerlDir_mapW(wNewName), wOldName, NULL)))
3716 {
3717 return 0;
3718 }
3719 translate_to_errno();
3720 return -1;
3721 }
3722
3723 typedef BOOLEAN (__stdcall *pCreateSymbolicLinkA_t)(LPCSTR, LPCSTR, DWORD);
3724
3725 #ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
3726 # define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
3727 #endif
3728
3729 #ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
3730 # define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x2
3731 #endif
3732
3733 DllExport int
win32_symlink(const char * oldfile,const char * newfile)3734 win32_symlink(const char *oldfile, const char *newfile)
3735 {
3736 dTHX;
3737 size_t oldfile_len = strlen(oldfile);
3738 pCreateSymbolicLinkA_t pCreateSymbolicLinkA =
3739 (pCreateSymbolicLinkA_t)GetProcAddress(GetModuleHandle("kernel32.dll"), "CreateSymbolicLinkA");
3740 DWORD create_flags = 0;
3741
3742 /* this flag can be used only on Windows 10 1703 or newer */
3743 if (g_osver.dwMajorVersion > 10 ||
3744 (g_osver.dwMajorVersion == 10 &&
3745 (g_osver.dwMinorVersion > 0 || g_osver.dwBuildNumber > 15063)))
3746 {
3747 create_flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
3748 }
3749
3750 if (!pCreateSymbolicLinkA) {
3751 errno = ENOSYS;
3752 return -1;
3753 }
3754
3755 /* oldfile might be relative and we don't want to change that,
3756 so don't map that.
3757 */
3758 newfile = PerlDir_mapA(newfile);
3759
3760 if (strchr(oldfile, '/')) {
3761 /* Win32 (or perhaps NTFS) won't follow symlinks containing
3762 /, so replace any with \\
3763 */
3764 char *temp = savepv(oldfile);
3765 SAVEFREEPV(temp);
3766 char *p = temp;
3767 while (*p) {
3768 if (*p == '/') {
3769 *p = '\\';
3770 }
3771 ++p;
3772 }
3773 *p = 0;
3774 oldfile = temp;
3775 oldfile_len = p - temp;
3776 }
3777
3778 /* are we linking to a directory?
3779 CreateSymlinkA() needs to know if the target is a directory,
3780 If it looks like a directory name:
3781 - ends in slash
3782 - is just . or ..
3783 - ends in /. or /.. (with either slash)
3784 - is a simple drive letter
3785 assume it's a directory.
3786
3787 Otherwise if the oldfile is relative we need to make a relative path
3788 based on the newfile to check if the target is a directory.
3789 */
3790 if ((oldfile_len >= 1 && isSLASH(oldfile[oldfile_len-1])) ||
3791 strEQ(oldfile, "..") ||
3792 strEQ(oldfile, ".") ||
3793 (isSLASH(oldfile[oldfile_len-2]) && oldfile[oldfile_len-1] == '.') ||
3794 strEQ(oldfile+oldfile_len-3, "\\..") ||
3795 (oldfile_len == 2 && oldfile[1] == ':')) {
3796 create_flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
3797 }
3798 else {
3799 DWORD dest_attr;
3800 const char *dest_path = oldfile;
3801 char szTargetName[MAX_PATH+1];
3802
3803 if (oldfile_len >= 3 && oldfile[1] == ':') {
3804 /* relative to current directory on a drive, or absolute */
3805 /* dest_path = oldfile; already done */
3806 }
3807 else if (oldfile[0] != '\\') {
3808 size_t newfile_len = strlen(newfile);
3809 const char *last_slash = strrchr(newfile, '/');
3810 const char *last_bslash = strrchr(newfile, '\\');
3811 const char *end_dir = last_slash && last_bslash
3812 ? ( last_slash > last_bslash ? last_slash : last_bslash)
3813 : last_slash ? last_slash : last_bslash ? last_bslash : NULL;
3814
3815 if (end_dir) {
3816 if ((end_dir - newfile + 1) + oldfile_len > MAX_PATH) {
3817 /* too long */
3818 errno = EINVAL;
3819 return -1;
3820 }
3821
3822 memcpy(szTargetName, newfile, end_dir - newfile + 1);
3823 strcpy(szTargetName + (end_dir - newfile + 1), oldfile);
3824 dest_path = szTargetName;
3825 }
3826 else {
3827 /* newpath is just a filename */
3828 /* dest_path = oldfile; */
3829 }
3830 }
3831
3832 dest_attr = GetFileAttributes(dest_path);
3833 if (dest_attr != (DWORD)-1 && (dest_attr & FILE_ATTRIBUTE_DIRECTORY)) {
3834 create_flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
3835 }
3836 }
3837
3838 if (!pCreateSymbolicLinkA(newfile, oldfile, create_flags)) {
3839 translate_to_errno();
3840 return -1;
3841 }
3842
3843 return 0;
3844 }
3845
3846 DllExport int
win32_rename(const char * oname,const char * newname)3847 win32_rename(const char *oname, const char *newname)
3848 {
3849 char szOldName[MAX_PATH+1];
3850 BOOL bResult;
3851 DWORD dwFlags = MOVEFILE_COPY_ALLOWED;
3852 dTHX;
3853
3854 if (stricmp(newname, oname))
3855 dwFlags |= MOVEFILE_REPLACE_EXISTING;
3856 strcpy(szOldName, PerlDir_mapA(oname));
3857
3858 bResult = MoveFileExA(szOldName,PerlDir_mapA(newname), dwFlags);
3859 if (!bResult) {
3860 DWORD err = GetLastError();
3861 switch (err) {
3862 case ERROR_BAD_NET_NAME:
3863 case ERROR_BAD_NETPATH:
3864 case ERROR_BAD_PATHNAME:
3865 case ERROR_FILE_NOT_FOUND:
3866 case ERROR_FILENAME_EXCED_RANGE:
3867 case ERROR_INVALID_DRIVE:
3868 case ERROR_NO_MORE_FILES:
3869 case ERROR_PATH_NOT_FOUND:
3870 errno = ENOENT;
3871 break;
3872 case ERROR_DISK_FULL:
3873 errno = ENOSPC;
3874 break;
3875 case ERROR_NOT_ENOUGH_QUOTA:
3876 errno = EDQUOT;
3877 break;
3878 default:
3879 errno = EACCES;
3880 break;
3881 }
3882 return -1;
3883 }
3884 return 0;
3885 }
3886
3887 DllExport int
win32_setmode(int fd,int mode)3888 win32_setmode(int fd, int mode)
3889 {
3890 return setmode(fd, mode);
3891 }
3892
3893 DllExport int
win32_chsize(int fd,Off_t size)3894 win32_chsize(int fd, Off_t size)
3895 {
3896 int retval = 0;
3897 Off_t cur, end, extend;
3898
3899 cur = win32_tell(fd);
3900 if (cur < 0)
3901 return -1;
3902 end = win32_lseek(fd, 0, SEEK_END);
3903 if (end < 0)
3904 return -1;
3905 extend = size - end;
3906 if (extend == 0) {
3907 /* do nothing */
3908 }
3909 else if (extend > 0) {
3910 /* must grow the file, padding with nulls */
3911 char b[4096];
3912 int oldmode = win32_setmode(fd, O_BINARY);
3913 size_t count;
3914 memset(b, '\0', sizeof(b));
3915 do {
3916 count = extend >= sizeof(b) ? sizeof(b) : (size_t)extend;
3917 count = win32_write(fd, b, count);
3918 if ((int)count < 0) {
3919 retval = -1;
3920 break;
3921 }
3922 } while ((extend -= count) > 0);
3923 win32_setmode(fd, oldmode);
3924 }
3925 else {
3926 /* shrink the file */
3927 win32_lseek(fd, size, SEEK_SET);
3928 if (!SetEndOfFile((HANDLE)_get_osfhandle(fd))) {
3929 errno = EACCES;
3930 retval = -1;
3931 }
3932 }
3933 win32_lseek(fd, cur, SEEK_SET);
3934 return retval;
3935 }
3936
3937 DllExport Off_t
win32_lseek(int fd,Off_t offset,int origin)3938 win32_lseek(int fd, Off_t offset, int origin)
3939 {
3940 return _lseeki64(fd, offset, origin);
3941 }
3942
3943 DllExport Off_t
win32_tell(int fd)3944 win32_tell(int fd)
3945 {
3946 return _telli64(fd);
3947 }
3948
3949 DllExport int
win32_open(const char * path,int flag,...)3950 win32_open(const char *path, int flag, ...)
3951 {
3952 dTHXa(NULL);
3953 va_list ap;
3954 int pmode;
3955
3956 va_start(ap, flag);
3957 pmode = va_arg(ap, int);
3958 va_end(ap);
3959
3960 if (stricmp(path, "/dev/null")==0)
3961 path = "NUL";
3962
3963 aTHXa(PERL_GET_THX);
3964 return open(PerlDir_mapA(path), flag, pmode);
3965 }
3966
3967 DllExport int
win32_close(int fd)3968 win32_close(int fd)
3969 {
3970 return _close(fd);
3971 }
3972
3973 DllExport int
win32_eof(int fd)3974 win32_eof(int fd)
3975 {
3976 return eof(fd);
3977 }
3978
3979 DllExport int
win32_isatty(int fd)3980 win32_isatty(int fd)
3981 {
3982 /* The Microsoft isatty() function returns true for *all*
3983 * character mode devices, including "nul". Our implementation
3984 * should only return true if the handle has a console buffer.
3985 */
3986 DWORD mode;
3987 HANDLE fh = (HANDLE)_get_osfhandle(fd);
3988 if (fh == (HANDLE)-1) {
3989 /* errno is already set to EBADF */
3990 return 0;
3991 }
3992
3993 if (GetConsoleMode(fh, &mode))
3994 return 1;
3995
3996 errno = ENOTTY;
3997 return 0;
3998 }
3999
4000 DllExport int
win32_dup(int fd)4001 win32_dup(int fd)
4002 {
4003 return dup(fd);
4004 }
4005
4006 DllExport int
win32_dup2(int fd1,int fd2)4007 win32_dup2(int fd1,int fd2)
4008 {
4009 return dup2(fd1,fd2);
4010 }
4011
4012 static int
win32_read_console(int fd,U8 * buf,unsigned int cnt)4013 win32_read_console(int fd, U8 *buf, unsigned int cnt)
4014 {
4015 /* This function is a workaround for a bug in Windows:
4016 * https://github.com/microsoft/terminal/issues/4551
4017 * tl;dr: ReadFile() and ReadConsoleA() return garbage when reading
4018 * non-ASCII characters from the console with the 65001 codepage.
4019 */
4020 HANDLE h = (HANDLE)_get_osfhandle(fd);
4021 size_t left_to_read = cnt;
4022 DWORD mode;
4023
4024 if (h == INVALID_HANDLE_VALUE) {
4025 errno = EBADF;
4026 return -1;
4027 }
4028
4029 if (!GetConsoleMode(h, &mode)) {
4030 translate_to_errno();
4031 return -1;
4032 }
4033
4034 while (left_to_read) {
4035 /* The purpose of converted_buf is to preserve partial UTF-8 (or of any
4036 * other multibyte encoding) code points between read() calls. Since
4037 * there's only one console, the buffer is global. It's needed because
4038 * ReadConsoleW() returns a string of UTF-16 code units and its result,
4039 * after conversion to the current console codepage, may not fit in the
4040 * return buffer.
4041 *
4042 * The buffer's size is 8 because it will contain at most two UTF-8 code
4043 * points.
4044 */
4045 static char converted_buf[8];
4046 static size_t converted_buf_len = 0;
4047 WCHAR wbuf[2];
4048 DWORD wbuf_len = 0, chars_read;
4049
4050 if (converted_buf_len) {
4051 bool newline = 0;
4052 size_t to_write = MIN(converted_buf_len, left_to_read);
4053
4054 /* Don't read anything if the *first* character is ^Z and
4055 * ENABLE_PROCESSED_INPUT is enabled. On some versions of Windows,
4056 * ReadFile() ignores ENABLE_PROCESSED_INPUT, but apparently it's a
4057 * bug: https://github.com/microsoft/terminal/issues/4958
4058 */
4059 if (left_to_read == cnt && (mode & ENABLE_PROCESSED_INPUT) &&
4060 converted_buf[0] == 0x1a)
4061 break;
4062
4063 /* Are we returning a newline? */
4064 if (memchr(converted_buf, '\n', to_write))
4065 newline = 1;
4066
4067 memcpy(buf, converted_buf, to_write);
4068 buf += to_write;
4069
4070 /* If there's anything left in converted_buf, move it to the
4071 * beginning of the buffer. */
4072 converted_buf_len -= to_write;
4073 if (converted_buf_len)
4074 memmove(
4075 converted_buf, converted_buf + to_write, converted_buf_len
4076 );
4077
4078 left_to_read -= to_write;
4079
4080 /* With ENABLE_LINE_INPUT enabled, we stop reading after the first
4081 * newline, otherwise we stop reading after the first character. */
4082 if (!left_to_read || newline || (mode & ENABLE_LINE_INPUT) == 0)
4083 break;
4084 }
4085
4086 /* Reading one code unit at a time is inefficient, but since this code
4087 * is used only for the interactive console, that shouldn't matter. */
4088 if (!ReadConsoleW(h, wbuf, 1, &chars_read, 0)) {
4089 translate_to_errno();
4090 return -1;
4091 }
4092 if (!chars_read)
4093 break;
4094
4095 ++wbuf_len;
4096
4097 if (wbuf[0] >= 0xD800 && wbuf[0] <= 0xDBFF) {
4098 /* High surrogate, read one more code unit. */
4099 if (!ReadConsoleW(h, wbuf + 1, 1, &chars_read, 0)) {
4100 translate_to_errno();
4101 return -1;
4102 }
4103 if (chars_read)
4104 ++wbuf_len;
4105 }
4106
4107 converted_buf_len = WideCharToMultiByte(
4108 GetConsoleCP(), 0, wbuf, wbuf_len, converted_buf,
4109 sizeof(converted_buf), NULL, NULL
4110 );
4111 if (!converted_buf_len) {
4112 translate_to_errno();
4113 return -1;
4114 }
4115 }
4116
4117 return cnt - left_to_read;
4118 }
4119
4120
4121 DllExport int
win32_read(int fd,void * buf,unsigned int cnt)4122 win32_read(int fd, void *buf, unsigned int cnt)
4123 {
4124 int ret;
4125 if (UNLIKELY(win32_isatty(fd) && GetConsoleCP() == 65001)) {
4126 MUTEX_LOCK(&win32_read_console_mutex);
4127 ret = win32_read_console(fd, (U8 *)buf, cnt);
4128 MUTEX_UNLOCK(&win32_read_console_mutex);
4129 }
4130 else
4131 ret = read(fd, buf, cnt);
4132
4133 return ret;
4134 }
4135
4136 DllExport int
win32_write(int fd,const void * buf,unsigned int cnt)4137 win32_write(int fd, const void *buf, unsigned int cnt)
4138 {
4139 return write(fd, buf, cnt);
4140 }
4141
4142 DllExport int
win32_mkdir(const char * dir,int mode)4143 win32_mkdir(const char *dir, int mode)
4144 {
4145 dTHX;
4146 return mkdir(PerlDir_mapA(dir)); /* just ignore mode */
4147 }
4148
4149 DllExport int
win32_rmdir(const char * dir)4150 win32_rmdir(const char *dir)
4151 {
4152 dTHX;
4153 return rmdir(PerlDir_mapA(dir));
4154 }
4155
4156 DllExport int
win32_chdir(const char * dir)4157 win32_chdir(const char *dir)
4158 {
4159 if (!dir || !*dir) {
4160 errno = ENOENT;
4161 return -1;
4162 }
4163 return chdir(dir);
4164 }
4165
4166 DllExport int
win32_access(const char * path,int mode)4167 win32_access(const char *path, int mode)
4168 {
4169 dTHX;
4170 return access(PerlDir_mapA(path), mode);
4171 }
4172
4173 DllExport int
win32_chmod(const char * path,int mode)4174 win32_chmod(const char *path, int mode)
4175 {
4176 dTHX;
4177 return chmod(PerlDir_mapA(path), mode);
4178 }
4179
4180
4181 static char *
create_command_line(char * cname,STRLEN clen,const char * const * args)4182 create_command_line(char *cname, STRLEN clen, const char * const *args)
4183 {
4184 PERL_DEB(dTHX;)
4185 int index, argc;
4186 char *cmd, *ptr;
4187 const char *arg;
4188 STRLEN len = 0;
4189 bool bat_file = FALSE;
4190 bool cmd_shell = FALSE;
4191 bool dumb_shell = FALSE;
4192 bool extra_quotes = FALSE;
4193 bool quote_next = FALSE;
4194
4195 if (!cname)
4196 cname = (char*)args[0];
4197
4198 /* The NT cmd.exe shell has the following peculiarity that needs to be
4199 * worked around. It strips a leading and trailing dquote when any
4200 * of the following is true:
4201 * 1. the /S switch was used
4202 * 2. there are more than two dquotes
4203 * 3. there is a special character from this set: &<>()@^|
4204 * 4. no whitespace characters within the two dquotes
4205 * 5. string between two dquotes isn't an executable file
4206 * To work around this, we always add a leading and trailing dquote
4207 * to the string, if the first argument is either "cmd.exe" or "cmd",
4208 * and there were at least two or more arguments passed to cmd.exe
4209 * (not including switches).
4210 * XXX the above rules (from "cmd /?") don't seem to be applied
4211 * always, making for the convolutions below :-(
4212 */
4213 if (cname) {
4214 if (!clen)
4215 clen = strlen(cname);
4216
4217 if (clen > 4
4218 && (stricmp(&cname[clen-4], ".bat") == 0
4219 || (stricmp(&cname[clen-4], ".cmd") == 0)))
4220 {
4221 bat_file = TRUE;
4222 len += 3;
4223 }
4224 else {
4225 char *exe = strrchr(cname, '/');
4226 char *exe2 = strrchr(cname, '\\');
4227 if (exe2 > exe)
4228 exe = exe2;
4229 if (exe)
4230 ++exe;
4231 else
4232 exe = cname;
4233 if (stricmp(exe, "cmd.exe") == 0 || stricmp(exe, "cmd") == 0) {
4234 cmd_shell = TRUE;
4235 len += 3;
4236 }
4237 else if (stricmp(exe, "command.com") == 0
4238 || stricmp(exe, "command") == 0)
4239 {
4240 dumb_shell = TRUE;
4241 }
4242 }
4243 }
4244
4245 DEBUG_p(PerlIO_printf(Perl_debug_log, "Args "));
4246 for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
4247 STRLEN curlen = strlen(arg);
4248 if (!(arg[0] == '"' && arg[curlen-1] == '"'))
4249 len += 2; /* assume quoting needed (worst case) */
4250 len += curlen + 1;
4251 DEBUG_p(PerlIO_printf(Perl_debug_log, "[%s]",arg));
4252 }
4253 DEBUG_p(PerlIO_printf(Perl_debug_log, "\n"));
4254
4255 argc = index;
4256 Newx(cmd, len, char);
4257 ptr = cmd;
4258
4259 if (bat_file) {
4260 *ptr++ = '"';
4261 extra_quotes = TRUE;
4262 }
4263
4264 for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
4265 bool do_quote = 0;
4266 STRLEN curlen = strlen(arg);
4267
4268 /* we want to protect empty arguments and ones with spaces with
4269 * dquotes, but only if they aren't already there */
4270 if (!dumb_shell) {
4271 if (!curlen) {
4272 do_quote = 1;
4273 }
4274 else if (quote_next) {
4275 /* see if it really is multiple arguments pretending to
4276 * be one and force a set of quotes around it */
4277 if (*find_next_space(arg))
4278 do_quote = 1;
4279 }
4280 else if (!(arg[0] == '"' && curlen > 1 && arg[curlen-1] == '"')) {
4281 STRLEN i = 0;
4282 while (i < curlen) {
4283 if (isSPACE(arg[i])) {
4284 do_quote = 1;
4285 }
4286 else if (arg[i] == '"') {
4287 do_quote = 0;
4288 break;
4289 }
4290 i++;
4291 }
4292 }
4293 }
4294
4295 if (do_quote)
4296 *ptr++ = '"';
4297
4298 strcpy(ptr, arg);
4299 ptr += curlen;
4300
4301 if (do_quote)
4302 *ptr++ = '"';
4303
4304 if (args[index+1])
4305 *ptr++ = ' ';
4306
4307 if (!extra_quotes
4308 && cmd_shell
4309 && curlen >= 2
4310 && *arg == '/' /* see if arg is "/c", "/x/c", "/x/d/c" etc. */
4311 && stricmp(arg+curlen-2, "/c") == 0)
4312 {
4313 /* is there a next argument? */
4314 if (args[index+1]) {
4315 /* are there two or more next arguments? */
4316 if (args[index+2]) {
4317 *ptr++ = '"';
4318 extra_quotes = TRUE;
4319 }
4320 else {
4321 /* single argument, force quoting if it has spaces */
4322 quote_next = TRUE;
4323 }
4324 }
4325 }
4326 }
4327
4328 if (extra_quotes)
4329 *ptr++ = '"';
4330
4331 *ptr = '\0';
4332
4333 return cmd;
4334 }
4335
4336 static const char *exe_extensions[] =
4337 {
4338 ".exe", /* this must be first */
4339 ".cmd",
4340 ".bat"
4341 };
4342
4343 static char *
qualified_path(const char * cmd,bool other_exts)4344 qualified_path(const char *cmd, bool other_exts)
4345 {
4346 char *pathstr;
4347 char *fullcmd, *curfullcmd;
4348 STRLEN cmdlen = 0;
4349 int has_slash = 0;
4350
4351 if (!cmd)
4352 return NULL;
4353 fullcmd = (char*)cmd;
4354 while (*fullcmd) {
4355 if (*fullcmd == '/' || *fullcmd == '\\')
4356 has_slash++;
4357 fullcmd++;
4358 cmdlen++;
4359 }
4360
4361 /* look in PATH */
4362 {
4363 dTHX;
4364 pathstr = PerlEnv_getenv("PATH");
4365 }
4366 /* worst case: PATH is a single directory; we need additional space
4367 * to append "/", ".exe" and trailing "\0" */
4368 Newx(fullcmd, (pathstr ? strlen(pathstr) : 0) + cmdlen + 6, char);
4369 curfullcmd = fullcmd;
4370
4371 while (1) {
4372 DWORD res;
4373
4374 /* start by appending the name to the current prefix */
4375 strcpy(curfullcmd, cmd);
4376 curfullcmd += cmdlen;
4377
4378 /* if it doesn't end with '.', or has no extension, try adding
4379 * a trailing .exe first */
4380 if (cmd[cmdlen-1] != '.'
4381 && (cmdlen < 4 || cmd[cmdlen-4] != '.'))
4382 {
4383 int i;
4384 /* first extension is .exe */
4385 int ext_limit = other_exts ? C_ARRAY_LENGTH(exe_extensions) : 1;
4386 for (i = 0; i < ext_limit; ++i) {
4387 strcpy(curfullcmd, exe_extensions[i]);
4388 res = GetFileAttributes(fullcmd);
4389 if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
4390 return fullcmd;
4391 }
4392
4393 *curfullcmd = '\0';
4394 }
4395
4396 /* that failed, try the bare name */
4397 res = GetFileAttributes(fullcmd);
4398 if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
4399 return fullcmd;
4400
4401 /* quit if no other path exists, or if cmd already has path */
4402 if (!pathstr || !*pathstr || has_slash)
4403 break;
4404
4405 /* skip leading semis */
4406 while (*pathstr == ';')
4407 pathstr++;
4408
4409 /* build a new prefix from scratch */
4410 curfullcmd = fullcmd;
4411 while (*pathstr && *pathstr != ';') {
4412 if (*pathstr == '"') { /* foo;"baz;etc";bar */
4413 pathstr++; /* skip initial '"' */
4414 while (*pathstr && *pathstr != '"') {
4415 *curfullcmd++ = *pathstr++;
4416 }
4417 if (*pathstr)
4418 pathstr++; /* skip trailing '"' */
4419 }
4420 else {
4421 *curfullcmd++ = *pathstr++;
4422 }
4423 }
4424 if (*pathstr)
4425 pathstr++; /* skip trailing semi */
4426 if (curfullcmd > fullcmd /* append a dir separator */
4427 && curfullcmd[-1] != '/' && curfullcmd[-1] != '\\')
4428 {
4429 *curfullcmd++ = '\\';
4430 }
4431 }
4432
4433 Safefree(fullcmd);
4434 return NULL;
4435 }
4436
4437 /* The following are just place holders.
4438 * Some hosts may provide and environment that the OS is
4439 * not tracking, therefore, these host must provide that
4440 * environment and the current directory to CreateProcess
4441 */
4442
4443 DllExport void*
win32_get_childenv(void)4444 win32_get_childenv(void)
4445 {
4446 return NULL;
4447 }
4448
4449 DllExport void
win32_free_childenv(void * d)4450 win32_free_childenv(void* d)
4451 {
4452 }
4453
4454 DllExport void
win32_clearenv(void)4455 win32_clearenv(void)
4456 {
4457 char *envv = GetEnvironmentStrings();
4458 char *cur = envv;
4459 STRLEN len;
4460 while (*cur) {
4461 char *end = strchr(cur,'=');
4462 if (end && end != cur) {
4463 *end = '\0';
4464 SetEnvironmentVariable(cur, NULL);
4465 *end = '=';
4466 cur = end + strlen(end+1)+2;
4467 }
4468 else if ((len = strlen(cur)))
4469 cur += len+1;
4470 }
4471 FreeEnvironmentStrings(envv);
4472 }
4473
4474 DllExport char*
win32_get_childdir(void)4475 win32_get_childdir(void)
4476 {
4477 char* ptr;
4478 char szfilename[MAX_PATH+1];
4479
4480 GetCurrentDirectoryA(MAX_PATH+1, szfilename);
4481 Newx(ptr, strlen(szfilename)+1, char);
4482 strcpy(ptr, szfilename);
4483 return ptr;
4484 }
4485
4486 DllExport void
win32_free_childdir(char * d)4487 win32_free_childdir(char* d)
4488 {
4489 Safefree(d);
4490 }
4491
4492
4493 /* XXX this needs to be made more compatible with the spawnvp()
4494 * provided by the various RTLs. In particular, searching for
4495 * *.{com,bat,cmd} files (as done by the RTLs) is unimplemented.
4496 * This doesn't significantly affect perl itself, because we
4497 * always invoke things using PERL5SHELL if a direct attempt to
4498 * spawn the executable fails.
4499 *
4500 * XXX splitting and rejoining the commandline between do_aspawn()
4501 * and win32_spawnvp() could also be avoided.
4502 */
4503
4504 DllExport int
win32_spawnvp(int mode,const char * cmdname,const char * const * argv)4505 win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
4506 {
4507 #ifdef USE_RTL_SPAWNVP
4508 return _spawnvp(mode, cmdname, (char * const *)argv);
4509 #else
4510 return do_spawnvp_handles(mode, cmdname, argv, NULL);
4511 #endif
4512 }
4513
4514 static int
do_spawnvp_handles(int mode,const char * cmdname,const char * const * argv,const int * handles)4515 do_spawnvp_handles(int mode, const char *cmdname, const char *const *argv,
4516 const int *handles) {
4517 dTHXa(NULL);
4518 int ret;
4519 void* env;
4520 char* dir;
4521 child_IO_table tbl;
4522 STARTUPINFO StartupInfo;
4523 PROCESS_INFORMATION ProcessInformation;
4524 DWORD create = 0;
4525 char *cmd;
4526 char *fullcmd = NULL;
4527 char *cname = (char *)cmdname;
4528 STRLEN clen = 0;
4529
4530 if (cname) {
4531 clen = strlen(cname);
4532 /* if command name contains dquotes, must remove them */
4533 if (strchr(cname, '"')) {
4534 cmd = cname;
4535 Newx(cname,clen+1,char);
4536 clen = 0;
4537 while (*cmd) {
4538 if (*cmd != '"') {
4539 cname[clen] = *cmd;
4540 ++clen;
4541 }
4542 ++cmd;
4543 }
4544 cname[clen] = '\0';
4545 }
4546 }
4547
4548 cmd = create_command_line(cname, clen, argv);
4549
4550 aTHXa(PERL_GET_THX);
4551 env = PerlEnv_get_childenv();
4552 dir = PerlEnv_get_childdir();
4553
4554 switch(mode) {
4555 case P_NOWAIT: /* asynch + remember result */
4556 if (w32_num_children >= MAXIMUM_WAIT_OBJECTS) {
4557 errno = EAGAIN;
4558 ret = -1;
4559 goto RETVAL;
4560 }
4561 /* Create a new process group so we can use GenerateConsoleCtrlEvent()
4562 * in win32_kill()
4563 */
4564 create |= CREATE_NEW_PROCESS_GROUP;
4565 /* FALL THROUGH */
4566
4567 case P_WAIT: /* synchronous execution */
4568 break;
4569 default: /* invalid mode */
4570 errno = EINVAL;
4571 ret = -1;
4572 goto RETVAL;
4573 }
4574
4575 memset(&StartupInfo,0,sizeof(StartupInfo));
4576 StartupInfo.cb = sizeof(StartupInfo);
4577 memset(&tbl,0,sizeof(tbl));
4578 PerlEnv_get_child_IO(&tbl);
4579 StartupInfo.dwFlags = tbl.dwFlags;
4580 StartupInfo.dwX = tbl.dwX;
4581 StartupInfo.dwY = tbl.dwY;
4582 StartupInfo.dwXSize = tbl.dwXSize;
4583 StartupInfo.dwYSize = tbl.dwYSize;
4584 StartupInfo.dwXCountChars = tbl.dwXCountChars;
4585 StartupInfo.dwYCountChars = tbl.dwYCountChars;
4586 StartupInfo.dwFillAttribute = tbl.dwFillAttribute;
4587 StartupInfo.wShowWindow = tbl.wShowWindow;
4588 StartupInfo.hStdInput = handles && handles[0] != -1 ?
4589 (HANDLE)_get_osfhandle(handles[0]) : tbl.childStdIn;
4590 StartupInfo.hStdOutput = handles && handles[1] != -1 ?
4591 (HANDLE)_get_osfhandle(handles[1]) : tbl.childStdOut;
4592 StartupInfo.hStdError = handles && handles[2] != -1 ?
4593 (HANDLE)_get_osfhandle(handles[2]) : tbl.childStdErr;
4594 if (StartupInfo.hStdInput == INVALID_HANDLE_VALUE &&
4595 StartupInfo.hStdOutput == INVALID_HANDLE_VALUE &&
4596 StartupInfo.hStdError == INVALID_HANDLE_VALUE)
4597 {
4598 create |= CREATE_NEW_CONSOLE;
4599 }
4600 else {
4601 StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
4602 }
4603 if (w32_use_showwindow) {
4604 StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
4605 StartupInfo.wShowWindow = w32_showwindow;
4606 }
4607
4608 DEBUG_p(PerlIO_printf(Perl_debug_log, "Spawning [%s] with [%s]\n",
4609 cname,cmd));
4610 RETRY:
4611 if (!CreateProcess(cname, /* search PATH to find executable */
4612 cmd, /* executable, and its arguments */
4613 NULL, /* process attributes */
4614 NULL, /* thread attributes */
4615 TRUE, /* inherit handles */
4616 create, /* creation flags */
4617 (LPVOID)env, /* inherit environment */
4618 dir, /* inherit cwd */
4619 &StartupInfo,
4620 &ProcessInformation))
4621 {
4622 /* initial NULL argument to CreateProcess() does a PATH
4623 * search, but it always first looks in the directory
4624 * where the current process was started, which behavior
4625 * is undesirable for backward compatibility. So we
4626 * jump through our own hoops by picking out the path
4627 * we really want it to use. */
4628 if (!fullcmd) {
4629 fullcmd = qualified_path(cname, FALSE);
4630 if (fullcmd) {
4631 if (cname != cmdname)
4632 Safefree(cname);
4633 cname = fullcmd;
4634 DEBUG_p(PerlIO_printf(Perl_debug_log,
4635 "Retrying [%s] with same args\n",
4636 cname));
4637 goto RETRY;
4638 }
4639 }
4640 errno = ENOENT;
4641 ret = -1;
4642 goto RETVAL;
4643 }
4644
4645 if (mode == P_NOWAIT) {
4646 /* asynchronous spawn -- store handle, return PID */
4647 ret = (int)ProcessInformation.dwProcessId;
4648
4649 w32_child_handles[w32_num_children] = ProcessInformation.hProcess;
4650 w32_child_pids[w32_num_children] = (DWORD)ret;
4651 ++w32_num_children;
4652 }
4653 else {
4654 DWORD status;
4655 win32_msgwait(aTHX_ 1, &ProcessInformation.hProcess, INFINITE, NULL);
4656 /* FIXME: if msgwait returned due to message perhaps forward the
4657 "signal" to the process
4658 */
4659 GetExitCodeProcess(ProcessInformation.hProcess, &status);
4660 ret = (int)status;
4661 CloseHandle(ProcessInformation.hProcess);
4662 }
4663
4664 CloseHandle(ProcessInformation.hThread);
4665
4666 RETVAL:
4667 PerlEnv_free_childenv(env);
4668 PerlEnv_free_childdir(dir);
4669 Safefree(cmd);
4670 if (cname != cmdname)
4671 Safefree(cname);
4672 return ret;
4673 }
4674
4675 DllExport int
win32_execv(const char * cmdname,const char * const * argv)4676 win32_execv(const char *cmdname, const char *const *argv)
4677 {
4678 #ifdef USE_ITHREADS
4679 dTHX;
4680 /* if this is a pseudo-forked child, we just want to spawn
4681 * the new program, and return */
4682 if (w32_pseudo_id)
4683 return _spawnv(P_WAIT, cmdname, argv);
4684 #endif
4685 return _execv(cmdname, argv);
4686 }
4687
4688 DllExport int
win32_execvp(const char * cmdname,const char * const * argv)4689 win32_execvp(const char *cmdname, const char *const *argv)
4690 {
4691 #ifdef USE_ITHREADS
4692 dTHX;
4693 /* if this is a pseudo-forked child, we just want to spawn
4694 * the new program, and return */
4695 if (w32_pseudo_id) {
4696 int status = win32_spawnvp(P_WAIT, cmdname, (const char *const *)argv);
4697 if (status != -1) {
4698 my_exit(status);
4699 return 0;
4700 }
4701 else
4702 return status;
4703 }
4704 #endif
4705 return _execvp(cmdname, argv);
4706 }
4707
4708 DllExport void
win32_perror(const char * str)4709 win32_perror(const char *str)
4710 {
4711 perror(str);
4712 }
4713
4714 DllExport void
win32_setbuf(FILE * pf,char * buf)4715 win32_setbuf(FILE *pf, char *buf)
4716 {
4717 setbuf(pf, buf);
4718 }
4719
4720 DllExport int
win32_setvbuf(FILE * pf,char * buf,int type,size_t size)4721 win32_setvbuf(FILE *pf, char *buf, int type, size_t size)
4722 {
4723 return setvbuf(pf, buf, type, size);
4724 }
4725
4726 DllExport int
win32_flushall(void)4727 win32_flushall(void)
4728 {
4729 return flushall();
4730 }
4731
4732 DllExport int
win32_fcloseall(void)4733 win32_fcloseall(void)
4734 {
4735 return fcloseall();
4736 }
4737
4738 DllExport char*
win32_fgets(char * s,int n,FILE * pf)4739 win32_fgets(char *s, int n, FILE *pf)
4740 {
4741 return fgets(s, n, pf);
4742 }
4743
4744 DllExport char*
win32_gets(char * s)4745 win32_gets(char *s)
4746 {
4747 return gets(s);
4748 }
4749
4750 DllExport int
win32_fgetc(FILE * pf)4751 win32_fgetc(FILE *pf)
4752 {
4753 return fgetc(pf);
4754 }
4755
4756 DllExport int
win32_putc(int c,FILE * pf)4757 win32_putc(int c, FILE *pf)
4758 {
4759 return putc(c,pf);
4760 }
4761
4762 DllExport int
win32_puts(const char * s)4763 win32_puts(const char *s)
4764 {
4765 return puts(s);
4766 }
4767
4768 DllExport int
win32_getchar(void)4769 win32_getchar(void)
4770 {
4771 return getchar();
4772 }
4773
4774 DllExport int
win32_putchar(int c)4775 win32_putchar(int c)
4776 {
4777 return putchar(c);
4778 }
4779
4780 #ifdef MYMALLOC
4781
4782 #ifndef USE_PERL_SBRK
4783
4784 static char *committed = NULL; /* XXX threadead */
4785 static char *base = NULL; /* XXX threadead */
4786 static char *reserved = NULL; /* XXX threadead */
4787 static char *brk = NULL; /* XXX threadead */
4788 static DWORD pagesize = 0; /* XXX threadead */
4789
4790 void *
sbrk(ptrdiff_t need)4791 sbrk(ptrdiff_t need)
4792 {
4793 void *result;
4794 if (!pagesize)
4795 {SYSTEM_INFO info;
4796 GetSystemInfo(&info);
4797 /* Pretend page size is larger so we don't perpetually
4798 * call the OS to commit just one page ...
4799 */
4800 pagesize = info.dwPageSize << 3;
4801 }
4802 if (brk+need >= reserved)
4803 {
4804 DWORD size = brk+need-reserved;
4805 char *addr;
4806 char *prev_committed = NULL;
4807 if (committed && reserved && committed < reserved)
4808 {
4809 /* Commit last of previous chunk cannot span allocations */
4810 addr = (char *) VirtualAlloc(committed,reserved-committed,MEM_COMMIT,PAGE_READWRITE);
4811 if (addr)
4812 {
4813 /* Remember where we committed from in case we want to decommit later */
4814 prev_committed = committed;
4815 committed = reserved;
4816 }
4817 }
4818 /* Reserve some (more) space
4819 * Contiguous blocks give us greater efficiency, so reserve big blocks -
4820 * this is only address space not memory...
4821 * Note this is a little sneaky, 1st call passes NULL as reserved
4822 * so lets system choose where we start, subsequent calls pass
4823 * the old end address so ask for a contiguous block
4824 */
4825 sbrk_reserve:
4826 if (size < 64*1024*1024)
4827 size = 64*1024*1024;
4828 size = ((size + pagesize - 1) / pagesize) * pagesize;
4829 addr = (char *) VirtualAlloc(reserved,size,MEM_RESERVE,PAGE_NOACCESS);
4830 if (addr)
4831 {
4832 reserved = addr+size;
4833 if (!base)
4834 base = addr;
4835 if (!committed)
4836 committed = base;
4837 if (!brk)
4838 brk = committed;
4839 }
4840 else if (reserved)
4841 {
4842 /* The existing block could not be extended far enough, so decommit
4843 * anything that was just committed above and start anew */
4844 if (prev_committed)
4845 {
4846 if (!VirtualFree(prev_committed,reserved-prev_committed,MEM_DECOMMIT))
4847 return (void *) -1;
4848 }
4849 reserved = base = committed = brk = NULL;
4850 size = need;
4851 goto sbrk_reserve;
4852 }
4853 else
4854 {
4855 return (void *) -1;
4856 }
4857 }
4858 result = brk;
4859 brk += need;
4860 if (brk > committed)
4861 {
4862 DWORD size = ((brk-committed + pagesize -1)/pagesize) * pagesize;
4863 char *addr;
4864 if (committed+size > reserved)
4865 size = reserved-committed;
4866 addr = (char *) VirtualAlloc(committed,size,MEM_COMMIT,PAGE_READWRITE);
4867 if (addr)
4868 committed += size;
4869 else
4870 return (void *) -1;
4871 }
4872 return result;
4873 }
4874
4875 #endif
4876 #endif
4877
4878 DllExport void*
win32_malloc(size_t size)4879 win32_malloc(size_t size)
4880 {
4881 return malloc(size);
4882 }
4883
4884 DllExport void*
win32_calloc(size_t numitems,size_t size)4885 win32_calloc(size_t numitems, size_t size)
4886 {
4887 return calloc(numitems,size);
4888 }
4889
4890 DllExport void*
win32_realloc(void * block,size_t size)4891 win32_realloc(void *block, size_t size)
4892 {
4893 return realloc(block,size);
4894 }
4895
4896 DllExport void
win32_free(void * block)4897 win32_free(void *block)
4898 {
4899 free(block);
4900 }
4901
4902
4903 DllExport int
win32_open_osfhandle(intptr_t handle,int flags)4904 win32_open_osfhandle(intptr_t handle, int flags)
4905 {
4906 return _open_osfhandle(handle, flags);
4907 }
4908
4909 DllExport intptr_t
win32_get_osfhandle(int fd)4910 win32_get_osfhandle(int fd)
4911 {
4912 return (intptr_t)_get_osfhandle(fd);
4913 }
4914
4915 DllExport FILE *
win32_fdupopen(FILE * pf)4916 win32_fdupopen(FILE *pf)
4917 {
4918 FILE* pfdup;
4919 fpos_t pos;
4920 char mode[3];
4921 int fileno = win32_dup(win32_fileno(pf));
4922
4923 /* open the file in the same mode */
4924 if (PERLIO_FILE_flag(pf) & PERLIO_FILE_flag_RD) {
4925 mode[0] = 'r';
4926 mode[1] = 0;
4927 }
4928 else if (PERLIO_FILE_flag(pf) & PERLIO_FILE_flag_WR) {
4929 mode[0] = 'a';
4930 mode[1] = 0;
4931 }
4932 else if (PERLIO_FILE_flag(pf) & PERLIO_FILE_flag_RW) {
4933 mode[0] = 'r';
4934 mode[1] = '+';
4935 mode[2] = 0;
4936 }
4937
4938 /* it appears that the binmode is attached to the
4939 * file descriptor so binmode files will be handled
4940 * correctly
4941 */
4942 pfdup = win32_fdopen(fileno, mode);
4943
4944 /* move the file pointer to the same position */
4945 if (!fgetpos(pf, &pos)) {
4946 fsetpos(pfdup, &pos);
4947 }
4948 return pfdup;
4949 }
4950
4951 DllExport void*
win32_dynaload(const char * filename)4952 win32_dynaload(const char* filename)
4953 {
4954 dTHXa(NULL);
4955 char buf[MAX_PATH+1];
4956 const char *first;
4957
4958 /* LoadLibrary() doesn't recognize forward slashes correctly,
4959 * so turn 'em back. */
4960 first = strchr(filename, '/');
4961 if (first) {
4962 STRLEN len = strlen(filename);
4963 if (len <= MAX_PATH) {
4964 strcpy(buf, filename);
4965 filename = &buf[first - filename];
4966 while (*filename) {
4967 if (*filename == '/')
4968 *(char*)filename = '\\';
4969 ++filename;
4970 }
4971 filename = buf;
4972 }
4973 }
4974 aTHXa(PERL_GET_THX);
4975 return LoadLibraryExA(PerlDir_mapA(filename), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
4976 }
4977
XS(w32_SetChildShowWindow)4978 XS(w32_SetChildShowWindow)
4979 {
4980 dXSARGS;
4981 BOOL use_showwindow = w32_use_showwindow;
4982 /* use "unsigned short" because Perl has redefined "WORD" */
4983 unsigned short showwindow = w32_showwindow;
4984
4985 if (items > 1)
4986 croak_xs_usage(cv, "[showwindow]");
4987
4988 if (items == 0 || !SvOK(ST(0)))
4989 w32_use_showwindow = FALSE;
4990 else {
4991 w32_use_showwindow = TRUE;
4992 w32_showwindow = (unsigned short)SvIV(ST(0));
4993 }
4994
4995 EXTEND(SP, 1);
4996 if (use_showwindow)
4997 ST(0) = sv_2mortal(newSViv(showwindow));
4998 else
4999 ST(0) = &PL_sv_undef;
5000 XSRETURN(1);
5001 }
5002
5003
5004 #ifdef PERL_IS_MINIPERL
5005 /* shelling out is much slower, full perl uses Win32.pm */
XS(w32_GetCwd)5006 XS(w32_GetCwd)
5007 {
5008 dXSARGS;
5009 /* Make the host for current directory */
5010 char* ptr = PerlEnv_get_childdir();
5011 /*
5012 * If ptr != Nullch
5013 * then it worked, set PV valid,
5014 * else return 'undef'
5015 */
5016 if (ptr) {
5017 SV *sv = sv_newmortal();
5018 sv_setpv(sv, ptr);
5019 PerlEnv_free_childdir(ptr);
5020
5021 #ifndef INCOMPLETE_TAINTS
5022 SvTAINTED_on(sv);
5023 #endif
5024
5025 ST(0) = sv;
5026 XSRETURN(1);
5027 }
5028 XSRETURN_UNDEF;
5029 }
5030 #endif
5031
5032 void
Perl_init_os_extras(void)5033 Perl_init_os_extras(void)
5034 {
5035 dTHXa(NULL);
5036 const char *file = __FILE__;
5037
5038 /* Initialize Win32CORE if it has been statically linked. */
5039 #ifndef PERL_IS_MINIPERL
5040 void (*pfn_init)(pTHX);
5041 HMODULE module = (HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE)
5042 ? GetModuleHandle(NULL)
5043 : w32_perldll_handle);
5044 pfn_init = (void (*)(pTHX))GetProcAddress(module, "init_Win32CORE");
5045 aTHXa(PERL_GET_THX);
5046 if (pfn_init)
5047 pfn_init(aTHX);
5048 #else
5049 aTHXa(PERL_GET_THX);
5050 #endif
5051
5052 newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file);
5053 #ifdef PERL_IS_MINIPERL
5054 newXS("Win32::GetCwd", w32_GetCwd, file);
5055 #endif
5056 }
5057
5058 void *
win32_signal_context(void)5059 win32_signal_context(void)
5060 {
5061 dTHX;
5062 #ifdef MULTIPLICITY
5063 if (!my_perl) {
5064 my_perl = PL_curinterp;
5065 PERL_SET_THX(my_perl);
5066 }
5067 return my_perl;
5068 #else
5069 return PL_curinterp;
5070 #endif
5071 }
5072
5073
5074 BOOL WINAPI
win32_ctrlhandler(DWORD dwCtrlType)5075 win32_ctrlhandler(DWORD dwCtrlType)
5076 {
5077 #ifdef MULTIPLICITY
5078 dTHXa(PERL_GET_SIG_CONTEXT);
5079
5080 if (!my_perl)
5081 return FALSE;
5082 #endif
5083
5084 switch(dwCtrlType) {
5085 case CTRL_CLOSE_EVENT:
5086 /* A signal that the system sends to all processes attached to a console when
5087 the user closes the console (either by choosing the Close command from the
5088 console window's System menu, or by choosing the End Task command from the
5089 Task List
5090 */
5091 if (do_raise(aTHX_ 1)) /* SIGHUP */
5092 sig_terminate(aTHX_ 1);
5093 return TRUE;
5094
5095 case CTRL_C_EVENT:
5096 /* A CTRL+c signal was received */
5097 if (do_raise(aTHX_ SIGINT))
5098 sig_terminate(aTHX_ SIGINT);
5099 return TRUE;
5100
5101 case CTRL_BREAK_EVENT:
5102 /* A CTRL+BREAK signal was received */
5103 if (do_raise(aTHX_ SIGBREAK))
5104 sig_terminate(aTHX_ SIGBREAK);
5105 return TRUE;
5106
5107 case CTRL_LOGOFF_EVENT:
5108 /* A signal that the system sends to all console processes when a user is logging
5109 off. This signal does not indicate which user is logging off, so no
5110 assumptions can be made.
5111 */
5112 break;
5113 case CTRL_SHUTDOWN_EVENT:
5114 /* A signal that the system sends to all console processes when the system is
5115 shutting down.
5116 */
5117 if (do_raise(aTHX_ SIGTERM))
5118 sig_terminate(aTHX_ SIGTERM);
5119 return TRUE;
5120 default:
5121 break;
5122 }
5123 return FALSE;
5124 }
5125
5126
5127 #ifdef SET_INVALID_PARAMETER_HANDLER
5128 # include <crtdbg.h>
5129 #endif
5130
5131 static void
ansify_path(void)5132 ansify_path(void)
5133 {
5134 size_t len;
5135 char *ansi_path;
5136 WCHAR *wide_path;
5137 WCHAR *wide_dir;
5138
5139 /* fetch Unicode version of PATH */
5140 len = 2000;
5141 wide_path = (WCHAR*)win32_malloc(len*sizeof(WCHAR));
5142 while (wide_path) {
5143 size_t newlen = GetEnvironmentVariableW(L"PATH", wide_path, len);
5144 if (newlen == 0) {
5145 win32_free(wide_path);
5146 return;
5147 }
5148 if (newlen < len)
5149 break;
5150 len = newlen;
5151 wide_path = (WCHAR*)win32_realloc(wide_path, len*sizeof(WCHAR));
5152 }
5153 if (!wide_path)
5154 return;
5155
5156 /* convert to ANSI pathnames */
5157 wide_dir = wide_path;
5158 ansi_path = NULL;
5159 while (wide_dir) {
5160 WCHAR *sep = wcschr(wide_dir, ';');
5161 char *ansi_dir;
5162 size_t ansi_len;
5163 size_t wide_len;
5164
5165 if (sep)
5166 *sep++ = '\0';
5167
5168 /* remove quotes around pathname */
5169 if (*wide_dir == '"')
5170 ++wide_dir;
5171 wide_len = wcslen(wide_dir);
5172 if (wide_len && wide_dir[wide_len-1] == '"')
5173 wide_dir[wide_len-1] = '\0';
5174
5175 /* append ansi_dir to ansi_path */
5176 ansi_dir = win32_ansipath(wide_dir);
5177 ansi_len = strlen(ansi_dir);
5178 if (ansi_path) {
5179 size_t newlen = len + 1 + ansi_len;
5180 ansi_path = (char*)win32_realloc(ansi_path, newlen+1);
5181 if (!ansi_path)
5182 break;
5183 ansi_path[len] = ';';
5184 memcpy(ansi_path+len+1, ansi_dir, ansi_len+1);
5185 len = newlen;
5186 }
5187 else {
5188 len = ansi_len;
5189 ansi_path = (char*)win32_malloc(5+len+1);
5190 if (!ansi_path)
5191 break;
5192 memcpy(ansi_path, "PATH=", 5);
5193 memcpy(ansi_path+5, ansi_dir, len+1);
5194 len += 5;
5195 }
5196 win32_free(ansi_dir);
5197 wide_dir = sep;
5198 }
5199
5200 if (ansi_path) {
5201 /* Update C RTL environ array. This will only have full effect if
5202 * perl_parse() is later called with `environ` as the `env` argument.
5203 * Otherwise S_init_postdump_symbols() will overwrite PATH again.
5204 *
5205 * We do have to ansify() the PATH before Perl has been fully
5206 * initialized because S_find_script() uses the PATH when perl
5207 * is being invoked with the -S option. This happens before %ENV
5208 * is initialized in S_init_postdump_symbols().
5209 *
5210 * XXX Is this a bug? Should S_find_script() use the environment
5211 * XXX passed in the `env` arg to parse_perl()?
5212 */
5213 putenv(ansi_path);
5214 /* Keep system environment in sync because S_init_postdump_symbols()
5215 * will not call mg_set() if it initializes %ENV from `environ`.
5216 */
5217 SetEnvironmentVariableA("PATH", ansi_path+5);
5218 win32_free(ansi_path);
5219 }
5220 win32_free(wide_path);
5221 }
5222
5223 /* This hooks a function that is imported by the specified module. The hook is
5224 * local to that module. */
5225 static bool
win32_hook_imported_function_in_module(HMODULE module,LPCSTR fun_name,FARPROC hook_ptr)5226 win32_hook_imported_function_in_module(
5227 HMODULE module, LPCSTR fun_name, FARPROC hook_ptr
5228 )
5229 {
5230 ULONG_PTR image_base = (ULONG_PTR)module;
5231 PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)image_base;
5232 PIMAGE_NT_HEADERS nt_headers
5233 = (PIMAGE_NT_HEADERS)(image_base + dos_header->e_lfanew);
5234 PIMAGE_OPTIONAL_HEADER opt_header = &nt_headers->OptionalHeader;
5235
5236 PIMAGE_DATA_DIRECTORY data_dir = opt_header->DataDirectory;
5237 DWORD data_dir_len = opt_header->NumberOfRvaAndSizes;
5238
5239 BOOL is_idt_present = data_dir_len > IMAGE_DIRECTORY_ENTRY_IMPORT
5240 && data_dir[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress != 0;
5241
5242 if (!is_idt_present)
5243 return FALSE;
5244
5245 BOOL found = FALSE;
5246
5247 /* Import Directory Table */
5248 PIMAGE_IMPORT_DESCRIPTOR idt = (PIMAGE_IMPORT_DESCRIPTOR)(
5249 image_base + data_dir[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
5250 );
5251
5252 for (; idt->Name != 0; ++idt) {
5253 /* Import Lookup Table */
5254 PIMAGE_THUNK_DATA ilt
5255 = (PIMAGE_THUNK_DATA)(image_base + idt->OriginalFirstThunk);
5256 /* Import Address Table */
5257 PIMAGE_THUNK_DATA iat
5258 = (PIMAGE_THUNK_DATA)(image_base + idt->FirstThunk);
5259
5260 ULONG_PTR address_of_data;
5261 for (; address_of_data = ilt->u1.AddressOfData; ++ilt, ++iat) {
5262 /* Ordinal imports are quite rare, so skipping them will most likely
5263 * not cause any problems. */
5264 BOOL is_ordinal
5265 = address_of_data >> ((sizeof(address_of_data) * 8) - 1);
5266
5267 if (is_ordinal)
5268 continue;
5269
5270 LPCSTR name = (
5271 (PIMAGE_IMPORT_BY_NAME)(image_base + address_of_data)
5272 )->Name;
5273
5274 if (strEQ(name, fun_name)) {
5275 DWORD old_protect = 0;
5276 BOOL succ = VirtualProtect(
5277 &iat->u1.Function, sizeof(iat->u1.Function), PAGE_READWRITE,
5278 &old_protect
5279 );
5280 if (!succ)
5281 return FALSE;
5282
5283 iat->u1.Function = (ULONG_PTR)hook_ptr;
5284 found = TRUE;
5285
5286 VirtualProtect(
5287 &iat->u1.Function, sizeof(iat->u1.Function), old_protect,
5288 &old_protect
5289 );
5290 break;
5291 }
5292 }
5293 }
5294
5295 return found;
5296 }
5297
5298 typedef NTSTATUS (NTAPI *pNtQueryInformationFile_t)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, ULONG);
5299 pNtQueryInformationFile_t pNtQueryInformationFile = NULL;
5300
5301 typedef BOOL (WINAPI *pCloseHandle)(HANDLE h);
5302 static pCloseHandle CloseHandle_orig;
5303
5304 /* CloseHandle() that supports sockets. CRT uses mutexes during file operations,
5305 * so the lack of thread safety in this function isn't a problem. */
5306 static BOOL WINAPI
my_CloseHandle(HANDLE h)5307 my_CloseHandle(HANDLE h)
5308 {
5309 /* In theory, passing a non-socket handle to closesocket() is fine. It
5310 * should return a WSAENOTSOCK error, which is easy to recover from.
5311 * However, we should avoid doing that because it's not that simple in
5312 * practice. For instance, it can deadlock on a handle to a stuck pipe (see:
5313 * https://github.com/Perl/perl5/issues/19963).
5314 *
5315 * There's no foolproof way to tell if a handle is a socket (mostly because
5316 * of the non-IFS sockets), but in some cases we can tell if a handle
5317 * is definitely *not* a socket.
5318 */
5319
5320 /* GetFileType() always returns FILE_TYPE_PIPE for sockets. */
5321 BOOL maybe_socket = (GetFileType(h) == FILE_TYPE_PIPE);
5322
5323 if (maybe_socket && pNtQueryInformationFile) {
5324 IO_STATUS_BLOCK isb;
5325 struct {
5326 ULONG name_len;
5327 WCHAR name[100];
5328 } volume = {0};
5329
5330 /* There are many ways to tell a named pipe from a socket, but almost
5331 * all of them can deadlock on a handle to a stuck pipe (like in the
5332 * bug ticket mentioned above). According to my tests,
5333 * FileVolumeNameInfomation is the only relevant function that doesn't
5334 * suffer from this problem.
5335 *
5336 * It's undocumented and it requires Windows 10, so on older systems
5337 * we always pass pipes to closesocket().
5338 */
5339 NTSTATUS s = pNtQueryInformationFile(
5340 h, &isb, &volume, sizeof(volume), 58 /* FileVolumeNameInformation */
5341 );
5342 if (NT_SUCCESS(s)) {
5343 maybe_socket = (_wcsnicmp(
5344 volume.name, L"\\Device\\NamedPipe", C_ARRAY_LENGTH(volume.name)
5345 ) != 0);
5346 }
5347 }
5348
5349 if (maybe_socket)
5350 if (closesocket((SOCKET)h) == 0)
5351 return TRUE;
5352 else if (WSAGetLastError() != WSAENOTSOCK)
5353 return FALSE;
5354
5355 return CloseHandle_orig(h);
5356 }
5357
5358 /* Hook CloseHandle() inside CRT so its functions like _close() or
5359 * _dup2() can close sockets properly. */
5360 static void
win32_hook_closehandle_in_crt()5361 win32_hook_closehandle_in_crt()
5362 {
5363 /* Get the handle to the CRT module basing on the address of _close()
5364 * function. */
5365 HMODULE crt_handle;
5366 BOOL succ = GetModuleHandleExA(
5367 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
5368 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)_close,
5369 &crt_handle
5370 );
5371 if (!succ)
5372 return;
5373
5374 CloseHandle_orig = (pCloseHandle)GetProcAddress(
5375 GetModuleHandleA("kernel32.dll"), "CloseHandle"
5376 );
5377 if (!CloseHandle_orig)
5378 return;
5379
5380 win32_hook_imported_function_in_module(
5381 crt_handle, "CloseHandle", (FARPROC)my_CloseHandle
5382 );
5383
5384 pNtQueryInformationFile = (pNtQueryInformationFile_t)GetProcAddress(
5385 GetModuleHandleA("ntdll.dll"), "NtQueryInformationFile"
5386 );
5387 }
5388
5389 /* Remove the hook installed by win32_hook_closehandle_crt(). This is needed in
5390 * case the Perl DLL is unloaded, which would cause the hook become invalid.
5391 * This can happen in embedded Perls, for example in mod_perl. */
5392 static void
win32_unhook_closehandle_in_crt()5393 win32_unhook_closehandle_in_crt()
5394 {
5395 if (!CloseHandle_orig)
5396 return;
5397
5398 HMODULE crt_handle;
5399 BOOL succ = GetModuleHandleExA(
5400 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
5401 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)_close,
5402 &crt_handle
5403 );
5404 if (!succ)
5405 return;
5406
5407 win32_hook_imported_function_in_module(
5408 crt_handle, "CloseHandle", (FARPROC)CloseHandle_orig
5409 );
5410
5411 CloseHandle_orig = NULL;
5412 }
5413
5414 void
Perl_win32_init(int * argcp,char *** argvp)5415 Perl_win32_init(int *argcp, char ***argvp)
5416 {
5417 #ifdef SET_INVALID_PARAMETER_HANDLER
5418 _invalid_parameter_handler oldHandler, newHandler;
5419 newHandler = my_invalid_parameter_handler;
5420 oldHandler = _set_invalid_parameter_handler(newHandler);
5421 _CrtSetReportMode(_CRT_ASSERT, 0);
5422 #endif
5423 /* Disable floating point errors, Perl will trap the ones we
5424 * care about. VC++ RTL defaults to switching these off
5425 * already, but some RTLs don't. Since we don't
5426 * want to be at the vendor's whim on the default, we set
5427 * it explicitly here.
5428 */
5429 #if !defined(__GNUC__)
5430 _control87(MCW_EM, MCW_EM);
5431 #endif
5432 MALLOC_INIT;
5433
5434 /* When the manifest resource requests Common-Controls v6 then
5435 * user32.dll no longer registers all the Windows classes used for
5436 * standard controls but leaves some of them to be registered by
5437 * comctl32.dll. InitCommonControls() doesn't do anything but calling
5438 * it makes sure comctl32.dll gets loaded into the process and registers
5439 * the standard control classes. Without this even normal Windows APIs
5440 * like MessageBox() can fail under some versions of Windows XP.
5441 */
5442 InitCommonControls();
5443
5444 WSADATA wsadata;
5445 WSAStartup(MAKEWORD(2, 2), &wsadata);
5446
5447 g_osver.dwOSVersionInfoSize = sizeof(g_osver);
5448 GetVersionEx(&g_osver);
5449
5450 win32_hook_closehandle_in_crt();
5451
5452 ansify_path();
5453
5454 #ifndef WIN32_NO_REGISTRY
5455 {
5456 LONG retval;
5457 retval = RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Perl", 0, KEY_READ, &HKCU_Perl_hnd);
5458 if (retval != ERROR_SUCCESS) {
5459 HKCU_Perl_hnd = NULL;
5460 }
5461 retval = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Perl", 0, KEY_READ, &HKLM_Perl_hnd);
5462 if (retval != ERROR_SUCCESS) {
5463 HKLM_Perl_hnd = NULL;
5464 }
5465 }
5466 #endif
5467
5468 {
5469 FILETIME ft;
5470 if (!SystemTimeToFileTime(&time_t_epoch_base_systemtime,
5471 &ft)) {
5472 fprintf(stderr, "panic: cannot convert base system time to filetime\n"); /* no interp */
5473 exit(1);
5474 }
5475 time_t_epoch_base_filetime.LowPart = ft.dwLowDateTime;
5476 time_t_epoch_base_filetime.HighPart = ft.dwHighDateTime;
5477 }
5478
5479 MUTEX_INIT(&win32_read_console_mutex);
5480 }
5481
5482 void
Perl_win32_term(void)5483 Perl_win32_term(void)
5484 {
5485 HINTS_REFCNT_TERM;
5486 OP_REFCNT_TERM;
5487 PERLIO_TERM;
5488 MALLOC_TERM;
5489 LOCALE_TERM;
5490 ENV_TERM;
5491 #ifndef WIN32_NO_REGISTRY
5492 /* handles might be NULL, RegCloseKey then returns ERROR_INVALID_HANDLE
5493 but no point of checking and we can't die() at this point */
5494 RegCloseKey(HKLM_Perl_hnd);
5495 RegCloseKey(HKCU_Perl_hnd);
5496 /* the handles are in an undefined state until the next PERL_SYS_INIT3 */
5497 #endif
5498 win32_unhook_closehandle_in_crt();
5499 }
5500
5501 void
win32_get_child_IO(child_IO_table * ptbl)5502 win32_get_child_IO(child_IO_table* ptbl)
5503 {
5504 ptbl->childStdIn = GetStdHandle(STD_INPUT_HANDLE);
5505 ptbl->childStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
5506 ptbl->childStdErr = GetStdHandle(STD_ERROR_HANDLE);
5507 }
5508
5509 Sighandler_t
win32_signal(int sig,Sighandler_t subcode)5510 win32_signal(int sig, Sighandler_t subcode)
5511 {
5512 dTHXa(NULL);
5513 if (sig < SIG_SIZE) {
5514 int save_errno = errno;
5515 Sighandler_t result;
5516 #ifdef SET_INVALID_PARAMETER_HANDLER
5517 /* Silence our invalid parameter handler since we expect to make some
5518 * calls with invalid signal numbers giving a SIG_ERR result. */
5519 BOOL oldvalue = set_silent_invalid_parameter_handler(TRUE);
5520 #endif
5521 result = signal(sig, subcode);
5522 #ifdef SET_INVALID_PARAMETER_HANDLER
5523 set_silent_invalid_parameter_handler(oldvalue);
5524 #endif
5525 aTHXa(PERL_GET_THX);
5526 if (result == SIG_ERR) {
5527 result = w32_sighandler[sig];
5528 errno = save_errno;
5529 }
5530 w32_sighandler[sig] = subcode;
5531 return result;
5532 }
5533 else {
5534 errno = EINVAL;
5535 return SIG_ERR;
5536 }
5537 }
5538
5539 /* The PerlMessageWindowClass's WindowProc */
5540 LRESULT CALLBACK
win32_message_window_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)5541 win32_message_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
5542 {
5543 return win32_process_message(hwnd, msg, wParam, lParam) ?
5544 0 : DefWindowProc(hwnd, msg, wParam, lParam);
5545 }
5546
5547 /* The real message handler. Can be called with
5548 * hwnd == NULL to process our thread messages. Returns TRUE for any messages
5549 * that it processes */
5550 static LRESULT
win32_process_message(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)5551 win32_process_message(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
5552 {
5553 /* BEWARE. The context retrieved using dTHX; is the context of the
5554 * 'parent' thread during the CreateWindow() phase - i.e. for all messages
5555 * up to and including WM_CREATE. If it ever happens that you need the
5556 * 'child' context before this, then it needs to be passed into
5557 * win32_create_message_window(), and passed to the WM_NCCREATE handler
5558 * from the lparam of CreateWindow(). It could then be stored/retrieved
5559 * using [GS]etWindowLongPtr(... GWLP_USERDATA ...), possibly eliminating
5560 * the dTHX calls here. */
5561 /* XXX For now it is assumed that the overhead of the dTHX; for what
5562 * are relativley infrequent code-paths, is better than the added
5563 * complexity of getting the correct context passed into
5564 * win32_create_message_window() */
5565 dTHX;
5566
5567 switch(msg) {
5568
5569 #ifdef USE_ITHREADS
5570 case WM_USER_MESSAGE: {
5571 long child = find_pseudo_pid(aTHX_ (int)wParam);
5572 if (child >= 0) {
5573 w32_pseudo_child_message_hwnds[child] = (HWND)lParam;
5574 return 1;
5575 }
5576 break;
5577 }
5578 #endif
5579
5580 case WM_USER_KILL: {
5581 /* We use WM_USER_KILL to fake kill() with other signals */
5582 int sig = (int)wParam;
5583 if (do_raise(aTHX_ sig))
5584 sig_terminate(aTHX_ sig);
5585
5586 return 1;
5587 }
5588
5589 case WM_TIMER: {
5590 /* alarm() is a one-shot but SetTimer() repeats so kill it */
5591 if (w32_timerid && w32_timerid==(UINT)wParam) {
5592 KillTimer(w32_message_hwnd, w32_timerid);
5593 w32_timerid=0;
5594
5595 /* Now fake a call to signal handler */
5596 if (do_raise(aTHX_ 14))
5597 sig_terminate(aTHX_ 14);
5598
5599 return 1;
5600 }
5601 break;
5602 }
5603
5604 default:
5605 break;
5606
5607 } /* switch */
5608
5609 /* Above or other stuff may have set a signal flag, and we may not have
5610 * been called from win32_async_check() (e.g. some other GUI's message
5611 * loop. BUT DON'T dispatch signals here: If someone has set a SIGALRM
5612 * handler that die's, and the message loop that calls here is wrapped
5613 * in an eval, then you may well end up with orphaned windows - signals
5614 * are dispatched by win32_async_check() */
5615
5616 return 0;
5617 }
5618
5619 void
win32_create_message_window_class(void)5620 win32_create_message_window_class(void)
5621 {
5622 /* create the window class for "message only" windows */
5623 WNDCLASS wc;
5624
5625 Zero(&wc, 1, wc);
5626 wc.lpfnWndProc = win32_message_window_proc;
5627 wc.hInstance = (HINSTANCE)GetModuleHandle(NULL);
5628 wc.lpszClassName = "PerlMessageWindowClass";
5629
5630 /* second and subsequent calls will fail, but class
5631 * will already be registered */
5632 RegisterClass(&wc);
5633 }
5634
5635 HWND
win32_create_message_window(void)5636 win32_create_message_window(void)
5637 {
5638 win32_create_message_window_class();
5639 return CreateWindow("PerlMessageWindowClass", "PerlMessageWindow",
5640 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
5641 }
5642
5643 #ifdef HAVE_INTERP_INTERN
5644
5645 static void
win32_csighandler(int sig)5646 win32_csighandler(int sig)
5647 {
5648 #if 0
5649 dTHXa(PERL_GET_SIG_CONTEXT);
5650 Perl_warn(aTHX_ "Got signal %d",sig);
5651 #endif
5652 /* Does nothing */
5653 }
5654
5655 #if defined(__MINGW32__) && defined(__cplusplus)
5656 #define CAST_HWND__(x) (HWND__*)(x)
5657 #else
5658 #define CAST_HWND__(x) x
5659 #endif
5660
5661 void
Perl_sys_intern_init(pTHX)5662 Perl_sys_intern_init(pTHX)
5663 {
5664 int i;
5665
5666 w32_perlshell_tokens = NULL;
5667 w32_perlshell_vec = (char**)NULL;
5668 w32_perlshell_items = 0;
5669 w32_fdpid = newAV();
5670 Newx(w32_children, 1, child_tab);
5671 w32_num_children = 0;
5672 # ifdef USE_ITHREADS
5673 w32_pseudo_id = 0;
5674 Newx(w32_pseudo_children, 1, pseudo_child_tab);
5675 w32_num_pseudo_children = 0;
5676 # endif
5677 w32_timerid = 0;
5678 w32_message_hwnd = CAST_HWND__(INVALID_HANDLE_VALUE);
5679 w32_poll_count = 0;
5680 for (i=0; i < SIG_SIZE; i++) {
5681 w32_sighandler[i] = SIG_DFL;
5682 }
5683 # ifdef MULTIPLICITY
5684 if (my_perl == PL_curinterp) {
5685 # else
5686 {
5687 # endif
5688 /* Force C runtime signal stuff to set its console handler */
5689 signal(SIGINT,win32_csighandler);
5690 signal(SIGBREAK,win32_csighandler);
5691
5692 /* We spawn asynchronous processes with the CREATE_NEW_PROCESS_GROUP
5693 * flag. This has the side-effect of disabling Ctrl-C events in all
5694 * processes in this group.
5695 * We re-enable Ctrl-C handling by calling SetConsoleCtrlHandler()
5696 * with a NULL handler.
5697 */
5698 SetConsoleCtrlHandler(NULL,FALSE);
5699
5700 /* Push our handler on top */
5701 SetConsoleCtrlHandler(win32_ctrlhandler,TRUE);
5702 }
5703 }
5704
5705 void
5706 Perl_sys_intern_clear(pTHX)
5707 {
5708
5709 Safefree(w32_perlshell_tokens);
5710 Safefree(w32_perlshell_vec);
5711 /* NOTE: w32_fdpid is freed by sv_clean_all() */
5712 Safefree(w32_children);
5713 if (w32_timerid) {
5714 KillTimer(w32_message_hwnd, w32_timerid);
5715 w32_timerid = 0;
5716 }
5717 if (w32_message_hwnd != NULL && w32_message_hwnd != INVALID_HANDLE_VALUE)
5718 DestroyWindow(w32_message_hwnd);
5719 # ifdef MULTIPLICITY
5720 if (my_perl == PL_curinterp) {
5721 # else
5722 {
5723 # endif
5724 SetConsoleCtrlHandler(win32_ctrlhandler,FALSE);
5725 }
5726 # ifdef USE_ITHREADS
5727 Safefree(w32_pseudo_children);
5728 # endif
5729 }
5730
5731 # ifdef USE_ITHREADS
5732
5733 void
5734 Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst)
5735 {
5736 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
5737
5738 dst->perlshell_tokens = NULL;
5739 dst->perlshell_vec = (char**)NULL;
5740 dst->perlshell_items = 0;
5741 dst->fdpid = newAV();
5742 Newxz(dst->children, 1, child_tab);
5743 dst->pseudo_id = 0;
5744 Newxz(dst->pseudo_children, 1, pseudo_child_tab);
5745 dst->timerid = 0;
5746 dst->message_hwnd = CAST_HWND__(INVALID_HANDLE_VALUE);
5747 dst->poll_count = 0;
5748 Copy(src->sigtable,dst->sigtable,SIG_SIZE,Sighandler_t);
5749 }
5750 # endif /* USE_ITHREADS */
5751 #endif /* HAVE_INTERP_INTERN */
5752