PageRenderTime 69ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://github.com/Bwooce/otp
C | 3428 lines | 3075 code | 146 blank | 207 comment | 199 complexity | b34ffb891953f3c9b392fe7b2a669ab2 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-2-Clause

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

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

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