PageRenderTime 59ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/erts/emulator/sys/win32/sys.c

https://github.com/bsmr-erlang/otp
C | 3291 lines | 2901 code | 167 blank | 223 comment | 209 complexity | 42bf62a5a9ccf009101fd19c86970b87 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * %CopyrightBegin%
  3. *
  4. * Copyright Ericsson AB 1996-2017. 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. #ifdef HAVE_CONFIG_H
  25. # include "config.h"
  26. #endif
  27. #include "sys.h"
  28. #include "erl_alloc.h"
  29. #include "erl_sys_driver.h"
  30. #include "global.h"
  31. #include "erl_threads.h"
  32. #include "../../drivers/win32/win_con.h"
  33. #include "erl_cpu_topology.h"
  34. #include <malloc.h>
  35. void erts_sys_init_float(void);
  36. void erl_start(int, char**);
  37. void erts_exit(int n, char*, ...);
  38. void erl_error(char*, va_list);
  39. /*
  40. * Microsoft-specific function to map a WIN32 error code to a Posix errno.
  41. */
  42. extern void _dosmaperr(DWORD);
  43. #ifdef ERL_RUN_SHARED_LIB
  44. #ifdef __argc
  45. #undef __argc
  46. #endif
  47. #define __argc e_argc
  48. #ifdef __argv
  49. #undef __argv
  50. #endif
  51. #define __argv e_argv
  52. #endif
  53. typedef struct driver_data DriverData;
  54. static void init_console();
  55. static int get_and_remove_option(int* argc, char** argv, const char* option);
  56. static char *get_and_remove_option2(int *argc, char **argv,
  57. const char *option);
  58. static int init_async_io(DriverData *dp, struct async_io* aio, int use_threads);
  59. static void release_async_io(struct async_io* aio, ErlDrvPort);
  60. static void async_read_file(struct async_io* aio, LPVOID buf, DWORD numToRead);
  61. static int async_write_file(struct async_io* aio, LPVOID buf, DWORD numToWrite);
  62. static int get_overlapped_result(struct async_io* aio,
  63. LPDWORD pBytesRead, BOOL wait);
  64. static BOOL create_child_process(wchar_t *, HANDLE, HANDLE,
  65. HANDLE, LPHANDLE, LPDWORD, BOOL,
  66. LPVOID, wchar_t*, unsigned,
  67. wchar_t **, int *);
  68. static int create_pipe(LPHANDLE, LPHANDLE, BOOL, BOOL);
  69. static int application_type(const wchar_t* originalName, wchar_t fullPath[MAX_PATH],
  70. BOOL search_in_path, BOOL handle_quotes,
  71. int *error_return);
  72. static void *build_env_block(const erts_osenv_t *env);
  73. HANDLE erts_service_event;
  74. static erts_tsd_key_t win32_errstr_key;
  75. static erts_atomic_t pipe_creation_counter;
  76. /* Results from application_type(_w) is one of */
  77. #define APPL_NONE 0
  78. #define APPL_DOS 1
  79. #define APPL_WIN3X 2
  80. #define APPL_WIN32 3
  81. static int driver_write(long, HANDLE, byte*, int);
  82. static int create_file_thread(struct async_io* aio, int mode);
  83. static void close_active_handle(DriverData *, HANDLE handle);
  84. static DWORD WINAPI threaded_handle_closer(LPVOID param);
  85. static DWORD WINAPI threaded_reader(LPVOID param);
  86. static DWORD WINAPI threaded_writer(LPVOID param);
  87. static DWORD WINAPI threaded_exiter(LPVOID param);
  88. #ifdef DEBUG
  89. static void debug_console(void);
  90. #endif
  91. BOOL WINAPI ctrl_handler(DWORD dwCtrlType);
  92. #define PORT_BUFSIZ 4096
  93. #define DRV_BUF_ALLOC(SZ) \
  94. erts_alloc_fnf(ERTS_ALC_T_DRV_DATA_BUF, (SZ))
  95. #define DRV_BUF_REALLOC(P, SZ) \
  96. erts_realloc_fnf(ERTS_ALC_T_DRV_DATA_BUF, (P), (SZ))
  97. #define DRV_BUF_FREE(P) \
  98. erts_free(ERTS_ALC_T_DRV_DATA_BUF, (P))
  99. /********************* General functions ****************************/
  100. /*
  101. * Whether create_pipe() should use a named pipe or an anonymous.
  102. * (Named pipes are not supported on Windows 95.)
  103. */
  104. static int max_files = 1024;
  105. static BOOL use_named_pipes;
  106. static BOOL win_console = FALSE;
  107. static OSVERSIONINFO int_os_version; /* Version information for Win32. */
  108. /*#define USE_CANCELIOEX
  109. Disabled the use of CancelIoEx as its been seen to cause problem with some
  110. drivers. Not sure what to blame; faulty drivers or some form of invalid use.
  111. */
  112. #if defined(USE_CANCELIOEX)
  113. static BOOL (WINAPI *fpCancelIoEx)(HANDLE,LPOVERLAPPED);
  114. #endif
  115. /* This is the system's main function (which may or may not be called "main")
  116. - do general system-dependent initialization
  117. - call erl_start() to parse arguments and do other init
  118. */
  119. static erts_atomic_t sys_misc_mem_sz;
  120. HMODULE beam_module = NULL;
  121. void erl_sys_init();
  122. void erl_sys_args(int* argc, char** argv);
  123. int nohup;
  124. #ifndef __GNUC__
  125. void erts_sys_invalid_parameter_handler(const wchar_t * expression,
  126. const wchar_t * function,
  127. const wchar_t * file,
  128. unsigned int line,
  129. uintptr_t pReserved
  130. )
  131. {
  132. #ifdef DEBUG
  133. fprintf(stderr,
  134. "Debug: Invalid parameter\"%ls\" "
  135. "(detected in \"%ls\" [%ls:%d]) \n",
  136. (expression) ? expression : L"(unknown)",
  137. (function) ? function : L"(unknown)",
  138. (file) ? file : L"(unknown)",
  139. line);
  140. #endif
  141. return;
  142. }
  143. #endif
  144. void sys_primitive_init(HMODULE beam)
  145. {
  146. #ifndef __GNUC__
  147. /* Initialize this module handle (the beam.dll module handle) and
  148. take care of the standard library's aggressive invalid parameter
  149. handling... */
  150. _set_invalid_parameter_handler(&erts_sys_invalid_parameter_handler);
  151. #endif
  152. beam_module = (HMODULE) beam;
  153. }
  154. UWord
  155. erts_sys_get_page_size(void)
  156. {
  157. SYSTEM_INFO info;
  158. GetSystemInfo(&info);
  159. return (UWord)info.dwPageSize;
  160. }
  161. Uint
  162. erts_sys_misc_mem_sz(void)
  163. {
  164. Uint res = (Uint) erts_check_io_size();
  165. res += (Uint) erts_atomic_read_mb(&sys_misc_mem_sz);
  166. return res;
  167. }
  168. /*
  169. * Reset the terminal to the original settings on exit
  170. */
  171. void sys_tty_reset(int exit_code)
  172. {
  173. if (exit_code == ERTS_ERROR_EXIT)
  174. ConWaitForExit();
  175. else
  176. ConNormalExit();
  177. }
  178. void erl_sys_args(int* argc, char** argv)
  179. {
  180. char *event_name;
  181. erts_sys_env_init();
  182. nohup = get_and_remove_option(argc, argv, "-nohup");
  183. #ifdef DEBUG
  184. /*
  185. * Start a debug console if -console option given.
  186. */
  187. if (get_and_remove_option(argc, argv, "-console")) {
  188. debug_console();
  189. }
  190. #endif
  191. if (nohup && (event_name = get_and_remove_option2(argc, argv,
  192. "-service_event"))) {
  193. if ((erts_service_event =
  194. OpenEvent(EVENT_ALL_ACCESS,FALSE,event_name)) == NULL) {
  195. erts_fprintf(stderr,
  196. "Warning: could not open service event: %s\r\n",
  197. event_name);
  198. }
  199. } else {
  200. erts_service_event = NULL;
  201. }
  202. #ifdef DEBUG
  203. /*
  204. * Given the "-threads" option, always use threads instead of
  205. * named pipes.
  206. */
  207. if (get_and_remove_option(argc, argv, "-threads")) {
  208. use_named_pipes = FALSE;
  209. }
  210. #endif
  211. }
  212. /*
  213. * Function returns 1 if we can read from all values in between
  214. * start and stop.
  215. */
  216. int
  217. erts_sys_is_area_readable(char *start, char *stop) {
  218. volatile char tmp;
  219. __try
  220. {
  221. while(start < stop) {
  222. tmp = *start;
  223. start++;
  224. }
  225. }
  226. __except(EXCEPTION_EXECUTE_HANDLER)
  227. {
  228. return 0;
  229. }
  230. return 1;
  231. }
  232. int erts_sys_prepare_crash_dump(int secs)
  233. {
  234. Port *heart_port;
  235. Eterm heap[3];
  236. Eterm *hp = heap;
  237. Eterm list = NIL;
  238. heart_port = erts_get_heart_port();
  239. if (heart_port) {
  240. list = CONS(hp, make_small(8), list); hp += 2;
  241. /* send to heart port, CMD = 8, i.e. prepare crash dump =o */
  242. erts_port_output(NULL, ERTS_PORT_SIG_FLG_FORCE_IMM_CALL, heart_port,
  243. heart_port->common.id, list, NULL);
  244. return 1;
  245. }
  246. /* Windows - free file descriptors are hopefully available */
  247. /* Alarm not used on windows */
  248. return 0;
  249. }
  250. int erts_set_signal(Eterm signal, Eterm type) {
  251. return 0;
  252. }
  253. static void
  254. init_console(void)
  255. {
  256. char* mode = erts_read_env("ERL_CONSOLE_MODE");
  257. if (!mode || strcmp(mode, "window") == 0) {
  258. win_console = TRUE;
  259. ConInit();
  260. /*nohup = 0;*/
  261. } else if (strncmp(mode, "tty:", 4) == 0) {
  262. if (mode[5] == 'c') {
  263. setvbuf(stdout, NULL, _IONBF, 0);
  264. }
  265. if (mode[6] == 'c') {
  266. setvbuf(stderr, NULL, _IONBF, 0);
  267. }
  268. }
  269. erts_free_read_env(mode);
  270. }
  271. int sys_max_files(void)
  272. {
  273. return max_files;
  274. }
  275. /*
  276. * Looks for the given option in the argv vector. If it is found,
  277. * it will be removed from the argv vector.
  278. *
  279. * If the return value indicates that the option was found and removed,
  280. * it is the responsibility of the caller to decrement the value of argc.
  281. *
  282. * Returns: 0 if the option wasn't found, 1 if it was found
  283. */
  284. static int
  285. get_and_remove_option(int* argc, char* argv[], const char *option)
  286. {
  287. int i;
  288. for (i = 1; i < *argc; i++) {
  289. if (strcmp(argv[i], option) == 0) {
  290. (*argc)--;
  291. while (i < *argc) {
  292. argv[i] = argv[i+1];
  293. i++;
  294. }
  295. argv[i] = NULL;
  296. return 1;
  297. }
  298. }
  299. return 0;
  300. }
  301. static char *get_and_remove_option2(int *argc, char **argv,
  302. const char *option)
  303. {
  304. char *ret;
  305. int i;
  306. for (i = 1; i < *argc; i++) {
  307. if (strcmp(argv[i], option) == 0) {
  308. if (i+1 < *argc) {
  309. ret = argv[i+1];
  310. (*argc) -= 2;
  311. while (i < *argc) {
  312. argv[i] = argv[i+2];
  313. i++;
  314. }
  315. argv[i] = NULL;
  316. return ret;
  317. }
  318. }
  319. }
  320. return NULL;
  321. }
  322. /************************** OS info *******************************/
  323. /* Used by erlang:info/1. */
  324. /* (This code was formerly in drv.XXX/XXX_os_drv.c) */
  325. char os_type[] = "win32";
  326. void
  327. os_flavor(char *namebuf, unsigned size)
  328. {
  329. switch (int_os_version.dwPlatformId) {
  330. case VER_PLATFORM_WIN32_WINDOWS:
  331. strcpy(namebuf, "windows");
  332. break;
  333. case VER_PLATFORM_WIN32_NT:
  334. strcpy(namebuf, "nt");
  335. break;
  336. default: /* Can't happen. */
  337. strcpy(namebuf, "unknown");
  338. break;
  339. }
  340. }
  341. void
  342. os_version(pMajor, pMinor, pBuild)
  343. int* pMajor; /* Pointer to major version. */
  344. int* pMinor; /* Pointer to minor version. */
  345. int* pBuild; /* Pointer to build number. */
  346. {
  347. *pMajor = int_os_version.dwMajorVersion;
  348. *pMinor = int_os_version.dwMinorVersion;
  349. *pBuild = int_os_version.dwBuildNumber;
  350. }
  351. /************************** Port I/O *******************************/
  352. /* I. Common stuff */
  353. /* II. The spawn/fd/vanilla drivers */
  354. /*
  355. * Definitions for driver flags.
  356. */
  357. #define DF_OVR_READY 1 /* Overlapped result is ready. */
  358. #define DF_EXIT_THREAD 2 /* The thread should exit. */
  359. #define DF_XLAT_CR 4 /* The thread should translate CRs. */
  360. #define DF_DROP_IF_INVH 8 /* Drop packages instead of crash if
  361. invalid handle (stderr) */
  362. #define DF_THREAD_FLUSHED 16 /* The thread should exit. */
  363. #define OV_BUFFER_PTR(dp) ((LPVOID) ((dp)->ov.Internal))
  364. #define OV_NUM_TO_READ(dp) ((dp)->ov.InternalHigh)
  365. /*
  366. * This data is used to make overlapped I/O operations work on both
  367. * Windows NT (using true overlapped I/O) and Windows 95 (using threads).
  368. */
  369. typedef struct async_io {
  370. unsigned flags; /* Driver flags, definitions found above. */
  371. HANDLE thread; /* If -1, overlapped I/O is used (Windows NT).
  372. * Otherwise, it is the handle of the thread used
  373. * for simulating overlapped I/O (Windows 95 and
  374. * the console for Windows NT).
  375. */
  376. HANDLE fd; /* Handle for file or pipe. */
  377. int async_io_active; /* if true, a close of the file will signal the event in ov */
  378. OVERLAPPED ov; /* Control structure for overlapped reading.
  379. * When overlapped reading is simulated with
  380. * a thread, the fields are used as follows:
  381. * ov.Internal - Read buffer.
  382. * ov.InternalHigh - Number of bytes to read.
  383. * See macros above.
  384. */
  385. HANDLE ioAllowed; /* The thread will wait for this event
  386. * before starting a new read or write.
  387. */
  388. HANDLE flushEvent; /* Used to signal that a flush should be done. */
  389. HANDLE flushReplyEvent; /* Used to signal that a flush has been done. */
  390. DWORD pendingError; /* Used to delay presentating an error to Erlang
  391. * until the check_io function is entered.
  392. */
  393. DWORD bytesTransferred; /* Bytes read or write in the last operation.
  394. * Valid only when DF_OVR_READY is set.
  395. */
  396. DriverData *dp; /* Pointer to driver data struct which
  397. this struct is part of */
  398. } AsyncIo;
  399. /*
  400. * Input thread for fd_driver (if fd_driver is running).
  401. */
  402. static AsyncIo* fd_driver_input = NULL;
  403. static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD);
  404. /*
  405. * This data is used by the spawn and vanilla drivers.
  406. * There will be one entry for each port, even if the input
  407. * and output HANDLES are different. Since handles are not
  408. * guaranteed to be small numbers in Win32, we cannot index
  409. * with them. I.e. the index for each entry is not equal to
  410. * none of the file handles.
  411. */
  412. struct driver_data {
  413. int totalNeeded; /* Total number of bytes needed to fill
  414. * up the packet header or packet. */
  415. int bytesInBuffer; /* Number of bytes read so far in
  416. * the input buffer.
  417. */
  418. int inBufSize; /* Size of input buffer. */
  419. byte *inbuf; /* Buffer to use for overlapped read. */
  420. int outBufSize; /* Size of output buffer. */
  421. byte *outbuf; /* Buffer to use for overlapped write. */
  422. ErlDrvPort port_num; /* The port handle. */
  423. int packet_bytes; /* 0: continuous stream, 1, 2, or 4: the number
  424. * of bytes in the packet header.
  425. */
  426. HANDLE port_pid; /* PID of the port process. */
  427. AsyncIo in; /* Control block for overlapped reading. */
  428. AsyncIo out; /* Control block for overlapped writing. */
  429. int report_exit; /* Do report exit status for the port */
  430. erts_atomic32_t refc; /* References to this struct */
  431. };
  432. /* Driver interfaces */
  433. static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*);
  434. static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*);
  435. static ErlDrvData vanilla_start(ErlDrvPort, char*, SysDriverOpts*);
  436. static int spawn_init(void);
  437. static int fd_init(void);
  438. static void fd_stop(ErlDrvData);
  439. static void stop(ErlDrvData);
  440. static void output(ErlDrvData, char*, ErlDrvSizeT);
  441. static void ready_input(ErlDrvData, ErlDrvEvent);
  442. static void ready_output(ErlDrvData, ErlDrvEvent);
  443. static void stop_select(ErlDrvEvent, void*);
  444. struct erl_drv_entry spawn_driver_entry = {
  445. spawn_init,
  446. spawn_start,
  447. stop,
  448. output,
  449. ready_input,
  450. ready_output,
  451. "spawn",
  452. NULL, /* finish */
  453. NULL, /* handle */
  454. NULL, /* control */
  455. NULL, /* timeout */
  456. NULL, /* outputv */
  457. NULL, /* ready_async */
  458. NULL, /* flush */
  459. NULL, /* call */
  460. NULL, /* event */
  461. ERL_DRV_EXTENDED_MARKER,
  462. ERL_DRV_EXTENDED_MAJOR_VERSION,
  463. ERL_DRV_EXTENDED_MINOR_VERSION,
  464. 0, /* ERL_DRV_FLAGs */
  465. NULL,
  466. NULL, /* process_exit */
  467. stop_select
  468. };
  469. #ifdef HARD_POLL_DEBUG
  470. extern void poll_debug_set_active_fd(ErtsSysFdType fd);
  471. extern void poll_debug_read_begin(ErtsSysFdType fd);
  472. extern void poll_debug_read_done(ErtsSysFdType fd, int bytes);
  473. extern void poll_debug_async_initialized(ErtsSysFdType fd);
  474. extern void poll_debug_async_immediate(ErtsSysFdType fd, int bytes);
  475. extern void poll_debug_write_begin(ErtsSysFdType fd);
  476. extern void poll_debug_write_done(ErtsSysFdType fd, int bytes);
  477. #endif
  478. extern int null_func(void);
  479. struct erl_drv_entry fd_driver_entry = {
  480. fd_init,
  481. fd_start,
  482. fd_stop,
  483. output,
  484. ready_input,
  485. ready_output,
  486. "fd",
  487. NULL, /* finish */
  488. NULL, /* handle */
  489. NULL, /* control */
  490. NULL, /* timeout */
  491. NULL, /* outputv */
  492. NULL, /* ready_async */
  493. NULL, /* flush */
  494. NULL, /* call */
  495. NULL, /* event */
  496. ERL_DRV_EXTENDED_MARKER,
  497. ERL_DRV_EXTENDED_MAJOR_VERSION,
  498. ERL_DRV_EXTENDED_MINOR_VERSION,
  499. 0, /* ERL_DRV_FLAGs */
  500. NULL,
  501. NULL, /* process_exit */
  502. stop_select
  503. };
  504. struct erl_drv_entry vanilla_driver_entry = {
  505. null_func,
  506. vanilla_start,
  507. stop,
  508. output,
  509. ready_input,
  510. ready_output,
  511. "vanilla",
  512. NULL, /* finish */
  513. NULL, /* handle */
  514. NULL, /* control */
  515. NULL, /* timeout */
  516. NULL, /* outputv */
  517. NULL, /* ready_async */
  518. NULL, /* flush */
  519. NULL, /* call */
  520. NULL, /* event */
  521. ERL_DRV_EXTENDED_MARKER,
  522. ERL_DRV_EXTENDED_MAJOR_VERSION,
  523. ERL_DRV_EXTENDED_MINOR_VERSION,
  524. 0, /* ERL_DRV_FLAGs */
  525. NULL,
  526. NULL, /* process_exit */
  527. stop_select
  528. };
  529. static ERTS_INLINE void
  530. refer_driver_data(DriverData *dp)
  531. {
  532. #ifdef DEBUG
  533. erts_aint32_t refc = erts_atomic32_inc_read_nob(&dp->refc);
  534. ASSERT(refc > 1);
  535. #else
  536. erts_atomic32_inc_nob(&dp->refc);
  537. #endif
  538. }
  539. static ERTS_INLINE void
  540. unrefer_driver_data(DriverData *dp)
  541. {
  542. erts_aint32_t refc = erts_atomic32_dec_read_mb(&dp->refc);
  543. ASSERT(refc >= 0);
  544. if (refc == 0)
  545. driver_free(dp);
  546. }
  547. /*
  548. * Initialises a DriverData structure.
  549. *
  550. * Results: Returns a pointer to a DriverData structure, or NULL
  551. * if the initialsation failed.
  552. */
  553. static DriverData*
  554. new_driver_data(ErlDrvPort port_num, int packet_bytes, int wait_objs_required, int use_threads)
  555. {
  556. DriverData* dp;
  557. DEBUGF(("new_driver_data(%p, pb %d)\n", port_num, packet_bytes));
  558. dp = driver_alloc(sizeof(DriverData));
  559. if (!dp)
  560. return NULL;
  561. /*
  562. * We used to test first at all that there is enough room in the
  563. * array used by WaitForMultipleObjects(), but that is not necessary
  564. * any more, since driver_select() can't fail.
  565. */
  566. erts_atomic32_init_nob(&dp->refc, 1);
  567. dp->bytesInBuffer = 0;
  568. dp->totalNeeded = packet_bytes;
  569. dp->inBufSize = PORT_BUFSIZ;
  570. dp->inbuf = DRV_BUF_ALLOC(dp->inBufSize);
  571. if (dp->inbuf == NULL)
  572. goto buf_alloc_error;
  573. erts_atomic_add_nob(&sys_misc_mem_sz, dp->inBufSize);
  574. dp->outBufSize = 0;
  575. dp->outbuf = NULL;
  576. dp->port_num = port_num;
  577. dp->packet_bytes = packet_bytes;
  578. dp->port_pid = INVALID_HANDLE_VALUE;
  579. if (init_async_io(dp, &dp->in, use_threads) == -1)
  580. goto async_io_error1;
  581. if (init_async_io(dp, &dp->out, use_threads) == -1)
  582. goto async_io_error2;
  583. return dp;
  584. async_io_error2:
  585. release_async_io(&dp->in, dp->port_num);
  586. async_io_error1:
  587. release_async_io(&dp->out, dp->port_num);
  588. buf_alloc_error:
  589. driver_free(dp);
  590. return NULL;
  591. }
  592. static void
  593. release_driver_data(DriverData* dp)
  594. {
  595. #ifdef USE_CANCELIOEX
  596. if (fpCancelIoEx != NULL) {
  597. if (dp->in.thread == (HANDLE) -1 && dp->in.fd != INVALID_HANDLE_VALUE) {
  598. (*fpCancelIoEx)(dp->in.fd, NULL);
  599. }
  600. if (dp->out.thread == (HANDLE) -1 && dp->out.fd != INVALID_HANDLE_VALUE) {
  601. (*fpCancelIoEx)(dp->out.fd, NULL);
  602. }
  603. }
  604. else
  605. #endif
  606. {
  607. /* This is a workaround for the fact that CancelIo cant cancel
  608. requests issued by another thread and that we cant use
  609. CancelIoEx as that's only available in Vista etc.
  610. R14: Avoid scheduler deadlock by only wait for 10ms, and then spawn
  611. a thread that will keep waiting in in order to close handles. */
  612. HANDLE handles[2];
  613. int i = 0;
  614. int timeout = 10;
  615. if(dp->in.async_io_active && dp->in.fd != INVALID_HANDLE_VALUE) {
  616. CloseHandle(dp->in.fd);
  617. dp->in.fd = INVALID_HANDLE_VALUE;
  618. DEBUGF(("Waiting for the in event thingie"));
  619. if (WaitForSingleObject(dp->in.ov.hEvent,timeout) == WAIT_TIMEOUT) {
  620. close_active_handle(dp, dp->in.ov.hEvent);
  621. dp->in.ov.hEvent = NULL;
  622. timeout = 0;
  623. }
  624. DEBUGF(("...done\n"));
  625. }
  626. if(dp->out.async_io_active && dp->out.fd != INVALID_HANDLE_VALUE) {
  627. CloseHandle(dp->out.fd);
  628. dp->out.fd = INVALID_HANDLE_VALUE;
  629. DEBUGF(("Waiting for the out event thingie"));
  630. if (WaitForSingleObject(dp->out.ov.hEvent,timeout) == WAIT_TIMEOUT) {
  631. close_active_handle(dp, dp->out.ov.hEvent);
  632. dp->out.ov.hEvent = NULL;
  633. }
  634. DEBUGF(("...done\n"));
  635. }
  636. }
  637. if (dp->inbuf != NULL) {
  638. ASSERT(erts_atomic_read_nob(&sys_misc_mem_sz) >= dp->inBufSize);
  639. erts_atomic_add_nob(&sys_misc_mem_sz, -1*dp->inBufSize);
  640. DRV_BUF_FREE(dp->inbuf);
  641. dp->inBufSize = 0;
  642. dp->inbuf = NULL;
  643. }
  644. ASSERT(dp->inBufSize == 0);
  645. if (dp->outbuf != NULL) {
  646. ASSERT(erts_atomic_read_nob(&sys_misc_mem_sz) >= dp->outBufSize);
  647. erts_atomic_add_nob(&sys_misc_mem_sz, -1*dp->outBufSize);
  648. DRV_BUF_FREE(dp->outbuf);
  649. dp->outBufSize = 0;
  650. dp->outbuf = NULL;
  651. }
  652. ASSERT(dp->outBufSize == 0);
  653. if (dp->port_pid != INVALID_HANDLE_VALUE) {
  654. CloseHandle(dp->port_pid);
  655. dp->port_pid = INVALID_HANDLE_VALUE;
  656. }
  657. release_async_io(&dp->in, dp->port_num);
  658. release_async_io(&dp->out, dp->port_num);
  659. /*
  660. * This must be last, because this function might be executed from
  661. * the exit thread.
  662. */
  663. unrefer_driver_data(dp);
  664. }
  665. struct handles_to_be_closed {
  666. HANDLE handles[MAXIMUM_WAIT_OBJECTS];
  667. DriverData *drv_data[MAXIMUM_WAIT_OBJECTS];
  668. unsigned cnt;
  669. };
  670. static struct handles_to_be_closed* htbc_curr = NULL;
  671. CRITICAL_SECTION htbc_lock;
  672. static void close_active_handle(DriverData *dp, HANDLE handle)
  673. {
  674. struct handles_to_be_closed* htbc;
  675. int i;
  676. EnterCriticalSection(&htbc_lock);
  677. htbc = htbc_curr;
  678. if (htbc == NULL || htbc->cnt >= MAXIMUM_WAIT_OBJECTS) {
  679. DWORD tid;
  680. HANDLE thread;
  681. htbc = (struct handles_to_be_closed*) erts_alloc(ERTS_ALC_T_DRV_TAB,
  682. sizeof(*htbc));
  683. htbc->handles[0] = CreateAutoEvent(FALSE);
  684. htbc->drv_data[0] = NULL;
  685. htbc->cnt = 1;
  686. thread = (HANDLE *) _beginthreadex(NULL, 0, threaded_handle_closer, htbc, 0, &tid);
  687. CloseHandle(thread);
  688. }
  689. i = htbc->cnt++;
  690. htbc->handles[i] = handle;
  691. htbc->drv_data[i] = dp;
  692. if (dp)
  693. refer_driver_data(dp); /* Need to keep driver data until we have
  694. closed the event; outstanding operation
  695. might write into it.. */
  696. SetEvent(htbc->handles[0]);
  697. htbc_curr = htbc;
  698. LeaveCriticalSection(&htbc_lock);
  699. }
  700. static DWORD WINAPI
  701. threaded_handle_closer(LPVOID param)
  702. {
  703. struct handles_to_be_closed* htbc = (struct handles_to_be_closed*) param;
  704. unsigned ix;
  705. DWORD res;
  706. DEBUGF(("threaded_handle_closer %p started\r\n", htbc));
  707. EnterCriticalSection(&htbc_lock);
  708. for (;;) {
  709. {
  710. HANDLE* handles = htbc->handles;
  711. unsigned cnt = htbc->cnt;
  712. DWORD timeout = (htbc == htbc_curr) ? INFINITE : 10*1000;
  713. LeaveCriticalSection(&htbc_lock);
  714. DEBUGF(("threaded_handle_closer %p waiting for %d handles\r\n", htbc, cnt));
  715. res = WaitForMultipleObjects(cnt, handles, FALSE, timeout);
  716. }
  717. EnterCriticalSection(&htbc_lock);
  718. switch (res) {
  719. case WAIT_OBJECT_0:
  720. case WAIT_TIMEOUT:
  721. break; /* got some more handles to wait for maybe */
  722. default:
  723. ix = res - WAIT_OBJECT_0;
  724. if (ix > 0 && ix < htbc->cnt) {
  725. int move_ix;
  726. CloseHandle(htbc->handles[ix]);
  727. if (htbc->drv_data[ix])
  728. unrefer_driver_data(htbc->drv_data[ix]);
  729. move_ix = --htbc->cnt;
  730. htbc->handles[ix] = htbc->handles[move_ix];
  731. htbc->drv_data[ix] = htbc->drv_data[move_ix];
  732. }
  733. }
  734. if (htbc != htbc_curr) {
  735. if (htbc->cnt == 1) { /* no real handles left */
  736. break;
  737. }
  738. /* The thread with most free slots will be "current" */
  739. if (htbc->cnt < htbc_curr->cnt) {
  740. htbc_curr = htbc;
  741. DEBUGF(("threaded_handle_closer %p made current\r\n", htbc));
  742. }
  743. }
  744. }
  745. LeaveCriticalSection(&htbc_lock);
  746. CloseHandle(htbc->handles[0]);
  747. ASSERT(!htbc->drv_data[0]);
  748. erts_free(ERTS_ALC_T_DRV_TAB, htbc);
  749. DEBUGF(("threaded_handle_closer %p terminating\r\n", htbc));
  750. return 0;
  751. }
  752. /*
  753. * Stores input and output file descriptors in the DriverData structure,
  754. * and calls driver_select().
  755. *
  756. * This function fortunately can't fail!
  757. */
  758. static ErlDrvData
  759. set_driver_data(DriverData* dp, HANDLE ifd, HANDLE ofd, int read_write, int report_exit)
  760. {
  761. int result;
  762. dp->in.fd = ifd;
  763. dp->out.fd = ofd;
  764. dp->report_exit = report_exit;
  765. if (read_write & DO_READ) {
  766. result = driver_select(dp->port_num, (ErlDrvEvent)dp->in.ov.hEvent,
  767. ERL_DRV_READ|ERL_DRV_USE, 1);
  768. ASSERT(result != -1);
  769. async_read_file(&dp->in, dp->inbuf, dp->inBufSize);
  770. }
  771. if (read_write & DO_WRITE) {
  772. result = driver_select(dp->port_num, (ErlDrvEvent)dp->out.ov.hEvent,
  773. ERL_DRV_WRITE|ERL_DRV_USE, 1);
  774. ASSERT(result != -1);
  775. }
  776. return (ErlDrvData) dp;
  777. }
  778. static ErlDrvData
  779. reuse_driver_data(DriverData *dp, HANDLE ifd, HANDLE ofd, int read_write, ErlDrvPort port_num)
  780. {
  781. int result;
  782. dp->port_num = port_num;
  783. dp->in.fd = ifd;
  784. dp->out.fd = ofd;
  785. dp->report_exit = 0;
  786. if (read_write & DO_READ) {
  787. result = driver_select(dp->port_num, (ErlDrvEvent)dp->in.ov.hEvent,
  788. ERL_DRV_READ|ERL_DRV_USE, 1);
  789. ASSERT(result != -1);
  790. }
  791. if (read_write & DO_WRITE) {
  792. result = driver_select(dp->port_num, (ErlDrvEvent)dp->out.ov.hEvent,
  793. ERL_DRV_WRITE|ERL_DRV_USE, 1);
  794. ASSERT(result != -1);
  795. }
  796. return (ErlDrvData) dp;
  797. }
  798. /*
  799. * Initialises an AsyncIo structure.
  800. */
  801. static int
  802. init_async_io(DriverData *dp, AsyncIo* aio, int use_threads)
  803. {
  804. aio->dp = dp;
  805. aio->flags = 0;
  806. aio->thread = (HANDLE) -1;
  807. aio->fd = INVALID_HANDLE_VALUE;
  808. aio->ov.hEvent = NULL;
  809. aio->ov.Offset = 0L;
  810. aio->ov.OffsetHigh = 0L;
  811. aio->ioAllowed = NULL;
  812. aio->flushEvent = NULL;
  813. aio->flushReplyEvent = NULL;
  814. aio->pendingError = 0;
  815. aio->bytesTransferred = 0;
  816. aio->async_io_active = 0;
  817. aio->ov.hEvent = CreateManualEvent(FALSE);
  818. if (aio->ov.hEvent == NULL)
  819. return -1;
  820. if (use_threads) {
  821. OV_BUFFER_PTR(aio) = NULL;
  822. OV_NUM_TO_READ(aio) = 0;
  823. aio->ioAllowed = CreateAutoEvent(FALSE);
  824. if (aio->ioAllowed == NULL)
  825. return -1;
  826. aio->flushEvent = CreateAutoEvent(FALSE);
  827. if (aio->flushEvent == NULL)
  828. return -1;
  829. aio->flushReplyEvent = CreateAutoEvent(FALSE);
  830. if (aio->flushReplyEvent == NULL)
  831. return -1;
  832. }
  833. return 0;
  834. }
  835. /*
  836. * Releases everything allocated in an AsyncIo structure.
  837. */
  838. static void
  839. release_async_io(AsyncIo* aio, ErlDrvPort port_num)
  840. {
  841. aio->flags = 0;
  842. if (aio->thread != (HANDLE) -1)
  843. CloseHandle(aio->thread);
  844. aio->thread = (HANDLE) -1;
  845. if (aio->fd != INVALID_HANDLE_VALUE)
  846. CloseHandle(aio->fd);
  847. aio->fd = INVALID_HANDLE_VALUE;
  848. if (aio->ov.hEvent != NULL)
  849. CloseHandle(aio->ov.hEvent);
  850. aio->ov.hEvent = NULL;
  851. if (aio->ioAllowed != NULL)
  852. CloseHandle(aio->ioAllowed);
  853. aio->ioAllowed = NULL;
  854. if (aio->flushEvent != NULL)
  855. CloseHandle(aio->flushEvent);
  856. aio->flushEvent = NULL;
  857. if (aio->flushReplyEvent != NULL)
  858. CloseHandle(aio->flushReplyEvent);
  859. aio->flushReplyEvent = NULL;
  860. }
  861. /* ----------------------------------------------------------------------
  862. * async_read_file --
  863. * Initiaties an asynchronous file read, or simulates that using
  864. * the thread associated with this driver data. To get the results,
  865. * call get_overlapped_result().
  866. *
  867. * Results:
  868. * None.
  869. * ----------------------------------------------------------------------
  870. */
  871. static void
  872. async_read_file(AsyncIo* aio, LPVOID buf, DWORD numToRead)
  873. {
  874. aio->pendingError = NO_ERROR;
  875. #ifdef HARD_POLL_DEBUG
  876. poll_debug_async_initialized(aio->ov.hEvent);
  877. #endif
  878. if (aio->thread != (HANDLE) -1) {
  879. DEBUGF(("async_read_file: signaling thread 0x%x, event 0x%x\n",
  880. aio->thread, aio->ioAllowed));
  881. OV_BUFFER_PTR(aio) = buf;
  882. OV_NUM_TO_READ(aio) = numToRead;
  883. ResetEvent(aio->ov.hEvent);
  884. SetEvent(aio->ioAllowed);
  885. } else {
  886. aio->async_io_active = 1; /* Will get 0 when the event actually happened */
  887. if (ReadFile(aio->fd, buf, numToRead,
  888. &aio->bytesTransferred, &aio->ov)) {
  889. DEBUGF(("async_read_file: ReadFile() suceeded: %d bytes\n",
  890. aio->bytesTransferred));
  891. #ifdef HARD_POLL_DEBUG
  892. poll_debug_async_immediate(aio->ov.hEvent, aio->bytesTransferred);
  893. #endif
  894. aio->flags |= DF_OVR_READY;
  895. SetEvent(aio->ov.hEvent);
  896. } else {
  897. DWORD error = GetLastError();
  898. if (error != ERROR_IO_PENDING) {
  899. #ifdef HARD_POLL_DEBUG
  900. poll_debug_async_immediate(aio->ov.hEvent, 0);
  901. #endif
  902. aio->pendingError = error;
  903. SetEvent(aio->ov.hEvent);
  904. }
  905. DEBUGF(("async_read_file: ReadFile() -> %s\n", win32_errorstr(error)));
  906. }
  907. }
  908. }
  909. /* ----------------------------------------------------------------------
  910. * async_write_file --
  911. * Initiaties an asynchronous file write, or simulates that using
  912. * the output thread associated with this driver data.
  913. * To get the results, call get_overlapped_result().
  914. *
  915. * Results:
  916. * None.
  917. * ----------------------------------------------------------------------
  918. */
  919. static int
  920. async_write_file(AsyncIo* aio, /* Pointer to async control block. */
  921. LPVOID buf, /* Pointer to buffer with data to write. */
  922. DWORD numToWrite) /* Number of bytes to write. */
  923. {
  924. aio->pendingError = NO_ERROR;
  925. if (aio->thread != (HANDLE) -1) {
  926. DEBUGF(("async_write_file: signaling thread 0x%x, event 0x%x\n",
  927. aio->thread, aio->ioAllowed));
  928. OV_BUFFER_PTR(aio) = buf;
  929. OV_NUM_TO_READ(aio) = numToWrite;
  930. ResetEvent(aio->ov.hEvent);
  931. SetEvent(aio->ioAllowed);
  932. } else {
  933. aio->async_io_active = 1; /* Will get 0 when the event actually happened */
  934. if (WriteFile(aio->fd, buf, numToWrite,
  935. &aio->bytesTransferred, &aio->ov)) {
  936. DEBUGF(("async_write_file: WriteFile() suceeded: %d bytes\n",
  937. aio->bytesTransferred));
  938. aio->async_io_active = 0; /* The event will not be signalled */
  939. ResetEvent(aio->ov.hEvent);
  940. return TRUE;
  941. } else {
  942. DWORD error = GetLastError();
  943. if (error != ERROR_IO_PENDING) {
  944. aio->pendingError = error;
  945. SetEvent(aio->ov.hEvent);
  946. }
  947. DEBUGF(("async_write_file: WriteFile() -> %s\n", win32_errorstr(error)));
  948. }
  949. }
  950. return FALSE;
  951. }
  952. /* ----------------------------------------------------------------------
  953. * get_overlapped_result --
  954. *
  955. * Results:
  956. * Returns the error code for the overlapped result, or NO_ERROR
  957. * if no error.
  958. * ----------------------------------------------------------------------
  959. */
  960. static int
  961. get_overlapped_result(AsyncIo* aio, /* Pointer to async control block. */
  962. LPDWORD pBytesRead, /* Where to place the number of bytes
  963. * transferred.
  964. */
  965. BOOL wait /* If true, wait until result is ready. */
  966. )
  967. {
  968. DWORD error = NO_ERROR; /* Error status from last function. */
  969. if (aio->thread != (HANDLE) -1) {
  970. /*
  971. * Simulate overlapped io with a thread.
  972. */
  973. DEBUGF(("get_overlapped_result: about to wait for event 0x%x\n",
  974. aio->ov.hEvent));
  975. error = WaitForSingleObject(aio->ov.hEvent, wait ? INFINITE : 0);
  976. switch (error) {
  977. case WAIT_OBJECT_0:
  978. error = aio->pendingError;
  979. aio->pendingError = NO_ERROR;
  980. *pBytesRead = aio->bytesTransferred;
  981. ResetEvent(aio->ov.hEvent);
  982. DEBUGF(("get_overlapped_result -> %s\n",
  983. win32_errorstr(error)));
  984. return error;
  985. case WAIT_TIMEOUT:
  986. DEBUGF(("get_overlapped_result -> %s\n",
  987. ERROR_IO_INCOMPLETE));
  988. return ERROR_IO_INCOMPLETE;
  989. case WAIT_FAILED: /* XXX: Shouldn't happen? */
  990. error = GetLastError();
  991. DEBUGF(("get_overlapped_result (WAIT_FAILED) -> %s\n",
  992. win32_errorstr(error)));
  993. return error;
  994. }
  995. } else if (aio->pendingError != NO_ERROR) { /* Pending error. */
  996. error = aio->pendingError;
  997. aio->pendingError = NO_ERROR;
  998. ResetEvent(aio->ov.hEvent);
  999. DEBUGF(("get_overlapped_result: pending error: %s\n",
  1000. win32_errorstr(error)));
  1001. return error;
  1002. } else if (aio->flags & DF_OVR_READY) { /* Operation succeded. */
  1003. aio->flags &= ~DF_OVR_READY;
  1004. *pBytesRead = aio->bytesTransferred;
  1005. ResetEvent(aio->ov.hEvent);
  1006. DEBUGF(("get_overlapped_result: delayed success: %d bytes\n",
  1007. aio->bytesTransferred));
  1008. } else if (!GetOverlappedResult(aio->fd, &aio->ov, pBytesRead, wait)) {
  1009. error = GetLastError();
  1010. ResetEvent(aio->ov.hEvent);
  1011. DEBUGF(("get_overlapped_result: error: %s\n", win32_errorstr(error)));
  1012. return error;
  1013. } else { /* Success. */
  1014. DEBUGF(("get_overlapped_result: success\n"));
  1015. ResetEvent(aio->ov.hEvent);
  1016. }
  1017. return NO_ERROR;
  1018. }
  1019. static int
  1020. fd_init(void)
  1021. {
  1022. char kernel_dll_name[] = "kernel32";
  1023. HMODULE module;
  1024. module = GetModuleHandle(kernel_dll_name);
  1025. fpSetHandleInformation = (module != NULL) ?
  1026. (BOOL (WINAPI *)(HANDLE,DWORD,DWORD))
  1027. GetProcAddress(module,"SetHandleInformation") :
  1028. NULL;
  1029. return 0;
  1030. }
  1031. static int
  1032. spawn_init(void)
  1033. {
  1034. int i;
  1035. #if defined(USE_CANCELIOEX)
  1036. HMODULE module = GetModuleHandle("kernel32");
  1037. fpCancelIoEx = (BOOL (WINAPI *)(HANDLE,LPOVERLAPPED))
  1038. ((module != NULL) ? GetProcAddress(module,"CancelIoEx") : NULL);
  1039. DEBUGF(("fpCancelIoEx = %p\r\n", fpCancelIoEx));
  1040. #endif
  1041. return 0;
  1042. }
  1043. static ErlDrvData
  1044. spawn_start(ErlDrvPort port_num, char* utf8_name, SysDriverOpts* opts)
  1045. {
  1046. HANDLE hToChild = INVALID_HANDLE_VALUE; /* Write handle to child. */
  1047. HANDLE hFromChild = INVALID_HANDLE_VALUE; /* Read handle from child. */
  1048. HANDLE hChildStdin = INVALID_HANDLE_VALUE; /* Child's stdin. */
  1049. HANDLE hChildStdout = INVALID_HANDLE_VALUE; /* Child's stout. */
  1050. HANDLE hChildStderr = INVALID_HANDLE_VALUE; /* Child's sterr. */
  1051. DWORD pid;
  1052. int close_child_stderr = 0;
  1053. DriverData* dp; /* Pointer to driver data. */
  1054. ErlDrvData retval = ERL_DRV_ERROR_GENERAL; /* Return value. */
  1055. int ok;
  1056. int neededSelects = 0;
  1057. SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
  1058. int errno_return = -1;
  1059. wchar_t *name;
  1060. int len;
  1061. if (opts->read_write & DO_READ)
  1062. neededSelects++;
  1063. if (opts->read_write & DO_WRITE)
  1064. neededSelects++;
  1065. if ((dp = new_driver_data(port_num, opts->packet_bytes, neededSelects,
  1066. !use_named_pipes)) == NULL)
  1067. return ERL_DRV_ERROR_GENERAL;
  1068. /*
  1069. * Create two pipes to communicate with the port program.
  1070. */
  1071. if (opts->read_write & DO_READ) {
  1072. if (!create_pipe(&hFromChild, &hChildStdout, FALSE,
  1073. opts->overlapped_io))
  1074. goto error;
  1075. } else {
  1076. hChildStdout = CreateFile("nul", GENERIC_WRITE, 0,
  1077. &sa, OPEN_EXISTING,
  1078. FILE_ATTRIBUTE_NORMAL, NULL);
  1079. DEBUGF(("Created nul file for hChildStdout = %d\n",hChildStdout));
  1080. }
  1081. if (opts->read_write & DO_WRITE) {
  1082. if (!create_pipe(&hChildStdin, &hToChild, TRUE, opts->overlapped_io)) {
  1083. CloseHandle(hFromChild);
  1084. hFromChild = INVALID_HANDLE_VALUE;
  1085. CloseHandle(hChildStdout);
  1086. goto error;
  1087. }
  1088. } else {
  1089. hChildStdin = CreateFile("nul", GENERIC_READ, 0,
  1090. &sa, OPEN_EXISTING,
  1091. FILE_ATTRIBUTE_NORMAL, NULL);
  1092. DEBUGF(("Created nul file for hChildStdin = %d\n",hChildStdin));
  1093. }
  1094. /*
  1095. * Make sure that standard error is valid handle, because a Command Prompt
  1096. * window not work properly otherwise. We leave standard error alone if
  1097. * it is okay and no redirection was specified.
  1098. */
  1099. hChildStderr = GetStdHandle(STD_ERROR_HANDLE);
  1100. if (opts->redir_stderr) {
  1101. hChildStderr = hChildStdout;
  1102. } else if (hChildStderr == INVALID_HANDLE_VALUE || hChildStderr == 0) {
  1103. hChildStderr = CreateFile("nul", GENERIC_WRITE, 0, &sa, OPEN_EXISTING,
  1104. FILE_ATTRIBUTE_NORMAL, NULL);
  1105. close_child_stderr = 1;
  1106. }
  1107. if (fpSetHandleInformation != NULL) {
  1108. (*fpSetHandleInformation)(hChildStderr, HANDLE_FLAG_INHERIT, 1);
  1109. }
  1110. /*
  1111. * Spawn the port program.
  1112. */
  1113. if ((len = MultiByteToWideChar(CP_UTF8, 0, utf8_name, -1, NULL, 0)) > 0) {
  1114. name = erts_alloc(ERTS_ALC_T_TMP, len*sizeof(wchar_t));
  1115. MultiByteToWideChar(CP_UTF8, 0, utf8_name, -1, name, len);
  1116. } else { /* Not valid utf-8, just convert byte to wchar */
  1117. int i;
  1118. len = strlen(utf8_name);
  1119. name = erts_alloc(ERTS_ALC_T_TMP, (1+len)*sizeof(wchar_t));
  1120. for(i=0; i<len; i++) {
  1121. name[i] = (wchar_t) utf8_name[i];
  1122. }
  1123. name[i] = L'\0';
  1124. }
  1125. DEBUGF(("Spawning \"%S\"\n", name));
  1126. {
  1127. void *environment_block = build_env_block(&opts->envir);
  1128. ok = create_child_process(name,
  1129. hChildStdin,
  1130. hChildStdout,
  1131. hChildStderr,
  1132. &dp->port_pid,
  1133. &pid,
  1134. opts->hide_window,
  1135. environment_block,
  1136. (wchar_t *) opts->wd,
  1137. opts->spawn_type,
  1138. (wchar_t **) opts->argv,
  1139. &errno_return);
  1140. CloseHandle(hChildStdin);
  1141. CloseHandle(hChildStdout);
  1142. if (close_child_stderr && hChildStderr != INVALID_HANDLE_VALUE &&
  1143. hChildStderr != 0) {
  1144. CloseHandle(hChildStderr);
  1145. }
  1146. erts_free(ERTS_ALC_T_TMP, environment_block);
  1147. erts_free(ERTS_ALC_T_TMP, name);
  1148. }
  1149. if (!ok) {
  1150. dp->port_pid = INVALID_HANDLE_VALUE;
  1151. if (errno_return >= 0) {
  1152. retval = ERL_DRV_ERROR_ERRNO;
  1153. }
  1154. } else {
  1155. if (!use_named_pipes) {
  1156. if ((opts->read_write & DO_READ) &&
  1157. !create_file_thread(&dp->in, DO_READ))
  1158. goto error;
  1159. if ((opts->read_write & DO_WRITE) &&
  1160. !create_file_thread(&dp->out, DO_WRITE)) {
  1161. dp->in.flags = DF_EXIT_THREAD;
  1162. SetEvent(dp->in.ioAllowed);
  1163. WaitForSingleObject(dp->in.thread, INFINITE);
  1164. dp->in.thread = (HANDLE) -1;
  1165. goto error;
  1166. }
  1167. }
  1168. #ifdef HARD_POLL_DEBUG
  1169. if (strncmp(name,"inet_gethost",12) == 0) {
  1170. erts_printf("Debugging \"%s\"\n", name);
  1171. poll_debug_set_active_fd(dp->in.ov.hEvent);
  1172. }
  1173. #endif
  1174. retval = set_driver_data(dp, hFromChild, hToChild, opts->read_write,
  1175. opts->exit_status);
  1176. if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO) {
  1177. /* We assume that this cannot generate a negative number */
  1178. erl_drv_set_os_pid(port_num, pid);
  1179. }
  1180. }
  1181. if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO)
  1182. return retval;
  1183. error:
  1184. if (hFromChild != INVALID_HANDLE_VALUE)
  1185. CloseHandle(hFromChild);
  1186. if (hToChild != INVALID_HANDLE_VALUE)
  1187. CloseHandle(hToChild);
  1188. release_driver_data(dp);
  1189. if (retval == ERL_DRV_ERROR_ERRNO) {
  1190. errno = errno_return;
  1191. }
  1192. return retval;
  1193. }
  1194. struct __build_env_state {
  1195. WCHAR *next_variable;
  1196. };
  1197. static void build_env_foreach(void *_state, const erts_osenv_data_t *key,
  1198. const erts_osenv_data_t *value)
  1199. {
  1200. struct __build_env_state *state = (struct __build_env_state*)(_state);
  1201. sys_memcpy(state->next_variable, key->data, key->length);
  1202. state->next_variable += (int)key->length / sizeof(WCHAR);
  1203. *state->next_variable++ = L'=';
  1204. sys_memcpy(state->next_variable, value->data, value->length);
  1205. state->next_variable += (int)value->length / sizeof(WCHAR);
  1206. *state->next_variable++ = L'\0';
  1207. }
  1208. /* Builds an environment block suitable for CreateProcessW. */
  1209. static void *build_env_block(const erts_osenv_t *env) {
  1210. struct __build_env_state build_state;
  1211. WCHAR *env_block;
  1212. env_block = erts_alloc(ERTS_ALC_T_TMP, env->content_size +
  1213. (env->variable_count * sizeof(L"=\0") + sizeof(L'\0')));
  1214. build_state.next_variable = env_block;
  1215. erts_osenv_foreach_native(env, &build_state, build_env_foreach);
  1216. (*build_state.next_variable) = L'\0';
  1217. return env_block;
  1218. }
  1219. static int
  1220. create_file_thread(AsyncIo* aio, int mode)
  1221. {
  1222. DWORD tid; /* Id for thread. */
  1223. refer_driver_data(aio->dp);
  1224. aio->thread = (HANDLE)
  1225. _beginthreadex(NULL, 0,
  1226. (mode & DO_WRITE) ? threaded_writer : threaded_reader,
  1227. aio, 0, &tid);
  1228. if (aio->thread != (HANDLE) -1)
  1229. return 1;
  1230. unrefer_driver_data(aio->dp);
  1231. return 0;
  1232. }
  1233. /*
  1234. * A helper function used by create_child_process().
  1235. * Parses a command line with arguments and returns the length of the
  1236. * first part containing the program name.
  1237. * Example: input = "\"Program Files\"\\erl arg1 arg2"
  1238. * gives 19 as result.
  1239. * The length returned is equivalent with length(argv[0]) if the
  1240. * command line should have been prepared by _setargv for the main function
  1241. */
  1242. int parse_command(wchar_t* cmd){
  1243. #define NORMAL 2
  1244. #define STRING 1
  1245. #define STOP 0
  1246. int i =0;
  1247. int state = NORMAL;
  1248. while (cmd[i]) {
  1249. switch (cmd[i]) {
  1250. case L'"':
  1251. if (state == NORMAL)
  1252. state = STRING;
  1253. else
  1254. state = NORMAL;
  1255. break;
  1256. case L'\\':
  1257. if ((state == STRING) && (cmd[i+1]==L'"'))
  1258. i++;
  1259. break;
  1260. case L' ':
  1261. if (state == NORMAL)
  1262. state = STOP;
  1263. break;
  1264. default:
  1265. break;
  1266. }
  1267. if (state == STOP) {
  1268. return i;
  1269. }
  1270. i++;
  1271. }
  1272. return i;
  1273. }
  1274. /*
  1275. * Translating of command line arguments to correct format. In the examples
  1276. * below the '' are not part of the actual string.
  1277. * 'io:format("hello").' -> 'io:format(\"hello\").'
  1278. * 'io:format("is anybody in there?").' -> '"io:format(\"is anybody in there?\")."'
  1279. * 'Just nod if you can hear me.' -> '"Just nod if you can hear me."'
  1280. * 'Is there ""anyone at home?' -> '"Is there \"\"anyone at home?"'
  1281. * 'Relax."' -> 'Relax.\"'
  1282. *
  1283. * If new == NULL we just calculate the length.
  1284. *
  1285. * The reason for having to quote all of the is because CreateProcessW removes
  1286. * one level of escaping since it takes a single long command line rather
  1287. * than the argument chunks that unix uses.
  1288. */
  1289. static int escape_and_quote(wchar_t *str, wchar_t *new, BOOL *quoted) {
  1290. int i, j = 0;
  1291. if (new == NULL)
  1292. *quoted = FALSE;
  1293. else if (*quoted)
  1294. new[j++] = L'"';
  1295. for ( i = 0; str[i] != L'\0'; i++,j++) {
  1296. if (str[i] == L' ' && new == NULL && *quoted == FALSE) {
  1297. *quoted = TRUE;
  1298. j++;
  1299. }
  1300. /* check if we have to escape quotes */
  1301. if (str[i] == L'"') {
  1302. if (new) new[j] = L'\\';
  1303. j++;
  1304. }
  1305. if (new) new[j] = str[i];
  1306. }
  1307. if (*quoted) {
  1308. if (new) new[j] = L'"';
  1309. j++;
  1310. }
  1311. return j;
  1312. }
  1313. /*
  1314. *----------------------------------------------------------------------
  1315. *
  1316. * create_child_process --
  1317. *
  1318. * Create a child process that has pipes as its
  1319. * standard input, output, and error. The child process runs
  1320. * synchronously under Win32s and asynchronously under Windows NT
  1321. * and Windows 95, and runs with the same environment variables
  1322. * as the creating process.
  1323. *
  1324. * The complete Windows search path is searched to find the specified
  1325. * executable. If an executable by the given name is not found,
  1326. * automatically tries appending ".com", ".exe", and ".bat" to the
  1327. * executable name.
  1328. *
  1329. * Results:
  1330. * The return value is FALSE if there was a problem creating the child process.
  1331. * Otherwise, the return value is 0 and *phPid is
  1332. * filled with the process id of the child process.
  1333. *
  1334. * Side effects:
  1335. * A process is created.
  1336. *
  1337. *----------------------------------------------------------------------
  1338. */
  1339. static BOOL
  1340. create_child_process
  1341. (
  1342. wchar_t *origcmd, /* Command line for child process (including
  1343. * name of executable). Or whole executable if st is
  1344. * ERTS_SPAWN_EXECUTABLE
  1345. */
  1346. HANDLE hStdin, /* The standard input handle for child. */
  1347. HANDLE hStdout, /* The standard output handle for child. */
  1348. HANDLE hStderr, /* The standard error handle for child. */
  1349. LPHANDLE phPid, /* Pointer to variable to received Process handle. */
  1350. LPDWORD pdwID, /* Pointer to variable to received Process ID */
  1351. BOOL hide, /* Hide the window unconditionally. */
  1352. LPVOID env, /* Environment for the child */
  1353. wchar_t *wd, /* Working dir for the child */
  1354. unsigned st, /* Flags for spawn, tells us how to interpret origcmd */
  1355. wchar_t **argv, /* Argument vector if given. */
  1356. int *errno_return /* Place to put an errno in in case of failure */
  1357. )
  1358. {
  1359. PROCESS_INFORMATION piProcInfo = {0};
  1360. BOOL ok = FALSE;
  1361. int applType;
  1362. /* Not to be changed for different types of executables */
  1363. int staticCreateFlags = GetPriorityClass(GetCurrentProcess());
  1364. int createFlags = DETACHED_PROCESS;
  1365. wchar_t *newcmdline = NULL;
  1366. int cmdlength;
  1367. wchar_t* thecommand;
  1368. wchar_t* appname = NULL;
  1369. HANDLE hProcess = GetCurrentProcess();
  1370. STARTUPINFOW siStartInfo = {0};
  1371. wchar_t execPath[MAX_PATH];
  1372. *errno_return = -1;
  1373. siStartInfo.cb = sizeof(STARTUPINFOW);
  1374. siStartInfo.dwFlags = STARTF_USESTDHANDLES;
  1375. siStartInfo.hStdInput = hStdin;
  1376. siStartInfo.hStdOutput = hStdout;
  1377. siStartInfo.hStdError = hStderr;
  1378. if (st != ERTS_SPAWN_EXECUTABLE) {
  1379. /*
  1380. * Parse out the program name from the command line (it can be quoted and
  1381. * contain spaces).
  1382. */
  1383. cmdlength = parse_command(origcmd);
  1384. newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (MAX_PATH+wcslen(origcmd)-cmdlength)*sizeof(wchar_t));
  1385. thecommand = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (cmdlength+1)*sizeof(wchar_t));
  1386. wcsncpy(thecommand, origcmd, cmdlength);
  1387. thecommand[cmdlength] = L'\0';
  1388. DEBUGF(("spawn command: %S\n", thecommand));
  1389. applType = application_type(thecommand, execPath, TRUE, TRUE, errno_return);
  1390. DEBUGF(("application_type returned for (%S) is %d\n", thecommand, applType));
  1391. erts_free(ERTS_ALC_T_TMP, (void *) thecommand);
  1392. if (applType == APPL_NONE) {
  1393. erts_free(ERTS_ALC_T_TMP,newcmdline);
  1394. return FALSE;
  1395. }
  1396. newcmdline[0] = L'\0';
  1397. if (applType == APPL_DOS) {
  1398. /*
  1399. * Under NT, 16-bit DOS applications will not run unless they
  1400. * can be attached to a console. Run the 16-bit program as
  1401. * a normal process inside of a hidden console application,
  1402. * and then run that hidden console as a detached process.
  1403. */
  1404. siStartInfo.wShowWindow = SW_HIDE;
  1405. siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
  1406. createFlags = CREATE_NEW_CONSOLE;
  1407. wcscat(newcmdline, L"cmd.exe /c ");
  1408. } else if (hide) {
  1409. DEBUGF(("hiding window\n"));
  1410. siStartInfo.wShowWindow = SW_HIDE;
  1411. siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
  1412. createFlags = 0;
  1413. }
  1414. wcscat(newcmdline, execPath);
  1415. wcscat(newcmdline, origcmd+cmdlength);
  1416. DEBUGF(("Creating child process: %S, createFlags = %d\n", newcmdline, createFlags));
  1417. ok = CreateProcessW(appname,
  1418. newcmdline,
  1419. NULL,
  1420. NULL,
  1421. TRUE,
  1422. createFlags | staticCreateFlags |
  1423. CREATE_UNICODE_ENVIRONMENT,
  1424. env,
  1425. wd,
  1426. &siStartInfo,
  1427. &piProcInfo);
  1428. } else { /* ERTS_SPAWN_EXECUTABLE, filename and args are in unicode ({utf16,little}) */
  1429. int run_cmd = 0;
  1430. applType = application_type(origcmd, execPath, FALSE, FALSE, errno_return);
  1431. if (applType == APPL_NONE) {
  1432. return FALSE;
  1433. }
  1434. if (applType == APPL_DOS) {
  1435. /*
  1436. * See comment above
  1437. */
  1438. siStartInfo.wShowWindow = SW_HIDE;
  1439. siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
  1440. createFlags = CREATE_NEW_CONSOLE;
  1441. run_cmd = 1;
  1442. } else if (hide) {
  1443. DEBUGF(("hiding window\n"));
  1444. siStartInfo.wShowWindow = SW_HIDE;
  1445. siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
  1446. createFlags = 0;
  1447. }
  1448. if (run_cmd) {
  1449. wchar_t cmdPath[MAX_PATH];
  1450. int cmdType;
  1451. cmdType = application_type(L"cmd.exe", cmdPath, TRUE, FALSE, errno_return);
  1452. if (cmdType == APPL_NONE || cmdType == APPL_DOS) {
  1453. return FALSE;
  1454. }
  1455. appname = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (wcslen(cmdPath)+1)*sizeof(wchar_t));
  1456. wcscpy(appname,cmdPath);
  1457. } else {
  1458. appname = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (wcslen(execPath)+1)*sizeof(wchar_t));
  1459. wcscpy(appname, execPath);
  1460. }
  1461. if (argv == NULL) {
  1462. BOOL orig_need_q;
  1463. wchar_t *ptr;
  1464. int ocl = escape_and_quote(execPath, NULL, &orig_need_q);
  1465. if (run_cmd) {
  1466. newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP,
  1467. (ocl + 1 + 11)*sizeof(wchar_t));
  1468. memcpy(newcmdline,L"cmd.exe /c ",11*sizeof(wchar_t));
  1469. ptr = newcmdline + 11;
  1470. } else {
  1471. newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP,
  1472. (ocl + 1)*sizeof(wchar_t));
  1473. ptr = (wchar_t *) newcmdline;
  1474. }
  1475. ptr += escape_and_quote(execPath, ptr, &orig_need_q);
  1476. ptr[0] = L'\0';
  1477. } else {
  1478. int sum = 0;
  1479. BOOL *qte = NULL;
  1480. wchar_t **ar = argv;
  1481. wchar_t *n;
  1482. wchar_t *save_arg0 = NULL;
  1483. if (argv[0] == (wchar_t *) erts_default_arg0 || run_cmd) {
  1484. save_arg0 = argv[0];
  1485. argv[0] = execPath;
  1486. }
  1487. if (run_cmd) {
  1488. sum += 11; /* cmd.exe /c */
  1489. }
  1490. while (*ar != NULL) ar++;
  1491. qte = erts_alloc(ERTS_ALC_T_TMP, (ar - argv)*sizeof(BOOL));
  1492. ar = argv;
  1493. while (*ar != NULL) {
  1494. sum += escape_and_quote(*ar,NULL,qte+(ar - argv));
  1495. sum++; /* space */
  1496. ++ar;
  1497. }
  1498. ar = argv;
  1499. newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, sum*sizeof(wchar_t));
  1500. n = newcmdline;
  1501. if (run_cmd) {
  1502. memcpy(n,L"cmd.exe /c ",11*sizeof(wchar_t));
  1503. n += 11;
  1504. }
  1505. while (*ar != NULL) {
  1506. n += escape_and_quote(*ar,n,qte+(ar - argv));
  1507. *n++ = L' ';
  1508. ++ar;
  1509. }
  1510. *(n-1) = L'\0'; /* overwrite last space with '\0' */
  1511. if (save_arg0 != NULL) {
  1512. argv[0] = save_arg0;
  1513. }
  1514. erts_free(ERTS_ALC_T_TMP, qte);
  1515. }
  1516. DEBUGF((stderr,"Creating child process: %S, createFlags = %d\n", newcmdline, createFlags));
  1517. ok = CreateProcessW((wchar_t *) appname,
  1518. (wchar_t *) newcmdline,
  1519. NULL,
  1520. NULL,
  1521. TRUE,
  1522. createFlags | staticCreateFlags |
  1523. CREATE_UNICODE_ENVIRONMENT,
  1524. env,
  1525. wd,
  1526. &siStartInfo,
  1527. &piProcInfo);
  1528. } /* end SPAWN_EXECUTABLE */
  1529. if (newcmdline != NULL) {
  1530. erts_free(ERTS_ALC_T_TMP,newcmdline);
  1531. }
  1532. if (appname != NULL) {
  1533. erts_free(ERTS_ALC_T_TMP,appname);
  1534. }
  1535. if (!ok) {
  1536. DEBUGF(("CreateProcess failed: %s\n", last_error()));
  1537. if (*errno_return < 0) {
  1538. *errno_return = EACCES;
  1539. }
  1540. return FALSE;
  1541. }
  1542. CloseHandle(piProcInfo.hThread); /* Necessary to avoid resource leak. */
  1543. *phPid = piProcInfo.hProcess;
  1544. *pdwID = piProcInfo.dwProcessId;
  1545. if (applType == APPL_DOS) {
  1546. WaitForSingleObject(hProcess, 50);
  1547. }
  1548. return ok;
  1549. }
  1550. /*
  1551. * Note, inheritRead == FALSE means "inhetitWrite", i e one of the
  1552. * pipe ends is always expected to be inherited. The pipe end that should
  1553. * be inherited is opened without overlapped io flags, as the child program
  1554. * would expect stdout not to demand overlapped I/O.
  1555. */
  1556. static int create_pipe(HANDLE *phRead, HANDLE *phWrite, BOOL inheritRead, BOOL overlapped_io)
  1557. {
  1558. SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
  1559. char pipe_name[256]; /* Name of pipe. */
  1560. Uint calls;
  1561. /*
  1562. * If we should't use named pipes, create anonmous pipes.
  1563. */
  1564. if (!use_named_pipes) {
  1565. int success;
  1566. HANDLE non_inherited; /* Non-inherited copy of handle. */
  1567. if (!CreatePipe(phRead, phWrite, &sa, 0)) {
  1568. DEBUGF(("Error creating anonyomous pipe: %s\n", last_error()));
  1569. return FALSE;

Large files files are truncated, but you can click here to view the full file