1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 1996-2020. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20 /*
21 * system-dependent functions
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include "sys.h"
30 #include "erl_osenv.h"
31 #include "erl_alloc.h"
32 #include "erl_sys_driver.h"
33 #include "global.h"
34 #include "erl_threads.h"
35 #include "../../drivers/win32/win_con.h"
36 #include "erl_cpu_topology.h"
37 #include <malloc.h>
38
39 void erts_sys_init_float(void);
40
41 void erl_start(int, char**);
42 void erts_exit(int n, const char*, ...);
43 void erl_error(const char*, va_list);
44
45 /*
46 * Microsoft-specific function to map a WIN32 error code to a Posix errno.
47 */
48 extern void _dosmaperr(DWORD);
49
50 #ifdef ERL_RUN_SHARED_LIB
51 #ifdef __argc
52 #undef __argc
53 #endif
54 #define __argc e_argc
55 #ifdef __argv
56 #undef __argv
57 #endif
58 #define __argv e_argv
59 #endif
60
61 typedef struct driver_data DriverData;
62
63 static void init_console();
64 static int get_and_remove_option(int* argc, char** argv, const char* option);
65 static char *get_and_remove_option2(int *argc, char **argv,
66 const char *option);
67 static int init_async_io(DriverData *dp, struct async_io* aio, int use_threads);
68 static void release_async_io(struct async_io* aio, ErlDrvPort);
69 static void async_read_file(struct async_io* aio, LPVOID buf, DWORD numToRead);
70 static int async_write_file(struct async_io* aio, LPVOID buf, DWORD numToWrite);
71 static int get_overlapped_result(struct async_io* aio,
72 LPDWORD pBytesRead, BOOL wait);
73 static BOOL create_child_process(wchar_t *, HANDLE, HANDLE,
74 HANDLE, LPHANDLE, LPDWORD, BOOL,
75 LPVOID, wchar_t*, unsigned,
76 wchar_t **, int *);
77 static int create_pipe(LPHANDLE, LPHANDLE, BOOL, BOOL);
78 static int application_type(const wchar_t* originalName, wchar_t fullPath[MAX_PATH],
79 BOOL search_in_path, BOOL handle_quotes,
80 int *error_return);
81 static void *build_env_block(const erts_osenv_t *env);
82
83 HANDLE erts_service_event;
84
85 static erts_tsd_key_t win32_errstr_key;
86
87 static erts_atomic_t pipe_creation_counter;
88
89 /* Results from application_type(_w) is one of */
90 #define APPL_NONE 0
91 #define APPL_DOS 1
92 #define APPL_WIN3X 2
93 #define APPL_WIN32 3
94
95 static int driver_write(long, HANDLE, byte*, int);
96 static int create_file_thread(struct async_io* aio, int mode);
97 static void close_active_handle(DriverData *, HANDLE handle);
98 static DWORD WINAPI threaded_handle_closer(LPVOID param);
99 static DWORD WINAPI threaded_reader(LPVOID param);
100 static DWORD WINAPI threaded_writer(LPVOID param);
101 static DWORD WINAPI threaded_exiter(LPVOID param);
102
103 #ifdef DEBUG
104 static void debug_console(void);
105 #endif
106
107 BOOL WINAPI ctrl_handler(DWORD dwCtrlType);
108
109 #define PORT_BUFSIZ 4096
110
111 #define DRV_BUF_ALLOC(SZ) \
112 erts_alloc_fnf(ERTS_ALC_T_DRV_DATA_BUF, (SZ))
113 #define DRV_BUF_REALLOC(P, SZ) \
114 erts_realloc_fnf(ERTS_ALC_T_DRV_DATA_BUF, (P), (SZ))
115 #define DRV_BUF_FREE(P) \
116 erts_free(ERTS_ALC_T_DRV_DATA_BUF, (P))
117
118 /********************* General functions ****************************/
119
120 /*
121 * Whether create_pipe() should use a named pipe or an anonymous.
122 * (Named pipes are not supported on Windows 95.)
123 */
124
125 static int max_files = 1024;
126
127 static BOOL use_named_pipes;
128 static BOOL win_console = FALSE;
129
130
131 static OSVERSIONINFO int_os_version; /* Version information for Win32. */
132
133 /*#define USE_CANCELIOEX
134 Disabled the use of CancelIoEx as its been seen to cause problem with some
135 drivers. Not sure what to blame; faulty drivers or some form of invalid use.
136 */
137 #if defined(USE_CANCELIOEX)
138 static BOOL (WINAPI *fpCancelIoEx)(HANDLE,LPOVERLAPPED);
139 #endif
140
141 /* This is the system's main function (which may or may not be called "main")
142 - do general system-dependent initialization
143 - call erl_start() to parse arguments and do other init
144 */
145
146 static erts_atomic_t sys_misc_mem_sz;
147
148 HMODULE beam_module = NULL;
149
150 void erl_sys_init();
151
152 void erl_sys_args(int* argc, char** argv);
153
154 int nohup;
155 #ifndef __GNUC__
erts_sys_invalid_parameter_handler(const wchar_t * expression,const wchar_t * function,const wchar_t * file,unsigned int line,uintptr_t pReserved)156 void erts_sys_invalid_parameter_handler(const wchar_t * expression,
157 const wchar_t * function,
158 const wchar_t * file,
159 unsigned int line,
160 uintptr_t pReserved
161 )
162 {
163 #ifdef DEBUG
164 fprintf(stderr,
165 "Debug: Invalid parameter\"%ls\" "
166 "(detected in \"%ls\" [%ls:%d]) \n",
167 (expression) ? expression : L"(unknown)",
168 (function) ? function : L"(unknown)",
169 (file) ? file : L"(unknown)",
170 line);
171 #endif
172 return;
173 }
174 #endif
175
sys_primitive_init(HMODULE beam)176 void sys_primitive_init(HMODULE beam)
177 {
178 #ifndef __GNUC__
179 /* Initialize this module handle (the beam.dll module handle) and
180 take care of the standard library's aggressive invalid parameter
181 handling... */
182 _set_invalid_parameter_handler(&erts_sys_invalid_parameter_handler);
183 #endif
184 beam_module = (HMODULE) beam;
185 }
186
187 UWord
erts_sys_get_page_size(void)188 erts_sys_get_page_size(void)
189 {
190 SYSTEM_INFO info;
191 GetSystemInfo(&info);
192 return (UWord)info.dwPageSize;
193 }
194
195 Uint
erts_sys_misc_mem_sz(void)196 erts_sys_misc_mem_sz(void)
197 {
198 Uint res = (Uint) erts_check_io_size();
199 res += (Uint) erts_atomic_read_mb(&sys_misc_mem_sz);
200 return res;
201 }
202
203 /*
204 * Reset the terminal to the original settings on exit
205 */
sys_tty_reset(int exit_code)206 void sys_tty_reset(int exit_code)
207 {
208 if (exit_code == ERTS_ERROR_EXIT)
209 ConWaitForExit();
210 else
211 ConNormalExit();
212 }
213
erl_sys_args(int * argc,char ** argv)214 void erl_sys_args(int* argc, char** argv)
215 {
216 char *event_name;
217
218 erts_sys_env_init();
219
220 nohup = get_and_remove_option(argc, argv, "-nohup");
221
222 #ifdef DEBUG
223 /*
224 * Start a debug console if -console option given.
225 */
226
227 if (get_and_remove_option(argc, argv, "-console")) {
228 debug_console();
229 }
230 #endif
231
232 if (nohup && (event_name = get_and_remove_option2(argc, argv,
233 "-service_event"))) {
234 if ((erts_service_event =
235 OpenEvent(EVENT_ALL_ACCESS,FALSE,event_name)) == NULL) {
236 erts_fprintf(stderr,
237 "Warning: could not open service event: %s\r\n",
238 event_name);
239 }
240 } else {
241 erts_service_event = NULL;
242 }
243
244 #ifdef DEBUG
245 /*
246 * Given the "-threads" option, always use threads instead of
247 * named pipes.
248 */
249
250 if (get_and_remove_option(argc, argv, "-threads")) {
251 use_named_pipes = FALSE;
252 }
253 #endif
254 }
255
256 /*
257 * Function returns 1 if we can read from all values in between
258 * start and stop.
259 */
260 int
erts_sys_is_area_readable(char * start,char * stop)261 erts_sys_is_area_readable(char *start, char *stop) {
262 volatile char tmp;
263 __try
264 {
265 while(start < stop) {
266 tmp = *start;
267 start++;
268 }
269 }
270 __except(EXCEPTION_EXECUTE_HANDLER)
271 {
272 return 0;
273 }
274 return 1;
275 }
276
erts_sys_prepare_crash_dump(int secs)277 int erts_sys_prepare_crash_dump(int secs)
278 {
279 Port *heart_port;
280 Eterm heap[3];
281 Eterm *hp = heap;
282 Eterm list = NIL;
283
284 heart_port = erts_get_heart_port();
285
286 if (heart_port) {
287
288 list = CONS(hp, make_small(8), list); hp += 2;
289
290 /* send to heart port, CMD = 8, i.e. prepare crash dump =o */
291 erts_port_output(NULL, ERTS_PORT_SIG_FLG_FORCE_IMM_CALL, heart_port,
292 heart_port->common.id, list, NULL);
293
294 return 1;
295 }
296
297 /* Windows - free file descriptors are hopefully available */
298 /* Alarm not used on windows */
299
300 return 0;
301 }
302
erts_set_signal(Eterm signal,Eterm type)303 int erts_set_signal(Eterm signal, Eterm type) {
304 return 0;
305 }
306
307 static void
init_console(void)308 init_console(void)
309 {
310 char* mode = erts_read_env("ERL_CONSOLE_MODE");
311
312 if (!mode || strcmp(mode, "window") == 0) {
313 win_console = TRUE;
314 ConInit();
315 /*nohup = 0;*/
316 } else if (strncmp(mode, "tty:", 4) == 0) {
317 if (mode[5] == 'c') {
318 setvbuf(stdout, NULL, _IONBF, 0);
319 }
320 if (mode[6] == 'c') {
321 setvbuf(stderr, NULL, _IONBF, 0);
322 }
323 }
324
325 erts_free_read_env(mode);
326 }
327
sys_max_files(void)328 int sys_max_files(void)
329 {
330 return max_files;
331 }
332
333 /*
334 * Looks for the given option in the argv vector. If it is found,
335 * it will be removed from the argv vector.
336 *
337 * If the return value indicates that the option was found and removed,
338 * it is the responsibility of the caller to decrement the value of argc.
339 *
340 * Returns: 0 if the option wasn't found, 1 if it was found
341 */
342
343 static int
get_and_remove_option(int * argc,char * argv[],const char * option)344 get_and_remove_option(int* argc, char* argv[], const char *option)
345 {
346 int i;
347
348 for (i = 1; i < *argc; i++) {
349 if (strcmp(argv[i], option) == 0) {
350 (*argc)--;
351 while (i < *argc) {
352 argv[i] = argv[i+1];
353 i++;
354 }
355 argv[i] = NULL;
356 return 1;
357 }
358 }
359 return 0;
360 }
361
get_and_remove_option2(int * argc,char ** argv,const char * option)362 static char *get_and_remove_option2(int *argc, char **argv,
363 const char *option)
364 {
365 char *ret;
366 int i;
367
368 for (i = 1; i < *argc; i++) {
369 if (strcmp(argv[i], option) == 0) {
370 if (i+1 < *argc) {
371 ret = argv[i+1];
372 (*argc) -= 2;
373 while (i < *argc) {
374 argv[i] = argv[i+2];
375 i++;
376 }
377 argv[i] = NULL;
378 return ret;
379 }
380 }
381 }
382 return NULL;
383 }
384
385
386 /************************** OS info *******************************/
387
388 /* Used by erlang:info/1. */
389 /* (This code was formerly in drv.XXX/XXX_os_drv.c) */
390
391 char os_type[] = "win32";
392
393 void
os_flavor(char * namebuf,unsigned size)394 os_flavor(char *namebuf, unsigned size)
395 {
396 switch (int_os_version.dwPlatformId) {
397 case VER_PLATFORM_WIN32_WINDOWS:
398 strcpy(namebuf, "windows");
399 break;
400 case VER_PLATFORM_WIN32_NT:
401 strcpy(namebuf, "nt");
402 break;
403 default: /* Can't happen. */
404 strcpy(namebuf, "unknown");
405 break;
406 }
407 }
408
409 void
os_version(pMajor,pMinor,pBuild)410 os_version(pMajor, pMinor, pBuild)
411 int* pMajor; /* Pointer to major version. */
412 int* pMinor; /* Pointer to minor version. */
413 int* pBuild; /* Pointer to build number. */
414 {
415 *pMajor = int_os_version.dwMajorVersion;
416 *pMinor = int_os_version.dwMinorVersion;
417 *pBuild = int_os_version.dwBuildNumber;
418 }
419
420 /************************** Port I/O *******************************/
421
422 /* I. Common stuff */
423
424 /* II. The spawn/fd/vanilla drivers */
425
426 /*
427 * Definitions for driver flags.
428 */
429
430 #define DF_OVR_READY 1 /* Overlapped result is ready. */
431 #define DF_EXIT_THREAD 2 /* The thread should exit. */
432 #define DF_XLAT_CR 4 /* The thread should translate CRs. */
433 #define DF_DROP_IF_INVH 8 /* Drop packages instead of crash if
434 invalid handle (stderr) */
435 #define DF_THREAD_FLUSHED 16 /* The thread should exit. */
436
437 #define OV_BUFFER_PTR(dp) ((LPVOID) ((dp)->ov.Internal))
438 #define OV_NUM_TO_READ(dp) ((dp)->ov.InternalHigh)
439
440 /*
441 * This data is used to make overlapped I/O operations work on both
442 * Windows NT (using true overlapped I/O) and Windows 95 (using threads).
443 */
444
445 typedef struct async_io {
446 unsigned flags; /* Driver flags, definitions found above. */
447 HANDLE thread; /* If -1, overlapped I/O is used (Windows NT).
448 * Otherwise, it is the handle of the thread used
449 * for simulating overlapped I/O (Windows 95 and
450 * the console for Windows NT).
451 */
452 HANDLE fd; /* Handle for file or pipe. */
453 int async_io_active; /* if true, a close of the file will signal the event in ov */
454 OVERLAPPED ov; /* Control structure for overlapped reading.
455 * When overlapped reading is simulated with
456 * a thread, the fields are used as follows:
457 * ov.Internal - Read buffer.
458 * ov.InternalHigh - Number of bytes to read.
459 * See macros above.
460 */
461 HANDLE ioAllowed; /* The thread will wait for this event
462 * before starting a new read or write.
463 */
464 HANDLE flushEvent; /* Used to signal that a flush should be done. */
465 HANDLE flushReplyEvent; /* Used to signal that a flush has been done. */
466 DWORD pendingError; /* Used to delay presentating an error to Erlang
467 * until the check_io function is entered.
468 */
469 DWORD bytesTransferred; /* Bytes read or write in the last operation.
470 * Valid only when DF_OVR_READY is set.
471 */
472 DriverData *dp; /* Pointer to driver data struct which
473 this struct is part of */
474 } AsyncIo;
475
476
477 /*
478 * Input thread for fd_driver (if fd_driver is running).
479 */
480 static AsyncIo* fd_driver_input = NULL;
481 static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD);
482
483 /*
484 * This data is used by the spawn and vanilla drivers.
485 * There will be one entry for each port, even if the input
486 * and output HANDLES are different. Since handles are not
487 * guaranteed to be small numbers in Win32, we cannot index
488 * with them. I.e. the index for each entry is not equal to
489 * none of the file handles.
490 */
491
492 struct driver_data {
493 int totalNeeded; /* Total number of bytes needed to fill
494 * up the packet header or packet. */
495 int bytesInBuffer; /* Number of bytes read so far in
496 * the input buffer.
497 */
498 int inBufSize; /* Size of input buffer. */
499 byte *inbuf; /* Buffer to use for overlapped read. */
500 int outBufSize; /* Size of output buffer. */
501 byte *outbuf; /* Buffer to use for overlapped write. */
502 ErlDrvPort port_num; /* The port handle. */
503 int packet_bytes; /* 0: continuous stream, 1, 2, or 4: the number
504 * of bytes in the packet header.
505 */
506 HANDLE port_pid; /* PID of the port process. */
507 AsyncIo in; /* Control block for overlapped reading. */
508 AsyncIo out; /* Control block for overlapped writing. */
509 int report_exit; /* Do report exit status for the port */
510 erts_atomic32_t refc; /* References to this struct */
511 ErlDrvSizeT high_watermark; /* Q size when to go to busy port state */
512 ErlDrvSizeT low_watermark; /* Q size when to leave busy port state */
513 int busy;
514 };
515
516 /* Driver interfaces */
517 static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*);
518 static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*);
519 static ErlDrvData vanilla_start(ErlDrvPort, char*, SysDriverOpts*);
520 static int spawn_init(void);
521 static int fd_init(void);
522 static void fd_stop(ErlDrvData);
523 static void stop(ErlDrvData);
524 static void output(ErlDrvData, char*, ErlDrvSizeT);
525 static void ready_input(ErlDrvData, ErlDrvEvent);
526 static void ready_output(ErlDrvData, ErlDrvEvent);
527 static void stop_select(ErlDrvEvent, void*);
528
529 struct erl_drv_entry spawn_driver_entry = {
530 spawn_init,
531 spawn_start,
532 stop,
533 output,
534 ready_input,
535 ready_output,
536 "spawn",
537 NULL, /* finish */
538 NULL, /* handle */
539 NULL, /* control */
540 NULL, /* timeout */
541 NULL, /* outputv */
542 NULL, /* ready_async */
543 NULL, /* flush */
544 NULL, /* call */
545 NULL, /* event */
546 ERL_DRV_EXTENDED_MARKER,
547 ERL_DRV_EXTENDED_MAJOR_VERSION,
548 ERL_DRV_EXTENDED_MINOR_VERSION,
549 0, /* ERL_DRV_FLAGs */
550 NULL,
551 NULL, /* process_exit */
552 stop_select
553 };
554
555 #ifdef HARD_POLL_DEBUG
556 extern void poll_debug_set_active_fd(ErtsSysFdType fd);
557 extern void poll_debug_read_begin(ErtsSysFdType fd);
558 extern void poll_debug_read_done(ErtsSysFdType fd, int bytes);
559 extern void poll_debug_async_initialized(ErtsSysFdType fd);
560 extern void poll_debug_async_immediate(ErtsSysFdType fd, int bytes);
561 extern void poll_debug_write_begin(ErtsSysFdType fd);
562 extern void poll_debug_write_done(ErtsSysFdType fd, int bytes);
563 #endif
564
565 extern int null_func(void);
566
567 struct erl_drv_entry fd_driver_entry = {
568 fd_init,
569 fd_start,
570 fd_stop,
571 output,
572 ready_input,
573 ready_output,
574 "fd",
575 NULL, /* finish */
576 NULL, /* handle */
577 NULL, /* control */
578 NULL, /* timeout */
579 NULL, /* outputv */
580 NULL, /* ready_async */
581 NULL, /* flush */
582 NULL, /* call */
583 NULL, /* event */
584 ERL_DRV_EXTENDED_MARKER,
585 ERL_DRV_EXTENDED_MAJOR_VERSION,
586 ERL_DRV_EXTENDED_MINOR_VERSION,
587 0, /* ERL_DRV_FLAGs */
588 NULL,
589 NULL, /* process_exit */
590 stop_select
591 };
592
593 struct erl_drv_entry vanilla_driver_entry = {
594 null_func,
595 vanilla_start,
596 stop,
597 output,
598 ready_input,
599 ready_output,
600 "vanilla",
601 NULL, /* finish */
602 NULL, /* handle */
603 NULL, /* control */
604 NULL, /* timeout */
605 NULL, /* outputv */
606 NULL, /* ready_async */
607 NULL, /* flush */
608 NULL, /* call */
609 NULL, /* event */
610 ERL_DRV_EXTENDED_MARKER,
611 ERL_DRV_EXTENDED_MAJOR_VERSION,
612 ERL_DRV_EXTENDED_MINOR_VERSION,
613 0, /* ERL_DRV_FLAGs */
614 NULL,
615 NULL, /* process_exit */
616 stop_select
617 };
618
619 static ERTS_INLINE void
refer_driver_data(DriverData * dp)620 refer_driver_data(DriverData *dp)
621 {
622 #ifdef DEBUG
623 erts_aint32_t refc = erts_atomic32_inc_read_nob(&dp->refc);
624 ASSERT(refc > 1);
625 #else
626 erts_atomic32_inc_nob(&dp->refc);
627 #endif
628 }
629
630 static ERTS_INLINE void
unrefer_driver_data(DriverData * dp)631 unrefer_driver_data(DriverData *dp)
632 {
633 erts_aint32_t refc = erts_atomic32_dec_read_mb(&dp->refc);
634 ASSERT(refc >= 0);
635 if (refc == 0)
636 driver_free(dp);
637 }
638
639 /*
640 * Initialises a DriverData structure.
641 *
642 * Results: Returns a pointer to a DriverData structure, or NULL
643 * if the initialsation failed.
644 */
645
646 static DriverData*
new_driver_data(ErlDrvPort port_num,int packet_bytes,int wait_objs_required,int use_threads)647 new_driver_data(ErlDrvPort port_num, int packet_bytes, int wait_objs_required, int use_threads)
648 {
649 DriverData* dp;
650
651 DEBUGF(("new_driver_data(%p, pb %d)\n", port_num, packet_bytes));
652
653 dp = driver_alloc(sizeof(DriverData));
654 if (!dp)
655 return NULL;
656 /*
657 * We used to test first at all that there is enough room in the
658 * array used by WaitForMultipleObjects(), but that is not necessary
659 * any more, since driver_select() can't fail.
660 */
661
662 erts_atomic32_init_nob(&dp->refc, 1);
663 dp->bytesInBuffer = 0;
664 dp->totalNeeded = packet_bytes;
665 dp->inBufSize = PORT_BUFSIZ;
666 dp->inbuf = DRV_BUF_ALLOC(dp->inBufSize);
667 if (dp->inbuf == NULL)
668 goto buf_alloc_error;
669 erts_atomic_add_nob(&sys_misc_mem_sz, dp->inBufSize);
670 dp->outBufSize = 0;
671 dp->outbuf = NULL;
672 dp->port_num = port_num;
673 dp->packet_bytes = packet_bytes;
674 dp->port_pid = INVALID_HANDLE_VALUE;
675 if (init_async_io(dp, &dp->in, use_threads) == -1)
676 goto async_io_error1;
677 if (init_async_io(dp, &dp->out, use_threads) == -1)
678 goto async_io_error2;
679
680 return dp;
681
682 async_io_error2:
683 release_async_io(&dp->in, dp->port_num);
684 async_io_error1:
685 release_async_io(&dp->out, dp->port_num);
686
687 buf_alloc_error:
688 driver_free(dp);
689 return NULL;
690 }
691
692 static void
release_driver_data(DriverData * dp)693 release_driver_data(DriverData* dp)
694 {
695 #ifdef USE_CANCELIOEX
696 if (fpCancelIoEx != NULL) {
697 if (dp->in.thread == (HANDLE) -1 && dp->in.fd != INVALID_HANDLE_VALUE) {
698 (*fpCancelIoEx)(dp->in.fd, NULL);
699 }
700 if (dp->out.thread == (HANDLE) -1 && dp->out.fd != INVALID_HANDLE_VALUE) {
701 (*fpCancelIoEx)(dp->out.fd, NULL);
702 }
703 }
704 else
705 #endif
706 {
707 /* This is a workaround for the fact that CancelIo cant cancel
708 requests issued by another thread and that we cant use
709 CancelIoEx as that's only available in Vista etc.
710 R14: Avoid scheduler deadlock by only wait for 10ms, and then spawn
711 a thread that will keep waiting in in order to close handles. */
712 HANDLE handles[2];
713 int i = 0;
714 int timeout = 10;
715 if(dp->in.async_io_active && dp->in.fd != INVALID_HANDLE_VALUE) {
716 CloseHandle(dp->in.fd);
717 dp->in.fd = INVALID_HANDLE_VALUE;
718 DEBUGF(("Waiting for the in event thingie"));
719 if (WaitForSingleObject(dp->in.ov.hEvent,timeout) == WAIT_TIMEOUT) {
720 close_active_handle(dp, dp->in.ov.hEvent);
721 dp->in.ov.hEvent = NULL;
722 timeout = 0;
723 }
724 DEBUGF(("...done\n"));
725 }
726 if(dp->out.async_io_active && dp->out.fd != INVALID_HANDLE_VALUE) {
727 CloseHandle(dp->out.fd);
728 dp->out.fd = INVALID_HANDLE_VALUE;
729 DEBUGF(("Waiting for the out event thingie"));
730 if (WaitForSingleObject(dp->out.ov.hEvent,timeout) == WAIT_TIMEOUT) {
731 close_active_handle(dp, dp->out.ov.hEvent);
732 dp->out.ov.hEvent = NULL;
733 }
734 DEBUGF(("...done\n"));
735 }
736 }
737
738 if (dp->inbuf != NULL) {
739 ASSERT(erts_atomic_read_nob(&sys_misc_mem_sz) >= dp->inBufSize);
740 erts_atomic_add_nob(&sys_misc_mem_sz, -1*dp->inBufSize);
741 DRV_BUF_FREE(dp->inbuf);
742 dp->inBufSize = 0;
743 dp->inbuf = NULL;
744 }
745 ASSERT(dp->inBufSize == 0);
746
747 /* outbuf is released when queue is released */
748 ASSERT(!dp->outbuf);
749 ASSERT(dp->outBufSize == 0);
750
751 if (dp->port_pid != INVALID_HANDLE_VALUE) {
752 CloseHandle(dp->port_pid);
753 dp->port_pid = INVALID_HANDLE_VALUE;
754 }
755
756 release_async_io(&dp->in, dp->port_num);
757 release_async_io(&dp->out, dp->port_num);
758
759 /*
760 * This must be last, because this function might be executed from
761 * the exit thread.
762 */
763
764 unrefer_driver_data(dp);
765 }
766
767
768 struct handles_to_be_closed {
769 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
770 DriverData *drv_data[MAXIMUM_WAIT_OBJECTS];
771 unsigned cnt;
772 };
773 static struct handles_to_be_closed* htbc_curr = NULL;
774 CRITICAL_SECTION htbc_lock;
775
close_active_handle(DriverData * dp,HANDLE handle)776 static void close_active_handle(DriverData *dp, HANDLE handle)
777 {
778 struct handles_to_be_closed* htbc;
779 int i;
780 EnterCriticalSection(&htbc_lock);
781 htbc = htbc_curr;
782 if (htbc == NULL || htbc->cnt >= MAXIMUM_WAIT_OBJECTS) {
783 DWORD tid;
784 HANDLE thread;
785
786 htbc = (struct handles_to_be_closed*) erts_alloc(ERTS_ALC_T_DRV_TAB,
787 sizeof(*htbc));
788 htbc->handles[0] = CreateAutoEvent(FALSE);
789 htbc->drv_data[0] = NULL;
790 htbc->cnt = 1;
791 thread = (HANDLE *) _beginthreadex(NULL, 0, threaded_handle_closer, htbc, 0, &tid);
792 CloseHandle(thread);
793 }
794 i = htbc->cnt++;
795 htbc->handles[i] = handle;
796 htbc->drv_data[i] = dp;
797 if (dp)
798 refer_driver_data(dp); /* Need to keep driver data until we have
799 closed the event; outstanding operation
800 might write into it.. */
801 SetEvent(htbc->handles[0]);
802 htbc_curr = htbc;
803 LeaveCriticalSection(&htbc_lock);
804 }
805
806 static DWORD WINAPI
threaded_handle_closer(LPVOID param)807 threaded_handle_closer(LPVOID param)
808 {
809 struct handles_to_be_closed* htbc = (struct handles_to_be_closed*) param;
810 unsigned ix;
811 DWORD res;
812 DEBUGF(("threaded_handle_closer %p started\r\n", htbc));
813 EnterCriticalSection(&htbc_lock);
814 for (;;) {
815 {
816 HANDLE* handles = htbc->handles;
817 unsigned cnt = htbc->cnt;
818 DWORD timeout = (htbc == htbc_curr) ? INFINITE : 10*1000;
819
820 LeaveCriticalSection(&htbc_lock);
821 DEBUGF(("threaded_handle_closer %p waiting for %d handles\r\n", htbc, cnt));
822 res = WaitForMultipleObjects(cnt, handles, FALSE, timeout);
823 }
824 EnterCriticalSection(&htbc_lock);
825 switch (res) {
826 case WAIT_OBJECT_0:
827 case WAIT_TIMEOUT:
828 break; /* got some more handles to wait for maybe */
829 default:
830 ix = res - WAIT_OBJECT_0;
831 if (ix > 0 && ix < htbc->cnt) {
832 int move_ix;
833 CloseHandle(htbc->handles[ix]);
834 if (htbc->drv_data[ix])
835 unrefer_driver_data(htbc->drv_data[ix]);
836 move_ix = --htbc->cnt;
837 htbc->handles[ix] = htbc->handles[move_ix];
838 htbc->drv_data[ix] = htbc->drv_data[move_ix];
839 }
840 }
841 if (htbc != htbc_curr) {
842 if (htbc->cnt == 1) { /* no real handles left */
843 break;
844 }
845 /* The thread with most free slots will be "current" */
846 if (htbc->cnt < htbc_curr->cnt) {
847 htbc_curr = htbc;
848 DEBUGF(("threaded_handle_closer %p made current\r\n", htbc));
849 }
850 }
851 }
852 LeaveCriticalSection(&htbc_lock);
853 CloseHandle(htbc->handles[0]);
854 ASSERT(!htbc->drv_data[0]);
855 erts_free(ERTS_ALC_T_DRV_TAB, htbc);
856 DEBUGF(("threaded_handle_closer %p terminating\r\n", htbc));
857 return 0;
858 }
859
860 /*
861 * Stores input and output file descriptors in the DriverData structure,
862 * and calls driver_select().
863 *
864 * This function fortunately can't fail!
865 */
866
867 static ErlDrvData
set_driver_data(DriverData * dp,HANDLE ifd,HANDLE ofd,int read_write,int report_exit,SysDriverOpts * opts)868 set_driver_data(DriverData* dp, HANDLE ifd, HANDLE ofd, int read_write, int report_exit,
869 SysDriverOpts* opts)
870 {
871 int result;
872
873 dp->in.fd = ifd;
874 dp->out.fd = ofd;
875 dp->report_exit = report_exit;
876 dp->high_watermark = opts->high_watermark;
877 dp->low_watermark = opts->low_watermark;
878
879 dp->busy = 0;
880
881 if (read_write & DO_READ) {
882 result = driver_select(dp->port_num, (ErlDrvEvent)dp->in.ov.hEvent,
883 ERL_DRV_READ|ERL_DRV_USE, 1);
884 ASSERT(result != -1);
885 async_read_file(&dp->in, dp->inbuf, dp->inBufSize);
886 }
887
888 if (read_write & DO_WRITE) {
889 result = driver_select(dp->port_num, (ErlDrvEvent)dp->out.ov.hEvent,
890 ERL_DRV_WRITE|ERL_DRV_USE, 1);
891 ASSERT(result != -1);
892 }
893 return (ErlDrvData) dp;
894 }
895
896 static ErlDrvData
reuse_driver_data(DriverData * dp,HANDLE ifd,HANDLE ofd,int read_write,ErlDrvPort port_num)897 reuse_driver_data(DriverData *dp, HANDLE ifd, HANDLE ofd, int read_write, ErlDrvPort port_num)
898 {
899 int result;
900
901 dp->port_num = port_num;
902 dp->in.fd = ifd;
903 dp->out.fd = ofd;
904 dp->report_exit = 0;
905
906 if (read_write & DO_READ) {
907 result = driver_select(dp->port_num, (ErlDrvEvent)dp->in.ov.hEvent,
908 ERL_DRV_READ|ERL_DRV_USE, 1);
909 ASSERT(result != -1);
910 }
911
912 if (read_write & DO_WRITE) {
913 result = driver_select(dp->port_num, (ErlDrvEvent)dp->out.ov.hEvent,
914 ERL_DRV_WRITE|ERL_DRV_USE, 1);
915 ASSERT(result != -1);
916 }
917 return (ErlDrvData) dp;
918 }
919
920 /*
921 * Initialises an AsyncIo structure.
922 */
923
924 static int
init_async_io(DriverData * dp,AsyncIo * aio,int use_threads)925 init_async_io(DriverData *dp, AsyncIo* aio, int use_threads)
926 {
927 aio->dp = dp;
928 aio->flags = 0;
929 aio->thread = (HANDLE) -1;
930 aio->fd = INVALID_HANDLE_VALUE;
931 aio->ov.hEvent = NULL;
932 aio->ov.Offset = 0L;
933 aio->ov.OffsetHigh = 0L;
934 aio->ioAllowed = NULL;
935 aio->flushEvent = NULL;
936 aio->flushReplyEvent = NULL;
937 aio->pendingError = 0;
938 aio->bytesTransferred = 0;
939 aio->async_io_active = 0;
940 aio->ov.hEvent = CreateManualEvent(FALSE);
941 if (aio->ov.hEvent == NULL)
942 return -1;
943 if (use_threads) {
944 OV_BUFFER_PTR(aio) = NULL;
945 OV_NUM_TO_READ(aio) = 0;
946 aio->ioAllowed = CreateAutoEvent(FALSE);
947 if (aio->ioAllowed == NULL)
948 return -1;
949 aio->flushEvent = CreateAutoEvent(FALSE);
950 if (aio->flushEvent == NULL)
951 return -1;
952 aio->flushReplyEvent = CreateAutoEvent(FALSE);
953 if (aio->flushReplyEvent == NULL)
954 return -1;
955 }
956 return 0;
957 }
958
959 /*
960 * Releases everything allocated in an AsyncIo structure.
961 */
962
963 static void
release_async_io(AsyncIo * aio,ErlDrvPort port_num)964 release_async_io(AsyncIo* aio, ErlDrvPort port_num)
965 {
966 aio->flags = 0;
967
968 if (aio->thread != (HANDLE) -1)
969 CloseHandle(aio->thread);
970 aio->thread = (HANDLE) -1;
971
972 if (aio->fd != INVALID_HANDLE_VALUE)
973 CloseHandle(aio->fd);
974 aio->fd = INVALID_HANDLE_VALUE;
975
976 if (aio->ov.hEvent != NULL)
977 CloseHandle(aio->ov.hEvent);
978
979 aio->ov.hEvent = NULL;
980
981 if (aio->ioAllowed != NULL)
982 CloseHandle(aio->ioAllowed);
983 aio->ioAllowed = NULL;
984
985 if (aio->flushEvent != NULL)
986 CloseHandle(aio->flushEvent);
987 aio->flushEvent = NULL;
988
989 if (aio->flushReplyEvent != NULL)
990 CloseHandle(aio->flushReplyEvent);
991 aio->flushReplyEvent = NULL;
992 }
993
994 /* ----------------------------------------------------------------------
995 * async_read_file --
996 * Initiaties an asynchronous file read, or simulates that using
997 * the thread associated with this driver data. To get the results,
998 * call get_overlapped_result().
999 *
1000 * Results:
1001 * None.
1002 * ----------------------------------------------------------------------
1003 */
1004
1005 static void
async_read_file(AsyncIo * aio,LPVOID buf,DWORD numToRead)1006 async_read_file(AsyncIo* aio, LPVOID buf, DWORD numToRead)
1007 {
1008 aio->pendingError = NO_ERROR;
1009 #ifdef HARD_POLL_DEBUG
1010 poll_debug_async_initialized(aio->ov.hEvent);
1011 #endif
1012 if (aio->thread != (HANDLE) -1) {
1013 DEBUGF(("async_read_file: signaling thread 0x%x, event 0x%x\n",
1014 aio->thread, aio->ioAllowed));
1015 OV_BUFFER_PTR(aio) = buf;
1016 OV_NUM_TO_READ(aio) = numToRead;
1017 ResetEvent(aio->ov.hEvent);
1018 SetEvent(aio->ioAllowed);
1019 } else {
1020 aio->async_io_active = 1; /* Will get 0 when the event actually happened */
1021 if (ReadFile(aio->fd, buf, numToRead,
1022 &aio->bytesTransferred, &aio->ov)) {
1023 DEBUGF(("async_read_file: ReadFile() suceeded: %d bytes\n",
1024 aio->bytesTransferred));
1025 #ifdef HARD_POLL_DEBUG
1026 poll_debug_async_immediate(aio->ov.hEvent, aio->bytesTransferred);
1027 #endif
1028 aio->flags |= DF_OVR_READY;
1029 SetEvent(aio->ov.hEvent);
1030 } else {
1031 DWORD error = GetLastError();
1032 if (error != ERROR_IO_PENDING) {
1033 #ifdef HARD_POLL_DEBUG
1034 poll_debug_async_immediate(aio->ov.hEvent, 0);
1035 #endif
1036 aio->pendingError = error;
1037 SetEvent(aio->ov.hEvent);
1038 }
1039 DEBUGF(("async_read_file: ReadFile() -> %s\n", win32_errorstr(error)));
1040 }
1041 }
1042 }
1043
1044 /* ----------------------------------------------------------------------
1045 * async_write_file --
1046 * Initiaties an asynchronous file write, or simulates that using
1047 * the output thread associated with this driver data.
1048 * To get the results, call get_overlapped_result().
1049 *
1050 * Results:
1051 * None.
1052 * ----------------------------------------------------------------------
1053 */
1054 static int
async_write_file(AsyncIo * aio,LPVOID buf,DWORD numToWrite)1055 async_write_file(AsyncIo* aio, /* Pointer to async control block. */
1056 LPVOID buf, /* Pointer to buffer with data to write. */
1057 DWORD numToWrite) /* Number of bytes to write. */
1058 {
1059 aio->pendingError = NO_ERROR;
1060 if (aio->thread != (HANDLE) -1) {
1061 DEBUGF(("async_write_file: signaling thread 0x%x, event 0x%x\n",
1062 aio->thread, aio->ioAllowed));
1063 OV_BUFFER_PTR(aio) = buf;
1064 OV_NUM_TO_READ(aio) = numToWrite;
1065 ResetEvent(aio->ov.hEvent);
1066 SetEvent(aio->ioAllowed);
1067 } else {
1068 aio->async_io_active = 1; /* Will get 0 when the event actually happened */
1069 if (WriteFile(aio->fd, buf, numToWrite,
1070 &aio->bytesTransferred, &aio->ov)) {
1071 DEBUGF(("async_write_file: WriteFile() suceeded: %d bytes\n",
1072 aio->bytesTransferred));
1073 aio->async_io_active = 0; /* The event will not be signalled */
1074 ResetEvent(aio->ov.hEvent);
1075 return TRUE;
1076 } else {
1077 DWORD error = GetLastError();
1078 if (error != ERROR_IO_PENDING) {
1079 aio->pendingError = error;
1080 SetEvent(aio->ov.hEvent);
1081 }
1082 DEBUGF(("async_write_file: WriteFile() -> %s\n", win32_errorstr(error)));
1083 }
1084 }
1085 return FALSE;
1086 }
1087
1088 /* ----------------------------------------------------------------------
1089 * get_overlapped_result --
1090 *
1091 * Results:
1092 * Returns the error code for the overlapped result, or NO_ERROR
1093 * if no error.
1094 * ----------------------------------------------------------------------
1095 */
1096 static int
get_overlapped_result(AsyncIo * aio,LPDWORD pBytesRead,BOOL wait)1097 get_overlapped_result(AsyncIo* aio, /* Pointer to async control block. */
1098 LPDWORD pBytesRead, /* Where to place the number of bytes
1099 * transferred.
1100 */
1101 BOOL wait /* If true, wait until result is ready. */
1102 )
1103 {
1104 DWORD error = NO_ERROR; /* Error status from last function. */
1105
1106 if (aio->thread != (HANDLE) -1) {
1107
1108 /*
1109 * Simulate overlapped io with a thread.
1110 */
1111 DEBUGF(("get_overlapped_result: about to wait for event 0x%x\n",
1112 aio->ov.hEvent));
1113 error = WaitForSingleObject(aio->ov.hEvent, wait ? INFINITE : 0);
1114 switch (error) {
1115 case WAIT_OBJECT_0:
1116 error = aio->pendingError;
1117 aio->pendingError = NO_ERROR;
1118 *pBytesRead = aio->bytesTransferred;
1119 ResetEvent(aio->ov.hEvent);
1120 DEBUGF(("get_overlapped_result -> %s\n",
1121 win32_errorstr(error)));
1122 return error;
1123 case WAIT_TIMEOUT:
1124 DEBUGF(("get_overlapped_result -> %s\n",
1125 ERROR_IO_INCOMPLETE));
1126 return ERROR_IO_INCOMPLETE;
1127 case WAIT_FAILED: /* XXX: Shouldn't happen? */
1128 error = GetLastError();
1129 DEBUGF(("get_overlapped_result (WAIT_FAILED) -> %s\n",
1130 win32_errorstr(error)));
1131 return error;
1132 }
1133 } else if (aio->pendingError != NO_ERROR) { /* Pending error. */
1134 error = aio->pendingError;
1135 aio->pendingError = NO_ERROR;
1136 ResetEvent(aio->ov.hEvent);
1137 DEBUGF(("get_overlapped_result: pending error: %s\n",
1138 win32_errorstr(error)));
1139 return error;
1140 } else if (aio->flags & DF_OVR_READY) { /* Operation succeded. */
1141 aio->flags &= ~DF_OVR_READY;
1142 *pBytesRead = aio->bytesTransferred;
1143 ResetEvent(aio->ov.hEvent);
1144 DEBUGF(("get_overlapped_result: delayed success: %d bytes\n",
1145 aio->bytesTransferred));
1146 } else if (!GetOverlappedResult(aio->fd, &aio->ov, pBytesRead, wait)) {
1147 error = GetLastError();
1148 ResetEvent(aio->ov.hEvent);
1149 DEBUGF(("get_overlapped_result: error: %s\n", win32_errorstr(error)));
1150 return error;
1151 } else { /* Success. */
1152 DEBUGF(("get_overlapped_result: success\n"));
1153 ResetEvent(aio->ov.hEvent);
1154 }
1155 return NO_ERROR;
1156 }
1157
1158 static int
fd_init(void)1159 fd_init(void)
1160 {
1161 char kernel_dll_name[] = "kernel32";
1162 HMODULE module;
1163 module = GetModuleHandle(kernel_dll_name);
1164 fpSetHandleInformation = (module != NULL) ?
1165 (BOOL (WINAPI *)(HANDLE,DWORD,DWORD))
1166 GetProcAddress(module,"SetHandleInformation") :
1167 NULL;
1168
1169 return 0;
1170 }
1171 static int
spawn_init(void)1172 spawn_init(void)
1173 {
1174 int i;
1175 #if defined(USE_CANCELIOEX)
1176 HMODULE module = GetModuleHandle("kernel32");
1177 fpCancelIoEx = (BOOL (WINAPI *)(HANDLE,LPOVERLAPPED))
1178 ((module != NULL) ? GetProcAddress(module,"CancelIoEx") : NULL);
1179 DEBUGF(("fpCancelIoEx = %p\r\n", fpCancelIoEx));
1180 #endif
1181
1182 return 0;
1183 }
1184
1185 static ErlDrvData
spawn_start(ErlDrvPort port_num,char * utf8_name,SysDriverOpts * opts)1186 spawn_start(ErlDrvPort port_num, char* utf8_name, SysDriverOpts* opts)
1187 {
1188 HANDLE hToChild = INVALID_HANDLE_VALUE; /* Write handle to child. */
1189 HANDLE hFromChild = INVALID_HANDLE_VALUE; /* Read handle from child. */
1190 HANDLE hChildStdin = INVALID_HANDLE_VALUE; /* Child's stdin. */
1191 HANDLE hChildStdout = INVALID_HANDLE_VALUE; /* Child's stout. */
1192 HANDLE hChildStderr = INVALID_HANDLE_VALUE; /* Child's sterr. */
1193 DWORD pid;
1194 int close_child_stderr = 0;
1195 DriverData* dp; /* Pointer to driver data. */
1196 ErlDrvData retval = ERL_DRV_ERROR_GENERAL; /* Return value. */
1197 int ok;
1198 int neededSelects = 0;
1199 SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
1200 int errno_return = -1;
1201 wchar_t *name;
1202 int len;
1203
1204 if (opts->read_write & DO_READ)
1205 neededSelects++;
1206 if (opts->read_write & DO_WRITE)
1207 neededSelects++;
1208
1209 if ((dp = new_driver_data(port_num, opts->packet_bytes, neededSelects,
1210 !use_named_pipes)) == NULL)
1211 return ERL_DRV_ERROR_GENERAL;
1212
1213 /*
1214 * Create two pipes to communicate with the port program.
1215 */
1216
1217 if (opts->read_write & DO_READ) {
1218 if (!create_pipe(&hFromChild, &hChildStdout, FALSE,
1219 opts->overlapped_io))
1220 goto error;
1221 } else {
1222 hChildStdout = CreateFile("nul", GENERIC_WRITE, 0,
1223 &sa, OPEN_EXISTING,
1224 FILE_ATTRIBUTE_NORMAL, NULL);
1225 DEBUGF(("Created nul file for hChildStdout = %d\n",hChildStdout));
1226 }
1227 if (opts->read_write & DO_WRITE) {
1228 if (!create_pipe(&hChildStdin, &hToChild, TRUE, opts->overlapped_io)) {
1229 CloseHandle(hFromChild);
1230 hFromChild = INVALID_HANDLE_VALUE;
1231 CloseHandle(hChildStdout);
1232 goto error;
1233 }
1234 } else {
1235 hChildStdin = CreateFile("nul", GENERIC_READ, 0,
1236 &sa, OPEN_EXISTING,
1237 FILE_ATTRIBUTE_NORMAL, NULL);
1238 DEBUGF(("Created nul file for hChildStdin = %d\n",hChildStdin));
1239 }
1240
1241 /*
1242 * Make sure that standard error is valid handle, because a Command Prompt
1243 * window not work properly otherwise. We leave standard error alone if
1244 * it is okay and no redirection was specified.
1245 */
1246 hChildStderr = GetStdHandle(STD_ERROR_HANDLE);
1247 if (opts->redir_stderr) {
1248 hChildStderr = hChildStdout;
1249 } else if (hChildStderr == INVALID_HANDLE_VALUE || hChildStderr == 0) {
1250 hChildStderr = CreateFile("nul", GENERIC_WRITE, 0, &sa, OPEN_EXISTING,
1251 FILE_ATTRIBUTE_NORMAL, NULL);
1252 close_child_stderr = 1;
1253 }
1254 if (fpSetHandleInformation != NULL) {
1255 (*fpSetHandleInformation)(hChildStderr, HANDLE_FLAG_INHERIT, 1);
1256 }
1257 /*
1258 * Spawn the port program.
1259 */
1260
1261 if ((len = MultiByteToWideChar(CP_UTF8, 0, utf8_name, -1, NULL, 0)) > 0) {
1262 name = erts_alloc(ERTS_ALC_T_TMP, len*sizeof(wchar_t));
1263 MultiByteToWideChar(CP_UTF8, 0, utf8_name, -1, name, len);
1264 } else { /* Not valid utf-8, just convert byte to wchar */
1265 int i;
1266 len = strlen(utf8_name);
1267 name = erts_alloc(ERTS_ALC_T_TMP, (1+len)*sizeof(wchar_t));
1268 for(i=0; i<len; i++) {
1269 name[i] = (wchar_t) utf8_name[i];
1270 }
1271 name[i] = L'\0';
1272 }
1273 DEBUGF(("Spawning \"%S\"\n", name));
1274
1275 {
1276 void *environment_block = build_env_block(&opts->envir);
1277
1278 ok = create_child_process(name,
1279 hChildStdin,
1280 hChildStdout,
1281 hChildStderr,
1282 &dp->port_pid,
1283 &pid,
1284 opts->hide_window,
1285 environment_block,
1286 (wchar_t *) opts->wd,
1287 opts->spawn_type,
1288 (wchar_t **) opts->argv,
1289 &errno_return);
1290
1291 CloseHandle(hChildStdin);
1292 CloseHandle(hChildStdout);
1293
1294 if (close_child_stderr && hChildStderr != INVALID_HANDLE_VALUE &&
1295 hChildStderr != 0) {
1296 CloseHandle(hChildStderr);
1297 }
1298
1299 erts_free(ERTS_ALC_T_TMP, environment_block);
1300 erts_free(ERTS_ALC_T_TMP, name);
1301 }
1302
1303 if (!ok) {
1304 dp->port_pid = INVALID_HANDLE_VALUE;
1305 if (errno_return >= 0) {
1306 retval = ERL_DRV_ERROR_ERRNO;
1307 }
1308 } else {
1309 if (!use_named_pipes) {
1310 if ((opts->read_write & DO_READ) &&
1311 !create_file_thread(&dp->in, DO_READ))
1312 goto error;
1313 if ((opts->read_write & DO_WRITE) &&
1314 !create_file_thread(&dp->out, DO_WRITE)) {
1315 dp->in.flags = DF_EXIT_THREAD;
1316 SetEvent(dp->in.ioAllowed);
1317 WaitForSingleObject(dp->in.thread, INFINITE);
1318 dp->in.thread = (HANDLE) -1;
1319 goto error;
1320 }
1321 }
1322 #ifdef HARD_POLL_DEBUG
1323 if (strncmp(name,"inet_gethost",12) == 0) {
1324 erts_printf("Debugging \"%s\"\n", name);
1325 poll_debug_set_active_fd(dp->in.ov.hEvent);
1326 }
1327 #endif
1328 retval = set_driver_data(dp, hFromChild, hToChild, opts->read_write,
1329 opts->exit_status, opts);
1330 if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO) {
1331 /* We assume that this cannot generate a negative number */
1332 erl_drv_set_os_pid(port_num, pid);
1333 }
1334 }
1335
1336 if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO)
1337 return retval;
1338
1339 error:
1340 if (hFromChild != INVALID_HANDLE_VALUE)
1341 CloseHandle(hFromChild);
1342 if (hToChild != INVALID_HANDLE_VALUE)
1343 CloseHandle(hToChild);
1344 release_driver_data(dp);
1345 if (retval == ERL_DRV_ERROR_ERRNO) {
1346 errno = errno_return;
1347 }
1348 return retval;
1349 }
1350
1351 struct __build_env_state {
1352 WCHAR *next_variable;
1353 };
1354
build_env_foreach(void * _state,const erts_osenv_data_t * key,const erts_osenv_data_t * value)1355 static void build_env_foreach(void *_state, const erts_osenv_data_t *key,
1356 const erts_osenv_data_t *value)
1357 {
1358 struct __build_env_state *state = (struct __build_env_state*)(_state);
1359
1360 sys_memcpy(state->next_variable, key->data, key->length);
1361 state->next_variable += (int)key->length / sizeof(WCHAR);
1362 *state->next_variable++ = L'=';
1363
1364 sys_memcpy(state->next_variable, value->data, value->length);
1365 state->next_variable += (int)value->length / sizeof(WCHAR);
1366 *state->next_variable++ = L'\0';
1367 }
1368
1369 /* Builds an environment block suitable for CreateProcessW. */
build_env_block(const erts_osenv_t * env)1370 static void *build_env_block(const erts_osenv_t *env) {
1371 struct __build_env_state build_state;
1372 WCHAR *env_block;
1373
1374 env_block = erts_alloc(ERTS_ALC_T_TMP, env->content_size +
1375 (env->variable_count * sizeof(L"=\0") + sizeof(L'\0')));
1376
1377 build_state.next_variable = env_block;
1378
1379 erts_osenv_foreach_native(env, &build_state, build_env_foreach);
1380
1381 (*build_state.next_variable) = L'\0';
1382
1383 return env_block;
1384 }
1385
1386 static int
create_file_thread(AsyncIo * aio,int mode)1387 create_file_thread(AsyncIo* aio, int mode)
1388 {
1389 DWORD tid; /* Id for thread. */
1390
1391 refer_driver_data(aio->dp);
1392 aio->thread = (HANDLE)
1393 _beginthreadex(NULL, 0,
1394 (mode & DO_WRITE) ? threaded_writer : threaded_reader,
1395 aio, 0, &tid);
1396 if (aio->thread != (HANDLE) -1)
1397 return 1;
1398 unrefer_driver_data(aio->dp);
1399 return 0;
1400 }
1401
1402 /*
1403 * A helper function used by create_child_process().
1404 * Parses a command line with arguments and returns the length of the
1405 * first part containing the program name.
1406 * Example: input = "\"Program Files\"\\erl arg1 arg2"
1407 * gives 19 as result.
1408 * The length returned is equivalent with length(argv[0]) if the
1409 * command line should have been prepared by _setargv for the main function
1410 */
parse_command(wchar_t * cmd)1411 int parse_command(wchar_t* cmd){
1412 #define NORMAL 2
1413 #define STRING 1
1414 #define STOP 0
1415 int i =0;
1416 int state = NORMAL;
1417 while (cmd[i]) {
1418 switch (cmd[i]) {
1419 case L'"':
1420 if (state == NORMAL)
1421 state = STRING;
1422 else
1423 state = NORMAL;
1424 break;
1425 case L'\\':
1426 if ((state == STRING) && (cmd[i+1]==L'"'))
1427 i++;
1428 break;
1429 case L' ':
1430 if (state == NORMAL)
1431 state = STOP;
1432 break;
1433 default:
1434 break;
1435 }
1436 if (state == STOP) {
1437 return i;
1438 }
1439 i++;
1440 }
1441 return i;
1442 }
1443
1444
1445 /*
1446 * Translating of command line arguments to correct format. In the examples
1447 * below the '' are not part of the actual string.
1448 * 'io:format("hello").' -> 'io:format(\"hello\").'
1449 * 'io:format("is anybody in there?").' -> '"io:format(\"is anybody in there?\")."'
1450 * 'Just nod if you can hear me.' -> '"Just nod if you can hear me."'
1451 * 'Is there ""anyone at home?' -> '"Is there \"\"anyone at home?"'
1452 * 'Relax."' -> 'Relax.\"'
1453 *
1454 * If new == NULL we just calculate the length.
1455 *
1456 * The reason for having to quote all of the is because CreateProcessW removes
1457 * one level of escaping since it takes a single long command line rather
1458 * than the argument chunks that unix uses.
1459 */
escape_and_quote(wchar_t * str,wchar_t * new,BOOL * quoted)1460 static int escape_and_quote(wchar_t *str, wchar_t *new, BOOL *quoted) {
1461 int i, j = 0;
1462 if (new == NULL)
1463 *quoted = FALSE;
1464 else if (*quoted)
1465 new[j++] = L'"';
1466 for ( i = 0; str[i] != L'\0'; i++,j++) {
1467 if (str[i] == L' ' && new == NULL && *quoted == FALSE) {
1468 *quoted = TRUE;
1469 j++;
1470 }
1471 /* check if we have to escape quotes */
1472 if (str[i] == L'"') {
1473 if (new) new[j] = L'\\';
1474 j++;
1475 }
1476 if (new) new[j] = str[i];
1477 }
1478 if (*quoted) {
1479 if (new) new[j] = L'"';
1480 j++;
1481 }
1482 return j;
1483 }
1484
1485 /*
1486 *----------------------------------------------------------------------
1487 *
1488 * create_child_process --
1489 *
1490 * Create a child process that has pipes as its
1491 * standard input, output, and error. The child process runs
1492 * synchronously under Win32s and asynchronously under Windows NT
1493 * and Windows 95, and runs with the same environment variables
1494 * as the creating process.
1495 *
1496 * The complete Windows search path is searched to find the specified
1497 * executable. If an executable by the given name is not found,
1498 * automatically tries appending ".com", ".exe", and ".bat" to the
1499 * executable name.
1500 *
1501 * Results:
1502 * The return value is FALSE if there was a problem creating the child process.
1503 * Otherwise, the return value is 0 and *phPid is
1504 * filled with the process id of the child process.
1505 *
1506 * Side effects:
1507 * A process is created.
1508 *
1509 *----------------------------------------------------------------------
1510 */
1511
1512 static BOOL
create_child_process(wchar_t * origcmd,HANDLE hStdin,HANDLE hStdout,HANDLE hStderr,LPHANDLE phPid,LPDWORD pdwID,BOOL hide,LPVOID env,wchar_t * wd,unsigned st,wchar_t ** argv,int * errno_return)1513 create_child_process
1514 (
1515 wchar_t *origcmd, /* Command line for child process (including
1516 * name of executable). Or whole executable if st is
1517 * ERTS_SPAWN_EXECUTABLE
1518 */
1519 HANDLE hStdin, /* The standard input handle for child. */
1520 HANDLE hStdout, /* The standard output handle for child. */
1521 HANDLE hStderr, /* The standard error handle for child. */
1522 LPHANDLE phPid, /* Pointer to variable to received Process handle. */
1523 LPDWORD pdwID, /* Pointer to variable to received Process ID */
1524 BOOL hide, /* Hide the window unconditionally. */
1525 LPVOID env, /* Environment for the child */
1526 wchar_t *wd, /* Working dir for the child */
1527 unsigned st, /* Flags for spawn, tells us how to interpret origcmd */
1528 wchar_t **argv, /* Argument vector if given. */
1529 int *errno_return /* Place to put an errno in in case of failure */
1530 )
1531 {
1532 PROCESS_INFORMATION piProcInfo = {0};
1533 BOOL ok = FALSE;
1534 int applType;
1535 /* Not to be changed for different types of executables */
1536 int staticCreateFlags = GetPriorityClass(GetCurrentProcess());
1537 int createFlags = DETACHED_PROCESS;
1538 wchar_t *newcmdline = NULL;
1539 int cmdlength;
1540 wchar_t* thecommand;
1541 wchar_t* appname = NULL;
1542 HANDLE hProcess = GetCurrentProcess();
1543 STARTUPINFOW siStartInfo = {0};
1544 wchar_t execPath[MAX_PATH];
1545
1546 *errno_return = -1;
1547 siStartInfo.cb = sizeof(STARTUPINFOW);
1548 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
1549 siStartInfo.hStdInput = hStdin;
1550 siStartInfo.hStdOutput = hStdout;
1551 siStartInfo.hStdError = hStderr;
1552
1553 if (st != ERTS_SPAWN_EXECUTABLE) {
1554 /*
1555 * Parse out the program name from the command line (it can be quoted and
1556 * contain spaces).
1557 */
1558 cmdlength = parse_command(origcmd);
1559 newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (MAX_PATH+wcslen(origcmd)-cmdlength)*sizeof(wchar_t));
1560 thecommand = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (cmdlength+1)*sizeof(wchar_t));
1561 wcsncpy(thecommand, origcmd, cmdlength);
1562 thecommand[cmdlength] = L'\0';
1563 DEBUGF(("spawn command: %S\n", thecommand));
1564
1565 applType = application_type(thecommand, execPath, TRUE, TRUE, errno_return);
1566 DEBUGF(("application_type returned for (%S) is %d\n", thecommand, applType));
1567 erts_free(ERTS_ALC_T_TMP, (void *) thecommand);
1568 if (applType == APPL_NONE) {
1569 erts_free(ERTS_ALC_T_TMP,newcmdline);
1570 return FALSE;
1571 }
1572 newcmdline[0] = L'\0';
1573
1574 if (applType == APPL_DOS) {
1575 /*
1576 * Under NT, 16-bit DOS applications will not run unless they
1577 * can be attached to a console. Run the 16-bit program as
1578 * a normal process inside of a hidden console application,
1579 * and then run that hidden console as a detached process.
1580 */
1581
1582 siStartInfo.wShowWindow = SW_HIDE;
1583 siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
1584 createFlags = CREATE_NEW_CONSOLE;
1585 wcscat(newcmdline, L"cmd.exe /c ");
1586 } else if (hide) {
1587 DEBUGF(("hiding window\n"));
1588 siStartInfo.wShowWindow = SW_HIDE;
1589 siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
1590 createFlags = 0;
1591 }
1592
1593 wcscat(newcmdline, execPath);
1594 wcscat(newcmdline, origcmd+cmdlength);
1595 DEBUGF(("Creating child process: %S, createFlags = %d\n", newcmdline, createFlags));
1596 ok = CreateProcessW(appname,
1597 newcmdline,
1598 NULL,
1599 NULL,
1600 TRUE,
1601 createFlags | staticCreateFlags |
1602 CREATE_UNICODE_ENVIRONMENT,
1603 env,
1604 wd,
1605 &siStartInfo,
1606 &piProcInfo);
1607
1608 } else { /* ERTS_SPAWN_EXECUTABLE, filename and args are in unicode ({utf16,little}) */
1609 int run_cmd = 0;
1610
1611 applType = application_type(origcmd, execPath, FALSE, FALSE, errno_return);
1612 if (applType == APPL_NONE) {
1613 return FALSE;
1614 }
1615 if (applType == APPL_DOS) {
1616 /*
1617 * See comment above
1618 */
1619
1620 siStartInfo.wShowWindow = SW_HIDE;
1621 siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
1622 createFlags = CREATE_NEW_CONSOLE;
1623 run_cmd = 1;
1624 } else if (hide) {
1625 DEBUGF(("hiding window\n"));
1626 siStartInfo.wShowWindow = SW_HIDE;
1627 siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
1628 createFlags = 0;
1629 }
1630 if (run_cmd) {
1631 wchar_t cmdPath[MAX_PATH];
1632 int cmdType;
1633 cmdType = application_type(L"cmd.exe", cmdPath, TRUE, FALSE, errno_return);
1634 if (cmdType == APPL_NONE || cmdType == APPL_DOS) {
1635 return FALSE;
1636 }
1637 appname = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (wcslen(cmdPath)+1)*sizeof(wchar_t));
1638 wcscpy(appname,cmdPath);
1639 } else {
1640 appname = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (wcslen(execPath)+1)*sizeof(wchar_t));
1641 wcscpy(appname, execPath);
1642 }
1643 if (argv == NULL) {
1644 BOOL orig_need_q;
1645 wchar_t *ptr;
1646 int ocl = escape_and_quote(execPath, NULL, &orig_need_q);
1647 if (run_cmd) {
1648 newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP,
1649 (ocl + 1 + 11)*sizeof(wchar_t));
1650 memcpy(newcmdline,L"cmd.exe /c ",11*sizeof(wchar_t));
1651 ptr = newcmdline + 11;
1652 } else {
1653 newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP,
1654 (ocl + 1)*sizeof(wchar_t));
1655 ptr = (wchar_t *) newcmdline;
1656 }
1657 ptr += escape_and_quote(execPath, ptr, &orig_need_q);
1658 ptr[0] = L'\0';
1659 } else {
1660 int sum = 0;
1661 BOOL *qte = NULL;
1662 wchar_t **ar = argv;
1663 wchar_t *n;
1664 wchar_t *save_arg0 = NULL;
1665 if (argv[0] == (wchar_t *) erts_default_arg0 || run_cmd) {
1666 save_arg0 = argv[0];
1667 argv[0] = execPath;
1668 }
1669 if (run_cmd) {
1670 sum += 11; /* cmd.exe /c */
1671 }
1672
1673 while (*ar != NULL) ar++;
1674 qte = erts_alloc(ERTS_ALC_T_TMP, (ar - argv)*sizeof(BOOL));
1675
1676 ar = argv;
1677 while (*ar != NULL) {
1678 sum += escape_and_quote(*ar,NULL,qte+(ar - argv));
1679 sum++; /* space */
1680 ++ar;
1681 }
1682 ar = argv;
1683 newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, sum*sizeof(wchar_t));
1684 n = newcmdline;
1685 if (run_cmd) {
1686 memcpy(n,L"cmd.exe /c ",11*sizeof(wchar_t));
1687 n += 11;
1688 }
1689 while (*ar != NULL) {
1690 n += escape_and_quote(*ar,n,qte+(ar - argv));
1691 *n++ = L' ';
1692 ++ar;
1693 }
1694 *(n-1) = L'\0'; /* overwrite last space with '\0' */
1695 if (save_arg0 != NULL) {
1696 argv[0] = save_arg0;
1697 }
1698 erts_free(ERTS_ALC_T_TMP, qte);
1699 }
1700
1701 DEBUGF(("Creating child process: %S, createFlags = %d\n", newcmdline, createFlags));
1702 ok = CreateProcessW((wchar_t *) appname,
1703 (wchar_t *) newcmdline,
1704 NULL,
1705 NULL,
1706 TRUE,
1707 createFlags | staticCreateFlags |
1708 CREATE_UNICODE_ENVIRONMENT,
1709 env,
1710 wd,
1711 &siStartInfo,
1712 &piProcInfo);
1713
1714 } /* end SPAWN_EXECUTABLE */
1715 if (newcmdline != NULL) {
1716 erts_free(ERTS_ALC_T_TMP,newcmdline);
1717 }
1718 if (appname != NULL) {
1719 erts_free(ERTS_ALC_T_TMP,appname);
1720 }
1721 if (!ok) {
1722 DEBUGF(("CreateProcess failed: %s\n", last_error()));
1723 if (*errno_return < 0) {
1724 *errno_return = EACCES;
1725 }
1726 return FALSE;
1727 }
1728 CloseHandle(piProcInfo.hThread); /* Necessary to avoid resource leak. */
1729 *phPid = piProcInfo.hProcess;
1730 *pdwID = piProcInfo.dwProcessId;
1731
1732 if (applType == APPL_DOS) {
1733 WaitForSingleObject(hProcess, 50);
1734 }
1735
1736 return ok;
1737 }
1738
1739 /*
1740 * Note, inheritRead == FALSE means "inhetitWrite", i e one of the
1741 * pipe ends is always expected to be inherited. The pipe end that should
1742 * be inherited is opened without overlapped io flags, as the child program
1743 * would expect stdout not to demand overlapped I/O.
1744 */
create_pipe(HANDLE * phRead,HANDLE * phWrite,BOOL inheritRead,BOOL overlapped_io)1745 static int create_pipe(HANDLE *phRead, HANDLE *phWrite, BOOL inheritRead, BOOL overlapped_io)
1746 {
1747 SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
1748 char pipe_name[256]; /* Name of pipe. */
1749 Uint calls;
1750
1751 /*
1752 * If we should't use named pipes, create anonmous pipes.
1753 */
1754
1755 if (!use_named_pipes) {
1756 int success;
1757 HANDLE non_inherited; /* Non-inherited copy of handle. */
1758
1759 if (!CreatePipe(phRead, phWrite, &sa, 0)) {
1760 DEBUGF(("Error creating anonyomous pipe: %s\n", last_error()));
1761 return FALSE;
1762 }
1763
1764 if (inheritRead) {
1765 success = DuplicateHandle(GetCurrentProcess(), *phWrite,
1766 GetCurrentProcess(), &non_inherited, 0,
1767 FALSE, DUPLICATE_SAME_ACCESS);
1768 CloseHandle(*phWrite);
1769 *phWrite = non_inherited;
1770 } else {
1771 success = DuplicateHandle(GetCurrentProcess(), *phRead,
1772 GetCurrentProcess(), &non_inherited, 0,
1773 FALSE, DUPLICATE_SAME_ACCESS);
1774 CloseHandle(*phRead);
1775 *phRead = non_inherited;
1776 }
1777 return success;
1778 }
1779
1780
1781 /*
1782 * Otherwise, create named pipes.
1783 */
1784
1785 calls = (UWord) erts_atomic_inc_read_nob(&pipe_creation_counter);
1786 erts_snprintf(pipe_name, sizeof(pipe_name),
1787 "\\\\.\\pipe\\erlang44_%d_%bpu", getpid(), calls);
1788
1789 DEBUGF(("Creating pipe %s\n", pipe_name));
1790 sa.bInheritHandle = inheritRead;
1791 if ((*phRead = CreateNamedPipe(pipe_name,
1792 PIPE_ACCESS_INBOUND |
1793 ((inheritRead && !overlapped_io) ? 0 : FILE_FLAG_OVERLAPPED),
1794 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
1795 1,
1796 0,
1797 0,
1798 2000,
1799 &sa)) == NULL) {
1800 DEBUGF(("Error creating pipe: %s\n", last_error()));
1801 return FALSE;
1802 }
1803
1804 sa.bInheritHandle = !inheritRead;
1805 if ((*phWrite = CreateFile(pipe_name,
1806 GENERIC_WRITE,
1807 0, /* No sharing */
1808 &sa,
1809 OPEN_EXISTING,
1810 FILE_ATTRIBUTE_NORMAL |
1811 ((inheritRead || overlapped_io) ? FILE_FLAG_OVERLAPPED : 0),
1812 NULL)) == INVALID_HANDLE_VALUE) {
1813 CloseHandle(*phRead);
1814 DEBUGF(("Error opening other end of pipe: %s\n", last_error()));
1815 return FALSE;
1816 }
1817 return TRUE;
1818 }
1819
application_type(const wchar_t * originalName,wchar_t wfullpath[MAX_PATH],BOOL search_in_path,BOOL handle_quotes,int * error_return)1820 static int application_type (const wchar_t *originalName, /* Name of the application to find. */
1821 wchar_t wfullpath[MAX_PATH],/* Filled with complete path to
1822 * application. */
1823 BOOL search_in_path, /* If we should search the system wide path */
1824 BOOL handle_quotes, /* If we should handle quotes around executable */
1825 int *error_return) /* A place to put an error code */
1826 {
1827 int applType, i;
1828 HANDLE hFile;
1829 wchar_t *ext, *rest;
1830 char buf[2];
1831 DWORD read;
1832 IMAGE_DOS_HEADER header;
1833 static wchar_t extensions[][5] = {L"", L".com", L".exe", L".bat"};
1834 int is_quoted;
1835 int len;
1836 wchar_t xfullpath[MAX_PATH];
1837
1838 len = wcslen(originalName);
1839 is_quoted = handle_quotes && len > 0 && originalName[0] == L'"' &&
1840 originalName[len-1] == L'"';
1841
1842 applType = APPL_NONE;
1843 *error_return = ENOENT;
1844 for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
1845 if(is_quoted) {
1846 lstrcpynW(xfullpath, originalName+1, MAX_PATH - 7); /* Cannot start using StringCchCopy yet, we support
1847 older platforms */
1848 len = wcslen(xfullpath);
1849 if(len > 0) {
1850 xfullpath[len-1] = L'\0';
1851 }
1852 } else {
1853 lstrcpynW(xfullpath, originalName, MAX_PATH - 5);
1854 }
1855 wcscat(xfullpath, extensions[i]);
1856 /* It seems that the Unicode version does not allow in and out parameter to overlap. */
1857 SearchPathW((search_in_path) ? NULL : L".", xfullpath, NULL, MAX_PATH, wfullpath, &rest);
1858
1859 /*
1860 * Ignore matches on directories or data files, return if identified
1861 * a known type.
1862 */
1863
1864 if (GetFileAttributesW(wfullpath) & FILE_ATTRIBUTE_DIRECTORY) {
1865 continue;
1866 }
1867
1868 ext = wcsrchr(wfullpath, L'.');
1869 if ((ext != NULL) && (_wcsicmp(ext, L".bat") == 0)) {
1870 *error_return = EACCES;
1871 applType = APPL_DOS;
1872 break;
1873 }
1874
1875 hFile = CreateFileW(wfullpath, GENERIC_READ, FILE_SHARE_READ, NULL,
1876 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1877 if (hFile == INVALID_HANDLE_VALUE) {
1878 continue;
1879 }
1880
1881 *error_return = EACCES; /* If considered an error,
1882 it's an access error */
1883 header.e_magic = 0;
1884 ReadFile(hFile, (void *) &header, sizeof(header), &read, NULL);
1885 if (header.e_magic != IMAGE_DOS_SIGNATURE) {
1886 /*
1887 * Doesn't have the magic number for relocatable executables. If
1888 * filename ends with .com, assume it's a DOS application anyhow.
1889 * Note that we didn't make this assumption at first, because some
1890 * supposed .com files are really 32-bit executables with all the
1891 * magic numbers and everything.
1892 */
1893
1894 CloseHandle(hFile);
1895 if ((ext != NULL) && (_wcsicmp(ext, L".com") == 0)) {
1896 applType = APPL_DOS;
1897 break;
1898 }
1899 continue;
1900 }
1901 if (header.e_lfarlc != sizeof(header)) {
1902 /*
1903 * All Windows 3.X and Win32 and some DOS programs have this value
1904 * set here. If it doesn't, assume that since it already had the
1905 * other magic number it was a DOS application.
1906 */
1907
1908 CloseHandle(hFile);
1909 applType = APPL_DOS;
1910 break;
1911 }
1912
1913 /*
1914 * The DWORD at header.e_lfanew points to yet another magic number.
1915 */
1916
1917 buf[0] = '\0';
1918 SetFilePointer(hFile, header.e_lfanew, NULL, FILE_BEGIN);
1919 ReadFile(hFile, (void *) buf, 2, &read, NULL);
1920 CloseHandle(hFile);
1921
1922 if ((buf[0] == 'L') && (buf[1] == 'E')) {
1923 applType = APPL_DOS;
1924 } else if ((buf[0] == 'N') && (buf[1] == 'E')) {
1925 applType = APPL_WIN3X;
1926 } else if ((buf[0] == 'P') && (buf[1] == 'E')) {
1927 applType = APPL_WIN32;
1928 } else {
1929 continue;
1930 }
1931 break;
1932 }
1933
1934 if (applType == APPL_NONE) {
1935 return APPL_NONE;
1936 }
1937
1938 if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) {
1939 /*
1940 * Replace long path name of executable with short path name for
1941 * 16-bit applications. Otherwise the application may not be able
1942 * to correctly parse its own command line to separate off the
1943 * application name from the arguments.
1944 */
1945
1946 GetShortPathNameW(wfullpath, wfullpath, MAX_PATH);
1947 }
1948 if (is_quoted) {
1949 /* restore quotes on quoted program name */
1950 len = wcslen(wfullpath);
1951 memmove(wfullpath+1,wfullpath,len*sizeof(wchar_t));
1952 wfullpath[0]=L'"';
1953 wfullpath[len+1]=L'"';
1954 wfullpath[len+2]=L'\0';
1955 }
1956 return applType;
1957 }
1958
1959 /*
1960 * Thread function used to emulate overlapped reading.
1961 */
1962
1963 DWORD WINAPI
threaded_reader(LPVOID param)1964 threaded_reader(LPVOID param)
1965 {
1966 AsyncIo* aio = (AsyncIo *) param;
1967 HANDLE thread = GetCurrentThread();
1968 char* buf;
1969 DWORD numToRead;
1970
1971 for (;;) {
1972 WaitForSingleObject(aio->ioAllowed, INFINITE);
1973 if (aio->flags & DF_EXIT_THREAD)
1974 break;
1975 buf = OV_BUFFER_PTR(aio);
1976 numToRead = OV_NUM_TO_READ(aio);
1977 aio->pendingError = 0;
1978 if (!ReadFile(aio->fd, buf, numToRead, &aio->bytesTransferred, NULL)) {
1979 int error = GetLastError();
1980 aio->pendingError = error;
1981 } else if (aio->flags & DF_XLAT_CR) {
1982 char *s;
1983 int n;
1984
1985 n = aio->bytesTransferred;
1986 for (s = buf; s < buf+n; s++) {
1987 if (*s == '\r') {
1988 if (s < buf + n - 1 && s[1] == '\n') {
1989 memmove(s, s+1, (buf+n - s - 1));
1990 --n;
1991 } else {
1992 *s = '\n';
1993 }
1994 }
1995 }
1996 aio->bytesTransferred = n;
1997 }
1998 SetEvent(aio->ov.hEvent);
1999 if (aio->bytesTransferred == 0) {
2000 break;
2001 }
2002 if (aio->pendingError != NO_ERROR) {
2003 break;
2004 }
2005 if (aio->flags & DF_EXIT_THREAD)
2006 break;
2007 }
2008 unrefer_driver_data(aio->dp);
2009 return 0;
2010 }
2011
2012 /*
2013 * Thread function used to emulate overlapped writing
2014 */
2015
2016 DWORD WINAPI
threaded_writer(LPVOID param)2017 threaded_writer(LPVOID param)
2018 {
2019 AsyncIo* aio = (AsyncIo *) param;
2020 HANDLE thread = GetCurrentThread();
2021 char* buf;
2022 DWORD numToWrite, handle;
2023 int ok;
2024 HANDLE handles[2];
2025 handles[0] = aio->ioAllowed;
2026 handles[1] = aio->flushEvent;
2027
2028 for (;;) {
2029 handle = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
2030 if (aio->flags & DF_EXIT_THREAD) {
2031 break;
2032 }
2033
2034 buf = OV_BUFFER_PTR(aio);
2035 numToWrite = OV_NUM_TO_READ(aio);
2036 aio->pendingError = 0;
2037
2038 if (handle == (WAIT_OBJECT_0 + 1) && numToWrite == 0) {
2039 SetEvent(aio->flushReplyEvent);
2040 aio->flags |= DF_THREAD_FLUSHED;
2041 continue;
2042 }
2043
2044 ok = WriteFile(aio->fd, buf, numToWrite, &aio->bytesTransferred, NULL);
2045 if (!ok) {
2046 aio->pendingError = GetLastError();
2047 if (aio->pendingError == ERROR_INVALID_HANDLE &&
2048 aio->flags & DF_DROP_IF_INVH) {
2049 /* This is standard error and we'we got an
2050 invalid standard error FD (non-inheritable) from parent.
2051 Just drop the message and be happy. */
2052 aio->pendingError = 0;
2053 aio->bytesTransferred = numToWrite;
2054 } else if (aio->pendingError == ERROR_NOT_ENOUGH_MEMORY) {
2055 /* This could be a console, which limits utput to 64kbytes,
2056 which might translate to less on a unicode system.
2057 Try 16k chunks and see if it works before giving up. */
2058 int done = 0;
2059 DWORD transferred;
2060 aio->pendingError = 0;
2061 aio->bytesTransferred = 0;
2062 ok = 1;
2063 while (ok && (numToWrite - done) > 0x4000) {
2064 ok = WriteFile(aio->fd, buf + done, 0x4000, &transferred, NULL);
2065 aio->bytesTransferred += transferred;
2066 done += 0x4000;
2067 }
2068 if (ok && (numToWrite - done) > 0) {
2069 ok = WriteFile(aio->fd, buf + done, (numToWrite - done),
2070 &transferred, NULL);
2071 aio->bytesTransferred += transferred;
2072 }
2073 if (!ok) {
2074 aio->pendingError = GetLastError();
2075 }
2076 }
2077 }
2078 OV_NUM_TO_READ(aio) = 0;
2079 if (handle == (WAIT_OBJECT_0 + 1))
2080 SetEvent(aio->flushReplyEvent);
2081 else
2082 SetEvent(aio->ov.hEvent);
2083 if (aio->pendingError != NO_ERROR || aio->bytesTransferred == 0)
2084 break;
2085 if (aio->flags & DF_EXIT_THREAD)
2086 break;
2087 }
2088 aio->flags |= DF_THREAD_FLUSHED;
2089 CloseHandle(aio->fd);
2090 aio->fd = INVALID_HANDLE_VALUE;
2091 unrefer_driver_data(aio->dp);
2092 return 0;
2093 }
2094
2095 static HANDLE
translate_fd(int fd)2096 translate_fd(int fd)
2097 {
2098 DWORD access;
2099 HANDLE handle;
2100
2101 switch (fd) {
2102 case 0:
2103 access = GENERIC_READ;
2104 handle = GetStdHandle(STD_INPUT_HANDLE);
2105 break;
2106 case 1:
2107 access = GENERIC_WRITE;
2108 handle = GetStdHandle(STD_OUTPUT_HANDLE);
2109 break;
2110 case 2:
2111 access = GENERIC_WRITE;
2112 handle = GetStdHandle(STD_ERROR_HANDLE);
2113 break;
2114 default:
2115 return (HANDLE)(SWord) fd;
2116 }
2117 DEBUGF(("translate_fd(%d) -> std(%p)\n", fd, (void*)handle));
2118
2119 if (handle == INVALID_HANDLE_VALUE || handle == 0) {
2120 handle = CreateFile("nul", access, 0,
2121 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2122 }
2123 DEBUGF(("translate_fd(%d) -> %p\n", fd, (void*)handle));
2124 return handle;
2125 }
2126
2127 /* Driver level locking, start function is serialized */
2128 static DriverData *save_01_port = NULL;
2129 static DriverData *save_22_port = NULL;
2130
2131 static ErlDrvData
fd_start(ErlDrvPort port_num,char * name,SysDriverOpts * opts)2132 fd_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
2133 {
2134 DriverData* dp;
2135 int is_std_error = (opts->ofd == 2);
2136 int in = opts->ifd, out = opts->ofd;
2137
2138 opts->ifd = (Uint) translate_fd(in);
2139 opts->ofd = (Uint) translate_fd(out);
2140 if ( in == 0 && out == 1 && save_01_port != NULL) {
2141 dp = save_01_port;
2142 return reuse_driver_data(dp, (HANDLE) opts->ifd, (HANDLE) opts->ofd, opts->read_write, port_num);
2143 } else if (in == 2 && out == 2 && save_22_port != NULL) {
2144 dp = save_22_port;
2145 return reuse_driver_data(dp, (HANDLE) opts->ifd, (HANDLE) opts->ofd, opts->read_write, port_num);
2146 } else {
2147 if ((dp = new_driver_data(port_num, opts->packet_bytes, 2, TRUE)) == NULL)
2148 return ERL_DRV_ERROR_GENERAL;
2149
2150 /**
2151 * Here is a brief description about how the fd driver works on windows.
2152 *
2153 * fd_init:
2154 * For each in/out fd pair a threaded_reader and threaded_writer thread is
2155 * created. Within the DriverData struct each of the threads have an AsyncIO
2156 * sctruct associated with it. Within AsyncIO there are two important HANDLEs,
2157 * ioAllowed and ov.hEvent. ioAllowed is used to signal the threaded_* threads
2158 * should read/write some data, and ov.hEvent is driver_select'ed to be used to
2159 * signal that the thread is done reading/writing.
2160 *
2161 * The reason for the driver being threaded like this is because once the FD is open
2162 * on windows, it is not possible to set the it in overlapped mode. So we have to
2163 * simulate this using threads.
2164 *
2165 * output:
2166 * When an output occurs the data to be outputted is copied to AsyncIO.ov. Then
2167 * the ioAllowed HANDLE is set, ov.hEvent is cleared and the port is marked as busy.
2168 * The threaded_writer thread is lying in WaitForMultipleObjects on ioAllowed, and
2169 * when signalled it writes all data in AsyncIO.ov and then sets ov.hEvent so that
2170 * ready_output gets triggered and (potentially) sends the reply to the port and
2171 * marks the port an non-busy.
2172 *
2173 * input:
2174 * The threaded_reader is lying waiting in ReadFile on the in fd and when a new
2175 * line is written it sets ov.hEvent that new data is available and then goes
2176 * and waits for ioAllowed to be set. ready_input is run when ov.hEvent is set and
2177 * delivers the data to the port. Then ioAllowed is signalled again and threaded_reader
2178 * goes back to ReadFile.
2179 *
2180 * shutdown:
2181 * In order to guarantee that all io is outputted before the driver is stopped,
2182 * fd_stop uses flushEvent and flushReplyEvent to make sure that there is no data
2183 * in ov which needs writing before returning from fd_stop.
2184 *
2185 **/
2186
2187 if (!create_file_thread(&dp->in, DO_READ)) {
2188 return ERL_DRV_ERROR_GENERAL;
2189 }
2190
2191 if (!create_file_thread(&dp->out, DO_WRITE)) {
2192 return ERL_DRV_ERROR_GENERAL;
2193 }
2194
2195 fd_driver_input = &(dp->in);
2196 dp->in.flags = DF_XLAT_CR;
2197 if (is_std_error) {
2198 dp->out.flags |= DF_DROP_IF_INVH; /* Just drop messages if stderror
2199 is an invalid handle */
2200 }
2201
2202 if ( in == 0 && out == 1) {
2203 save_01_port = dp;
2204 } else if (in == 2 && out == 2) {
2205 save_22_port = dp;
2206 }
2207 return set_driver_data(dp, (HANDLE) opts->ifd, (HANDLE) opts->ofd, opts->read_write,
2208 0, opts);
2209 }
2210 }
2211
fd_stop(ErlDrvData data)2212 static void fd_stop(ErlDrvData data)
2213 {
2214 DriverData * dp = (DriverData *) data;
2215 /*
2216 * There's no way we can terminate an fd port in a consistent way.
2217 * Instead we let it live until it's opened again (which it is,
2218 * as the only FD-drivers are for 0,1 and 2 adn the only time they
2219 * get closed is by init:reboot).
2220 * So - just deselect them and let everything be as is.
2221 * They get woken up in fd_start again, where the DriverData is
2222 * remembered. /PaN
2223 */
2224 if (dp->in.ov.hEvent != NULL) {
2225 (void) driver_select(dp->port_num,
2226 (ErlDrvEvent)dp->in.ov.hEvent,
2227 ERL_DRV_READ, 0);
2228 }
2229 if (dp->out.ov.hEvent != NULL) {
2230 (void) driver_select(dp->port_num,
2231 (ErlDrvEvent)dp->out.ov.hEvent,
2232 ERL_DRV_WRITE, 0);
2233 do {
2234 ASSERT(dp->out.flushEvent);
2235 SetEvent(dp->out.flushEvent);
2236 } while (WaitForSingleObject(dp->out.flushReplyEvent, 10) == WAIT_TIMEOUT
2237 && !(dp->out.flags & DF_THREAD_FLUSHED));
2238 }
2239
2240 }
2241
2242 static ErlDrvData
vanilla_start(ErlDrvPort port_num,char * name,SysDriverOpts * opts)2243 vanilla_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
2244 {
2245 HANDLE ofd,ifd;
2246 DriverData* dp;
2247 DWORD access; /* Access mode: GENERIC_READ, GENERIC_WRITE. */
2248 DWORD crFlags;
2249 HANDLE this_process = GetCurrentProcess();
2250
2251 access = 0;
2252 if (opts->read_write == DO_READ)
2253 access |= GENERIC_READ;
2254 if (opts->read_write == DO_WRITE)
2255 access |= GENERIC_WRITE;
2256
2257 if (opts->read_write == DO_READ)
2258 crFlags = OPEN_EXISTING;
2259 else if (opts->read_write == DO_WRITE)
2260 crFlags = CREATE_ALWAYS;
2261 else
2262 crFlags = OPEN_ALWAYS;
2263
2264 if ((dp = new_driver_data(port_num, opts->packet_bytes, 2, FALSE)) == NULL)
2265 return ERL_DRV_ERROR_GENERAL;
2266 ofd = CreateFile(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE,
2267 NULL, crFlags, FILE_ATTRIBUTE_NORMAL, NULL);
2268 if (!DuplicateHandle(this_process, (HANDLE) ofd,
2269 this_process, &ifd, 0,
2270 FALSE, DUPLICATE_SAME_ACCESS)) {
2271 CloseHandle(ofd);
2272 ofd = INVALID_HANDLE_VALUE;
2273 }
2274 if (ofd == INVALID_HANDLE_VALUE)
2275 return ERL_DRV_ERROR_GENERAL;
2276 return set_driver_data(dp, ifd, ofd, opts->read_write,
2277 0, opts);
2278 }
2279
2280 static void
stop(ErlDrvData data)2281 stop(ErlDrvData data)
2282 {
2283 DriverData *dp = (DriverData *) data;
2284 DEBUGF(("stop(%p)\n", dp));
2285
2286 if (dp->in.ov.hEvent != NULL) {
2287 (void) driver_select(dp->port_num,
2288 (ErlDrvEvent)dp->in.ov.hEvent,
2289 ERL_DRV_READ|ERL_DRV_USE_NO_CALLBACK, 0);
2290 }
2291 if (dp->out.ov.hEvent != NULL) {
2292 (void) driver_select(dp->port_num,
2293 (ErlDrvEvent)dp->out.ov.hEvent,
2294 ERL_DRV_WRITE|ERL_DRV_USE_NO_CALLBACK, 0);
2295 }
2296
2297 if (dp->out.thread == (HANDLE) -1 && dp->in.thread == (HANDLE) -1) {
2298 release_driver_data(dp);
2299 } else {
2300 /*
2301 * If there are read or write threads, start a thread which will
2302 * wait for them to finish.
2303 */
2304 HANDLE thread;
2305 DWORD tid;
2306
2307 /* threaded_exiter implicitly takes over refc from us... */
2308 thread = (HANDLE *) _beginthreadex(NULL, 0, threaded_exiter, dp, 0, &tid);
2309 CloseHandle(thread);
2310 }
2311 }
2312
2313 DWORD WINAPI
threaded_exiter(LPVOID param)2314 threaded_exiter(LPVOID param)
2315 {
2316 DriverData* dp = (DriverData *) param;
2317 HANDLE handles[2];
2318 int i;
2319
2320 /*
2321 * Ask the threads to terminated.
2322 *
2323 * Note that we can't reliable test the state of the ioAllowed event,
2324 * because it is an auto reset event. Therefore, always set the
2325 * exit flag and signal the event.
2326 */
2327 i = 0;
2328 if (dp->out.thread != (HANDLE) -1) {
2329 dp->out.flags |= DF_EXIT_THREAD;
2330 SetEvent(dp->out.ioAllowed);
2331 handles[i++] = dp->out.thread;
2332 }
2333 if (dp->in.thread != (HANDLE) -1) {
2334 dp->in.flags |= DF_EXIT_THREAD;
2335 SetEvent(dp->in.ioAllowed);
2336 handles[i++] = dp->in.thread;
2337 }
2338
2339 /*
2340 * If we were lucky, the following happened above:
2341 * 1) The output thread terminated (and closed the pipe).
2342 * 2) As a consequence of that, the port program received
2343 * EOF on its standard input.
2344 * 3) Hopefully, because of (2), the port program terminated.
2345 * 4) Because of (3), the input thread terminated.
2346 *
2347 * But this might need some time; therefore, we must wait for
2348 * both threads to terminate.
2349 */
2350
2351 if (i > 0) {
2352 switch (WaitForMultipleObjects(i, handles, TRUE, 5000)) {
2353 case WAIT_TIMEOUT:
2354 DEBUGF(("Timeout waiting for %d threads failed\n", i));
2355 break;
2356 case WAIT_FAILED:
2357 DEBUGF(("Wait for %d threads failed: %s\n",
2358 i, win32_errorstr(GetLastError())));
2359 break;
2360 default:
2361 break;
2362 }
2363 }
2364
2365 /*
2366 * Wait for threads to terminate didn't help. Now use some force.
2367 * TerminateThread() is *not* a good idea, because it doesn't clean
2368 * up the thread's stack.
2369 *
2370 * Instead we well terminate the port program and wait for the
2371 * threads to terminate themselves when they receive end of file.
2372 */
2373
2374 if (dp->out.thread != (HANDLE) -1) {
2375 int error;
2376
2377 if (WaitForSingleObject(dp->out.thread, 0) == WAIT_OBJECT_0) {
2378 CloseHandle(dp->out.thread);
2379 dp->out.thread = (HANDLE) -1;
2380 } else if (dp->port_pid != INVALID_HANDLE_VALUE) {
2381 DEBUGF(("Killing port process 0x%x (output thread)\n", dp->port_pid));
2382 TerminateProcess(dp->port_pid, 0);
2383 if (!CloseHandle(dp->port_pid))
2384 DEBUGF(("Failed to close output handle!!!\n"));
2385 dp->port_pid = INVALID_HANDLE_VALUE;
2386 DEBUGF(("Waiting for output thread 0x%x to finish\n", dp->out.thread));
2387 error = WaitForSingleObject(dp->out.thread, INFINITE);
2388 }
2389 }
2390
2391 if (dp->in.thread != (HANDLE) -1) {
2392 if (WaitForSingleObject(dp->in.thread, 0) == WAIT_OBJECT_0) {
2393 CloseHandle(dp->in.thread);
2394 dp->in.thread = (HANDLE) -1;
2395 } else if (dp->port_pid != INVALID_HANDLE_VALUE) {
2396 DEBUGF(("Killing port process 0x%x (input thread)\n", dp->port_pid));
2397 TerminateProcess(dp->port_pid, 0);
2398 if (!CloseHandle(dp->port_pid))
2399 DEBUGF(("Failed to close input handle!!!\n"));
2400 dp->port_pid = INVALID_HANDLE_VALUE;
2401
2402 DEBUGF(("Waiting for input thread 0x%x to finish\n", dp->in.thread));
2403 switch (WaitForSingleObject(dp->in.thread, INFINITE)) {
2404 case WAIT_OBJECT_0:
2405 CloseHandle(dp->in.thread);
2406 dp->in.thread = (HANDLE) -1;
2407 break;
2408 default:
2409 DEBUGF(("Wait for input thread to finish failed: %s\n",
2410 win32_errorstr(GetLastError())));
2411 break;
2412 }
2413 }
2414 }
2415
2416 release_driver_data(dp);
2417 return 0;
2418 }
2419
2420 /* ----------------------------------------------------------------------
2421 * output --
2422 * Outputs data from Erlang to the port program.
2423 *
2424 * Results:
2425 * Returns the actual number of bytes written (including the
2426 * packet header) or -1 if an error occurred.
2427 * ----------------------------------------------------------------------
2428 */
2429
2430 static void
output(ErlDrvData drv_data,char * buf,ErlDrvSizeT len)2431 output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len)
2432 /* ErlDrvData drv_data; /* The slot to use in the driver data table.
2433 * For Windows NT, this is *NOT* a file handle.
2434 * The handle is found in the driver data.
2435 */
2436 /* char *buf; /* Pointer to data to write to the port program. */
2437 /* ErlDrvSizeT len; /* Number of bytes to write. */
2438 {
2439 DriverData* dp = (DriverData *) drv_data;
2440 int pb; /* The header size for this port. */
2441 char* current;
2442 ErlDrvSizeT qsz, sz;
2443 ErlDrvBinary *bin;
2444
2445 pb = dp->packet_bytes;
2446
2447 if ((pb+len) == 0)
2448 return ; /* 0; */
2449
2450 /*
2451 * Check that the message can be sent with given header length.
2452 */
2453
2454 if ((pb == 2 && len > 65535) || (pb == 1 && len > 255)) {
2455 driver_failure_posix(dp->port_num, EINVAL);
2456 return ; /* -1; */
2457 }
2458
2459 /*
2460 * Allocate memory for both the message and the header.
2461 */
2462
2463 sz = pb+len;
2464 bin = driver_alloc_binary(sz);
2465 if (!bin) {
2466 driver_failure_posix(dp->port_num, ENOMEM);
2467 return ; /* -1; */
2468 }
2469
2470 /*
2471 * Store header bytes (if any).
2472 */
2473
2474 current = bin->orig_bytes;
2475
2476 switch (pb) {
2477 case 4:
2478 *current++ = (len >> 24) & 255;
2479 *current++ = (len >> 16) & 255;
2480 case 2:
2481 *current++ = (len >> 8) & 255;
2482 case 1:
2483 *current++ = len & 255;
2484 }
2485
2486 /*
2487 * Start the write.
2488 */
2489
2490 if (len)
2491 memcpy(current, buf, len);
2492
2493 qsz = driver_sizeq(dp->port_num);
2494
2495 if (qsz > 0) {
2496 driver_enq_bin(dp->port_num, bin, 0, sz);
2497 qsz += pb+len;
2498 }
2499 else {
2500 ASSERT(!dp->outbuf);
2501 dp->outbuf = bin->orig_bytes;
2502 dp->outBufSize = sz;
2503 if (!async_write_file(&dp->out, dp->outbuf, sz)) {
2504 driver_enq_bin(dp->port_num, bin, 0, sz);
2505 qsz = sz;
2506 } else {
2507 dp->out.ov.Offset += pb+len; /* For vanilla driver. */
2508 /* XXX OffsetHigh should be changed too. */
2509 dp->outBufSize = 0;
2510 dp->outbuf = NULL;
2511 }
2512 }
2513
2514 if (!dp->busy && qsz >= dp->high_watermark)
2515 set_busy_port(dp->port_num, (dp->busy = !0));
2516
2517 /* Binary either handled or buffered */
2518 driver_free_binary(bin);
2519
2520 /*return 0;*/
2521 }
2522
2523
2524 /* ----------------------------------------------------------------------
2525 * ready_input --
2526 * This function is called (indirectly) from check_io() when an
2527 * event object has been signaled, indicating that there is
2528 * something to read on the corresponding file handle.
2529 *
2530 * If the port is working in the continuous stream mode (packet_bytes == 0),
2531 * whatever data read will be sent straight to Erlang.
2532 *
2533 * Results:
2534 * Always 0.
2535 * ----------------------------------------------------------------------
2536 */
2537
2538 static void
ready_input(ErlDrvData drv_data,ErlDrvEvent ready_event)2539 ready_input(ErlDrvData drv_data, ErlDrvEvent ready_event)
2540 /* long drv_data; /* Driver data. */
2541 /* HANDLE ready_event; /* The handle for the ready event. */
2542 {
2543 int error = 0; /* The error code (assume initially no errors). */
2544 DWORD bytesRead; /* Number of bytes read. */
2545 DriverData* dp = (DriverData *) drv_data;
2546 int pb;
2547
2548 pb = dp->packet_bytes;
2549 if(dp->in.thread == (HANDLE) -1) {
2550 dp->in.async_io_active = 0;
2551 }
2552 DEBUGF(("ready_input: dp %p, event 0x%x\n", dp, ready_event));
2553
2554 /*
2555 * Evaluate the result of the overlapped read.
2556 */
2557
2558 #ifdef HARD_POLL_DEBUG
2559 poll_debug_read_begin(dp->in.ov.hEvent);
2560 #endif
2561
2562 error = get_overlapped_result(&dp->in, &bytesRead, TRUE);
2563
2564 #ifdef HARD_POLL_DEBUG
2565 poll_debug_read_done(dp->in.ov.hEvent,bytesRead);
2566 #endif
2567
2568 if (error == NO_ERROR) {
2569 if (pb == 0) { /* Continuous stream. */
2570 #ifdef DEBUG
2571 DEBUGF(("ready_input: %d: ", bytesRead));
2572 erl_bin_write(dp->inbuf, 16, bytesRead);
2573 DEBUGF(("\n"));
2574 #endif
2575 driver_output(dp->port_num, dp->inbuf, bytesRead);
2576 } else { /* Packet mode */
2577 dp->bytesInBuffer += bytesRead;
2578
2579 /*
2580 * Loop until we've exhausted the data in the buffer.
2581 */
2582
2583 for (;;) {
2584
2585 /*
2586 * Check for completion of a header read.
2587 */
2588
2589 if (dp->bytesInBuffer >= dp->totalNeeded &&
2590 dp->totalNeeded == pb) {
2591
2592 /*
2593 * We have successfully read the packet header
2594 * (and perhaps even the packet). Get the packet size
2595 * from the header and update dp->totalNeeded to include
2596 * the packet size.
2597 */
2598
2599 int packet_size = 0;
2600 unsigned char *header = (unsigned char *) dp->inbuf;
2601
2602 switch (pb) {
2603 case 4:
2604 packet_size = (packet_size << 8) | *header++;
2605 packet_size = (packet_size << 8) | *header++;
2606 case 2:
2607 packet_size = (packet_size << 8) | *header++;
2608 case 1:
2609 packet_size = (packet_size << 8) | *header++;
2610 }
2611
2612 dp->totalNeeded += packet_size;
2613
2614 /*
2615 * Make sure that the receive buffer is big enough.
2616 */
2617
2618 if (dp->inBufSize < dp->totalNeeded) {
2619 char* new_buf;
2620
2621 new_buf = DRV_BUF_REALLOC(dp->inbuf, dp->totalNeeded);
2622 if (new_buf == NULL) {
2623 error = ERROR_NOT_ENOUGH_MEMORY;
2624 break; /* Break out of loop into error handler. */
2625 }
2626 ASSERT(erts_atomic_read_nob(&sys_misc_mem_sz) >= dp->inBufSize);
2627 erts_atomic_add_nob(&sys_misc_mem_sz,
2628 dp->totalNeeded - dp->inBufSize);
2629 dp->inBufSize = dp->totalNeeded;
2630 dp->inbuf = new_buf;
2631 }
2632 }
2633
2634 /*
2635 * Check for completion of a packet read.
2636 */
2637
2638 if (dp->bytesInBuffer < dp->totalNeeded) {
2639 /*
2640 * Not enough bytes in the buffer. Break out of
2641 * the loop and initiate a new read.
2642 */
2643
2644 break;
2645 } else {
2646
2647 /*
2648 * We have successfully read a complete packet, which
2649 * can be passed to Erlang.
2650 */
2651
2652 driver_output(dp->port_num, dp->inbuf+pb, dp->totalNeeded-pb);
2653
2654 /*
2655 * Update the number of bytes remaining in the buffer,
2656 * and move the data remaining (if any) to the beginning
2657 * of the buffer.
2658 */
2659
2660 dp->bytesInBuffer -= dp->totalNeeded;
2661 if (dp->bytesInBuffer > 0) {
2662 memmove(dp->inbuf, dp->inbuf+dp->totalNeeded,
2663 dp->bytesInBuffer);
2664 }
2665
2666 /*
2667 * Indicate that we need the size of a header, and
2668 * go through the loop once more (to either process
2669 * remaining bytes or initiate reading more).
2670 */
2671
2672 dp->totalNeeded = pb;
2673 }
2674 }
2675 }
2676 }
2677
2678 /*
2679 * Start a new overlapped read, or report the error.
2680 */
2681
2682 if (error == NO_ERROR) {
2683 async_read_file(&dp->in, dp->inbuf+dp->bytesInBuffer,
2684 dp->inBufSize - dp->bytesInBuffer);
2685 } else {
2686 DEBUGF(("ready_input(): error: %s\n", win32_errorstr(error)));
2687 if (error == ERROR_BROKEN_PIPE || error == ERROR_HANDLE_EOF) {
2688 /* Maybe check exit status */
2689 if (dp->report_exit) {
2690 DWORD exitcode;
2691 if (GetExitCodeProcess(dp->port_pid, &exitcode) &&
2692 exitcode != STILL_ACTIVE) {
2693 driver_report_exit(dp->port_num, exitcode);
2694 }
2695 }
2696 driver_failure_eof(dp->port_num);
2697 } else { /* Report real errors. */
2698 int error = GetLastError();
2699
2700 (void) driver_select(dp->port_num, ready_event, ERL_DRV_READ, 0);
2701 _dosmaperr(error);
2702 driver_failure_posix(dp->port_num, errno);
2703 }
2704 }
2705
2706 /*return 0;*/
2707 }
2708
2709 static void
ready_output(ErlDrvData drv_data,ErlDrvEvent ready_event)2710 ready_output(ErlDrvData drv_data, ErlDrvEvent ready_event)
2711 {
2712 DWORD bytesWritten;
2713 DriverData *dp = (DriverData *) drv_data;
2714 int error;
2715 ErlDrvSizeT qsz;
2716
2717 if(dp->out.thread == (HANDLE) -1) {
2718 dp->out.async_io_active = 0;
2719 }
2720 DEBUGF(("ready_output(%p, 0x%x)\n", drv_data, ready_event));
2721 if (!dp->outbuf) {
2722 /* Happens because event sometimes get signalled during a successful
2723 write... */
2724 return;
2725 }
2726
2727 qsz = driver_deq(dp->port_num, dp->outBufSize);
2728 dp->outBufSize = 0;
2729 dp->outbuf = NULL;
2730 #ifdef HARD_POLL_DEBUG
2731 poll_debug_write_begin(dp->out.ov.hEvent);
2732 #endif
2733 error = get_overlapped_result(&dp->out, &bytesWritten, TRUE);
2734 #ifdef HARD_POLL_DEBUG
2735 poll_debug_write_done(dp->out.ov.hEvent,bytesWritten);
2736 #endif
2737
2738 if (error != NO_ERROR) {
2739 (void) driver_select(dp->port_num, ready_event, ERL_DRV_WRITE, 0);
2740 _dosmaperr(error);
2741 driver_failure_posix(dp->port_num, errno);
2742 return;
2743 }
2744
2745 dp->out.ov.Offset += bytesWritten; /* For vanilla driver. */
2746
2747 while (qsz > 0) {
2748 int vsize;
2749 SysIOVec *iov = driver_peekq(dp->port_num, &vsize);
2750 ASSERT(iov->iov_base && iov->iov_len);
2751 dp->outbuf = iov->iov_base;
2752 dp->outBufSize = iov->iov_len;
2753 if (!async_write_file(&dp->out, dp->outbuf, dp->outBufSize))
2754 break;
2755 dp->out.ov.Offset += dp->outBufSize; /* For vanilla driver. */
2756 /* XXX OffsetHigh should be changed too. */
2757 qsz = driver_deq(dp->port_num, dp->outBufSize);
2758 dp->outbuf = NULL;
2759 dp->outBufSize = 0;
2760 }
2761
2762 if (dp->busy && qsz < dp->low_watermark)
2763 set_busy_port(dp->port_num, (dp->busy = 0));
2764 }
2765
stop_select(ErlDrvEvent e,void * _)2766 static void stop_select(ErlDrvEvent e, void* _)
2767 {
2768 CloseHandle((HANDLE)e);
2769 }
2770
2771 /* Fills in the systems representation of the beam process identifier.
2772 ** The Pid is put in STRING representation in the supplied buffer,
2773 ** no interpretation of this should be done by the rest of the
2774 ** emulator. The buffer should be at least 21 bytes long.
2775 */
sys_get_pid(char * buffer,size_t buffer_size)2776 void sys_get_pid(char *buffer, size_t buffer_size){
2777 DWORD p = GetCurrentProcessId();
2778 /* The pid is scalar and is an unsigned long. */
2779 erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p);
2780 }
2781
2782 void
sys_init_io(void)2783 sys_init_io(void)
2784 {
2785
2786 /* Now heres an icky one... This is called before drivers are, so we
2787 can change our view of the number of open files possible.
2788 We estimate the number to twice the amount of ports.
2789 We really dont know on windows, do we? */
2790 max_files = 2*erts_ptab_max(&erts_port);
2791 }
2792
2793 void
erts_sys_main_thread(void)2794 erts_sys_main_thread(void)
2795 {
2796 HANDLE dummy;
2797 #ifdef ERTS_ENABLE_LOCK_CHECK
2798 erts_lc_set_thread_name("parent_thread");
2799 #endif
2800 dummy = CreateEvent(NULL, FALSE, FALSE, NULL);
2801 for(;;) {
2802 WaitForSingleObject(dummy, INFINITE);
2803 }
2804 }
2805
erts_sys_alloc_init(void)2806 void erts_sys_alloc_init(void)
2807 {
2808 }
2809
erts_sys_alloc(ErtsAlcType_t t,void * x,Uint sz)2810 void *erts_sys_alloc(ErtsAlcType_t t, void *x, Uint sz)
2811 {
2812 return malloc((size_t) sz);
2813 }
2814
erts_sys_realloc(ErtsAlcType_t t,void * x,void * p,Uint sz)2815 void *erts_sys_realloc(ErtsAlcType_t t, void *x, void *p, Uint sz)
2816 {
2817 return realloc(p, (size_t) sz);
2818 }
2819
erts_sys_free(ErtsAlcType_t t,void * x,void * p)2820 void erts_sys_free(ErtsAlcType_t t, void *x, void *p)
2821 {
2822 free(p);
2823 }
2824
erts_sys_aligned_alloc(UWord alignment,UWord size)2825 void *erts_sys_aligned_alloc(UWord alignment, UWord size)
2826 {
2827 void *ptr;
2828 ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */
2829 ptr = _aligned_malloc((size_t) size, (size_t) alignment);
2830 ASSERT(!ptr || (((UWord) ptr) & (alignment - 1)) == 0);
2831 return ptr;
2832 }
2833
erts_sys_aligned_free(UWord alignment,void * ptr)2834 void erts_sys_aligned_free(UWord alignment, void *ptr)
2835 {
2836 ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */
2837 _aligned_free(ptr);
2838 }
2839
erts_sys_aligned_realloc(UWord alignment,void * ptr,UWord size,UWord old_size)2840 void *erts_sys_aligned_realloc(UWord alignment, void *ptr, UWord size, UWord old_size)
2841 {
2842 void *new_ptr;
2843 ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */
2844 new_ptr = _aligned_realloc(ptr, (size_t) size, (size_t) alignment);
2845 ASSERT(!new_ptr || (((UWord) new_ptr) & (alignment - 1)) == 0);
2846 return new_ptr;
2847 }
2848
2849 static Preload* preloaded = NULL;
2850 static unsigned* res_name = NULL;
2851 static int num_preloaded = 0;
2852
2853 /* Return a pointer to a vector of names of preloaded modules */
2854
sys_preloaded(void)2855 Preload* sys_preloaded(void)
2856 {
2857 HRSRC hRes;
2858 unsigned char* data;
2859
2860 #define GETWORD(p) (0[p] | 1[p] << 8)
2861 #define GETDWORD(p) (GETWORD(p) | GETWORD(p+2) << 16)
2862
2863
2864 if (preloaded == NULL) {
2865 int i;
2866 ASSERT(beam_module != NULL);
2867 hRes = FindResource(beam_module, 0, "ERLANG_DICT");
2868 /* We might have a resource compiler laying out the 0 resource with
2869 "0" as a textual name instead... */
2870 if (hRes == NULL) {
2871 hRes = FindResource(beam_module, "0", "ERLANG_DICT");
2872 }
2873 if (hRes == NULL) {
2874 DWORD n = GetLastError();
2875 fprintf(stderr, "No ERLANG_DICT resource\n");
2876 exit(1);
2877 }
2878 data = (unsigned char *) LoadResource(beam_module, hRes);
2879
2880 num_preloaded = GETWORD(data);
2881 if (num_preloaded == 0) {
2882 fprintf(stderr, "No preloaded modules\n");
2883 exit(1);
2884 }
2885
2886 data += 2;
2887 preloaded = erts_alloc(ERTS_ALC_T_PRELOADED,
2888 (num_preloaded+1)*sizeof(Preload));
2889 res_name = erts_alloc(ERTS_ALC_T_PRELOADED,
2890 (num_preloaded+1)*sizeof(unsigned));
2891 erts_atomic_add_nob(&sys_misc_mem_sz,
2892 (num_preloaded+1)*sizeof(Preload)
2893 + (num_preloaded+1)*sizeof(unsigned));
2894 for (i = 0; i < num_preloaded; i++) {
2895 int n;
2896
2897 preloaded[i].size = GETDWORD(data);
2898 data += 4;
2899 res_name[i] = GETWORD(data);
2900 data += 2;
2901 n = GETWORD(data);
2902 data += 2;
2903 preloaded[i].name = erts_alloc(ERTS_ALC_T_PRELOADED, n+1);
2904 erts_atomic_add_nob(&sys_misc_mem_sz, n+1);
2905 sys_memcpy(preloaded[i].name, data, n);
2906 preloaded[i].name[n] = '\0';
2907 data += n;
2908 DEBUGF(("name: %s; size: %d; resource: %p\n",
2909 preloaded[i].name, preloaded[i].size, res_name[i]));
2910 }
2911 preloaded[i].name = NULL;
2912 }
2913
2914 #undef GETWORD
2915 #undef GETDWORD
2916 return preloaded;
2917 }
2918
2919 /* Return a pointer to preloaded code for module "module" */
sys_preload_begin(Preload * pp)2920 unsigned char* sys_preload_begin(Preload* pp)
2921 {
2922 HRSRC hRes;
2923 unsigned resource;
2924
2925 ASSERT(beam_module != NULL);
2926
2927 resource = res_name[pp-preloaded];
2928 DEBUGF(("Loading name: %s; size: %d; resource: %u\n",
2929 pp->name, pp->size, resource));
2930 hRes = FindResource(beam_module, (char*)(UWord) resource, "ERLANG_CODE");
2931 return pp->code = LoadResource(beam_module, hRes);
2932 }
2933
2934 /* Clean up if allocated */
sys_preload_end(Preload * pp)2935 void sys_preload_end(Preload* pp)
2936 {
2937 }
2938
2939 /* Read a key from console */
2940
2941 int
sys_get_key(int fd)2942 sys_get_key(int fd)
2943 {
2944 ASSERT(fd == 0);
2945
2946 if (win_console) {
2947 return ConGetKey();
2948 }
2949
2950 /*
2951 * Black magic follows. (Code stolen from get_overlapped_result())
2952 */
2953
2954 if (fd_driver_input != NULL && fd_driver_input->thread != (HANDLE)-1) {
2955 DWORD error;
2956 int key;
2957
2958 error = WaitForSingleObject(fd_driver_input->ov.hEvent, INFINITE);
2959 if (error == WAIT_OBJECT_0) {
2960 if (fd_driver_input->bytesTransferred > 0) {
2961 int n;
2962 int i;
2963 char* buf = OV_BUFFER_PTR(fd_driver_input);
2964
2965 fd_driver_input->bytesTransferred--;
2966 n = fd_driver_input->bytesTransferred;
2967 key = buf[0];
2968 for (i = n; i > 0; i--) {
2969 buf[i-1] = buf[i];
2970 }
2971 return key;
2972 }
2973 }
2974 }
2975 return '*'; /* Error! */
2976 }
2977
2978 /*
2979 * Returns a human-readable description of the last error.
2980 * The returned pointer will be valid only as long as last-error()
2981 * isn't called again.
2982 */
2983
win32_errorstr(int error)2984 char* win32_errorstr(int error)
2985 {
2986 LPTSTR lpBufPtr = erts_tsd_get(win32_errstr_key);
2987 if (lpBufPtr) {
2988 LocalFree(lpBufPtr);
2989 }
2990 FormatMessage(
2991 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
2992 FORMAT_MESSAGE_IGNORE_INSERTS,
2993 NULL,
2994 error,
2995 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
2996 (LPTSTR) &lpBufPtr,
2997 0,
2998 NULL);
2999 SetLastError(error);
3000 erts_tsd_set(win32_errstr_key,lpBufPtr);
3001 return lpBufPtr;
3002 }
3003
last_error(void)3004 char* last_error(void)
3005 {
3006 return win32_errorstr(GetLastError());
3007 }
3008
sys_func_memzero(void * s,size_t n)3009 static void* sys_func_memzero(void* s, size_t n)
3010 {
3011 return sys_memzero(s, n);
3012 }
3013
3014 #ifdef DEBUG
3015 static HANDLE hDebugWrite = INVALID_HANDLE_VALUE;
3016
erl_debug(char * fmt,...)3017 void erl_debug(char *fmt,...)
3018 {
3019 char sbuf[1024]; /* Temporary buffer. */
3020 DWORD written; /* Actual number of chars written. */
3021 va_list va;
3022
3023 if (hDebugWrite != INVALID_HANDLE_VALUE) {
3024 va_start(va, fmt);
3025 vsprintf(sbuf, fmt, va);
3026 WriteFile(hDebugWrite, sbuf, strlen(sbuf), &written, NULL);
3027 va_end(va);
3028 }
3029 }
3030
debug_console(void)3031 static void debug_console(void)
3032 {
3033 HANDLE hRead; /* Handle to read end of pipe. */
3034 SECURITY_ATTRIBUTES sa;
3035 PROCESS_INFORMATION procInfo;
3036 STARTUPINFO startInfo;
3037 BOOL ok;
3038
3039 /*
3040 * Create a pipe for communicating with the sub process.
3041 */
3042
3043 sa.nLength = sizeof(sa);
3044 sa.lpSecurityDescriptor = NULL;
3045 sa.bInheritHandle = TRUE;
3046 if (!CreatePipe(&hRead, &hDebugWrite, &sa, 0)) {
3047 fprintf(stderr, "Failed to create pipe: %d\n",
3048 GetLastError());
3049 exit(1);
3050 }
3051
3052 startInfo.cb = sizeof(STARTUPINFO);
3053 startInfo.lpTitle = "Erlang Debug Log";
3054 startInfo.lpReserved = NULL;
3055 startInfo.lpReserved2 = NULL;
3056 startInfo.cbReserved2 = 0;
3057 startInfo.lpDesktop = NULL;
3058 startInfo.dwFlags = STARTF_USESTDHANDLES;
3059 startInfo.hStdInput = hRead;
3060
3061 /* The following handles are not intended to be used. */
3062 startInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
3063 startInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
3064
3065 ok = CreateProcess(NULL,
3066 "erl_log.exe", /* Application */
3067 NULL, /* Process security attributes. */
3068 NULL, /* Thread security attributes. */
3069 TRUE, /* Handle inheritance flag. */
3070 CREATE_NEW_CONSOLE, /* Flags. */
3071 NULL, /* Environment. */
3072 NULL, /* Current directory. */
3073 &startInfo,/* Startup info. */
3074 &procInfo /* Process information. */
3075 );
3076
3077 CloseHandle(hRead);
3078
3079 if (ok) {
3080 /*
3081 * Since we don't use these, close them at once to avoid a resource
3082 * leak.
3083 */
3084 CloseHandle(procInfo.hProcess);
3085 CloseHandle(procInfo.hThread);
3086 } else {
3087 fprintf(stderr, "Create process failed: %s\n", last_error());
3088 exit(1);
3089 }
3090 }
3091
3092 void
erl_bin_write(buf,sz,max)3093 erl_bin_write(buf, sz, max)
3094 unsigned char* buf;
3095 int sz;
3096 int max;
3097 {
3098 int i, imax;
3099 char comma[5] = ",";
3100
3101 if (hDebugWrite == INVALID_HANDLE_VALUE)
3102 return;
3103
3104 if (!sz)
3105 return;
3106 if (sz > max)
3107 imax = max;
3108 else
3109 imax = sz;
3110
3111 for (i=0; i<imax; i++) {
3112 if (i == imax-1) {
3113 if (sz > max)
3114 strcpy(comma, ",...");
3115 else
3116 comma[0] = 0;
3117 }
3118 if (isdigit(buf[i]))
3119 erl_debug("%u%s", (int)(buf[i]), comma);
3120 else {
3121 if (isalpha(buf[i])) {
3122 erl_debug("%c%s", buf[i], comma);
3123 }
3124 else
3125 erl_debug("%u%s", (int)(buf[i]), comma);
3126 }
3127 }
3128 }
3129
3130 #endif /* DEBUG */
3131
3132 void
erl_assert_error(const char * expr,const char * func,const char * file,int line)3133 erl_assert_error(const char* expr, const char* func, const char* file, int line)
3134 {
3135 char message[1024];
3136
3137 erts_snprintf(message, sizeof(message),
3138 "File %hs, line %d: %hs", file, line, expr);
3139 MessageBox(GetActiveWindow(), message, "Assertion failed",
3140 MB_OK | MB_ICONERROR);
3141 #if 0
3142 erl_crash_dump(file, line, "Assertion failed: %hs\n", expr);
3143 #endif
3144 DebugBreak();
3145 }
3146
3147
3148 static void
check_supported_os_version(void)3149 check_supported_os_version(void)
3150 {
3151 #if defined(_WIN32_WINNT)
3152 {
3153 DWORD major = (_WIN32_WINNT >> 8) & 0xff;
3154 DWORD minor = _WIN32_WINNT & 0xff;
3155
3156 if (int_os_version.dwPlatformId != VER_PLATFORM_WIN32_NT
3157 || int_os_version.dwMajorVersion < major
3158 || (int_os_version.dwMajorVersion == major
3159 && int_os_version.dwMinorVersion < minor))
3160 erts_exit(1,
3161 "Windows version not supported "
3162 "(min required: winnt %d.%d)\n",
3163 major, minor);
3164 }
3165 #else
3166 erts_exit(1,
3167 "Windows version not supported "
3168 "(min required: win %d.%d)\n",
3169 nt_major, nt_minor);
3170 #endif
3171 }
3172
3173
3174 typedef struct {
3175 int sched_bind_data;
3176 } erts_thr_create_data_t;
3177
3178 /*
3179 * thr_create_prepare() is called in parent thread before thread creation.
3180 * Returned value is passed as argument to thr_create_cleanup().
3181 */
3182 static void *
thr_create_prepare(void)3183 thr_create_prepare(void)
3184 {
3185 erts_thr_create_data_t *tcdp;
3186
3187 tcdp = erts_alloc(ERTS_ALC_T_TMP, sizeof(erts_thr_create_data_t));
3188 tcdp->sched_bind_data = erts_sched_bind_atthrcreate_prepare();
3189
3190 return (void *) tcdp;
3191 }
3192
3193
3194 /* thr_create_cleanup() is called in parent thread after thread creation. */
3195 static void
thr_create_cleanup(void * vtcdp)3196 thr_create_cleanup(void *vtcdp)
3197 {
3198 erts_thr_create_data_t *tcdp = (erts_thr_create_data_t *) vtcdp;
3199
3200 erts_sched_bind_atthrcreate_parent(tcdp->sched_bind_data);
3201
3202 erts_free(ERTS_ALC_T_TMP, tcdp);
3203 }
3204
3205 static void
thr_create_prepare_child(void * vtcdp)3206 thr_create_prepare_child(void *vtcdp)
3207 {
3208 erts_thr_create_data_t *tcdp = (erts_thr_create_data_t *) vtcdp;
3209
3210 #ifdef ERTS_ENABLE_LOCK_COUNT
3211 erts_lcnt_thread_setup();
3212 #endif /* ERTS_ENABLE_LOCK_COUNT */
3213
3214 erts_sched_bind_atthrcreate_child(tcdp->sched_bind_data);
3215 }
3216
erts_sys_scheduler_init(void)3217 void erts_sys_scheduler_init(void) {
3218 /* Nothing needed on Windows. */
3219 }
3220
3221 void
erts_sys_pre_init(void)3222 erts_sys_pre_init(void)
3223 {
3224 erts_thr_init_data_t eid = ERTS_THR_INIT_DATA_DEF_INITER;
3225 int_os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
3226 GetVersionEx(&int_os_version);
3227 check_supported_os_version();
3228
3229 eid.thread_create_child_func = thr_create_prepare_child;
3230 /* Before creation in parent */
3231 eid.thread_create_prepare_func = thr_create_prepare;
3232 /* After creation in parent */
3233 eid.thread_create_parent_func = thr_create_cleanup;
3234
3235 #ifdef ERTS_ENABLE_LOCK_COUNT
3236 erts_lcnt_pre_thr_init();
3237 #endif
3238
3239 erts_thr_init(&eid);
3240
3241 #ifdef ERTS_ENABLE_LOCK_COUNT
3242 erts_lcnt_post_thr_init();
3243 #endif
3244
3245 #ifdef ERTS_ENABLE_LOCK_CHECK
3246 erts_lc_init();
3247 #endif
3248 #ifdef ERTS_DYN_LOCK_CHECK
3249 erts_dlc_init();
3250 #endif
3251
3252 erts_init_sys_time_sup();
3253
3254 erts_atomic_init_nob(&sys_misc_mem_sz, 0);
3255 }
3256
noinherit_std_handle(DWORD type)3257 void noinherit_std_handle(DWORD type)
3258 {
3259 HANDLE h = GetStdHandle(type);
3260 if (h != 0 && h != INVALID_HANDLE_VALUE) {
3261 SetHandleInformation(h,HANDLE_FLAG_INHERIT,0);
3262 }
3263 }
3264
3265
erl_sys_init(void)3266 void erl_sys_init(void)
3267 {
3268 HANDLE handle;
3269
3270 noinherit_std_handle(STD_OUTPUT_HANDLE);
3271 noinherit_std_handle(STD_INPUT_HANDLE);
3272 noinherit_std_handle(STD_ERROR_HANDLE);
3273
3274 erts_tsd_key_create(&win32_errstr_key,"win32_errstr_key");
3275 InitializeCriticalSection(&htbc_lock);
3276 erts_atomic_init_nob(&pipe_creation_counter,0);
3277 /*
3278 * Test if we have named pipes or not.
3279 */
3280
3281 switch (int_os_version.dwPlatformId) {
3282 case VER_PLATFORM_WIN32_WINDOWS:
3283 DEBUGF(("Running on Windows 95"));
3284 use_named_pipes = FALSE;
3285 break;
3286 case VER_PLATFORM_WIN32_NT:
3287 DEBUGF(("Running on Windows NT"));
3288 #ifdef DISABLE_NAMED_PIPES
3289 use_named_pipes = FALSE;
3290 #else
3291 use_named_pipes = TRUE;
3292 #endif
3293 break;
3294 default: /* Unsupported platform. */
3295 exit(1);
3296 }
3297 DEBUGF((" %d.%d, build %d, %s\n",
3298 int_os_version.dwMajorVersion, int_os_version.dwMinorVersion,
3299 int_os_version.dwBuildNumber, int_os_version.szCSDVersion));
3300
3301 ASSERT(beam_module != NULL);
3302 init_console();
3303
3304 /*
3305 * The following makes sure that the current directory for the current drive
3306 * is remembered (in the environment).
3307 */
3308
3309 chdir(".");
3310
3311 /*
3312 * Make sure that the standard error handle is valid.
3313 */
3314 handle = GetStdHandle(STD_ERROR_HANDLE);
3315 if (handle == INVALID_HANDLE_VALUE || handle == 0) {
3316 SetStdHandle(STD_ERROR_HANDLE, GetStdHandle(STD_OUTPUT_HANDLE));
3317 }
3318 erts_sys_init_float();
3319
3320 /* Suppress windows error message popups */
3321 SetErrorMode(SetErrorMode(0) |
3322 SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
3323 }
3324 void erts_poll_late_init(void);
3325
3326 void
erl_sys_late_init(void)3327 erl_sys_late_init(void)
3328 {
3329 /* do nothing */
3330 erts_poll_late_init();
3331 }
3332