X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fser-mingw.c;h=e6363a1053d93e6522a4119f4a5d0a13b9977eb4;hb=71780f455fbf35ed4c48e94b4228c55c11a213c8;hp=6621d613e924d97995484ad8e242493dab67601a;hpb=6aba47ca06d9150c6196a374b745c2711b46e045;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/ser-mingw.c b/gdb/ser-mingw.c index 6621d613e9..e6363a1053 100644 --- a/gdb/ser-mingw.c +++ b/gdb/ser-mingw.c @@ -1,12 +1,12 @@ /* Serial interface for local (hardwired) serial ports on Windows systems - Copyright (C) 2006, 2007 Free Software Foundation, Inc. + Copyright (C) 2006-2020 Free Software Foundation, Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -15,9 +15,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program. If not, see . */ #include "defs.h" #include "serial.h" @@ -31,10 +29,7 @@ #include #include -#include "gdb_assert.h" -#include "gdb_string.h" - -void _initialize_ser_windows (void); +#include "command.h" struct ser_windows_state { @@ -44,6 +39,12 @@ struct ser_windows_state HANDLE except_event; }; +/* CancelIo is not available for Windows 95 OS, so we need to use + LoadLibrary/GetProcAddress to avoid a startup failure. */ +#define CancelIo dyn_CancelIo +typedef BOOL WINAPI (CancelIo_ftype) (HANDLE); +static CancelIo_ftype *CancelIo; + /* Open up a real live device for serial I/O. */ static int @@ -53,13 +54,6 @@ ser_windows_open (struct serial *scb, const char *name) struct ser_windows_state *state; COMMTIMEOUTS timeouts; - /* Only allow COM ports. */ - if (strncmp (name, "COM", 3) != 0) - { - errno = ENOENT; - return -1; - } - h = CreateFile (name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (h == INVALID_HANDLE_VALUE) @@ -68,7 +62,7 @@ ser_windows_open (struct serial *scb, const char *name) return -1; } - scb->fd = _open_osfhandle ((long) h, O_RDWR); + scb->fd = _open_osfhandle ((intptr_t) h, O_RDWR); if (scb->fd < 0) { errno = ENOENT; @@ -92,8 +86,7 @@ ser_windows_open (struct serial *scb, const char *name) return -1; } - state = xmalloc (sizeof (struct ser_windows_state)); - memset (state, 0, sizeof (struct ser_windows_state)); + state = XCNEW (struct ser_windows_state); scb->state = state; /* Create a manual reset event to watch the input buffer. */ @@ -158,7 +151,6 @@ ser_windows_raw (struct serial *scb) if (GetCommState (h, &state) == 0) return; - state.fParity = FALSE; state.fOutxCtsFlow = FALSE; state.fOutxDsrFlow = FALSE; state.fDtrControl = DTR_CONTROL_ENABLE; @@ -168,12 +160,9 @@ ser_windows_raw (struct serial *scb) state.fNull = FALSE; state.fAbortOnError = FALSE; state.ByteSize = 8; - state.Parity = NOPARITY; - - scb->current_timeout = 0; if (SetCommState (h, &state) == 0) - warning (_("SetCommState failed\n")); + warning (_("SetCommState failed")); } static int @@ -203,6 +192,40 @@ ser_windows_setstopbits (struct serial *scb, int num) return (SetCommState (h, &state) != 0) ? 0 : -1; } +/* Implement the "setparity" serial_ops callback. */ + +static int +ser_windows_setparity (struct serial *scb, int parity) +{ + HANDLE h = (HANDLE) _get_osfhandle (scb->fd); + DCB state; + + if (GetCommState (h, &state) == 0) + return -1; + + switch (parity) + { + case GDBPARITY_NONE: + state.Parity = NOPARITY; + state.fParity = FALSE; + break; + case GDBPARITY_ODD: + state.Parity = ODDPARITY; + state.fParity = TRUE; + break; + case GDBPARITY_EVEN: + state.Parity = EVENPARITY; + state.fParity = TRUE; + break; + default: + internal_warning (__FILE__, __LINE__, + "Incorrect parity value: %d", parity); + return -1; + } + + return (SetCommState (h, &state) != 0) ? 0 : -1; +} + static int ser_windows_setbaudrate (struct serial *scb, int rate) { @@ -222,9 +245,13 @@ ser_windows_close (struct serial *scb) { struct ser_windows_state *state; - /* Stop any pending selects. */ - CancelIo ((HANDLE) _get_osfhandle (scb->fd)); - state = scb->state; + /* Stop any pending selects. On Windows 95 OS, CancelIo function does + not exist. In that case, it can be replaced by a call to CloseHandle, + but this is not necessary here as we do close the Windows handle + by calling close (scb->fd) below. */ + if (CancelIo) + CancelIo ((HANDLE) _get_osfhandle (scb->fd)); + state = (struct ser_windows_state *) scb->state; CloseHandle (state->ov.hEvent); CloseHandle (state->except_event); @@ -245,7 +272,7 @@ ser_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) DWORD errors; HANDLE h = (HANDLE) _get_osfhandle (scb->fd); - state = scb->state; + state = (struct ser_windows_state *) scb->state; *except = state->except_event; *read = state->ov.hEvent; @@ -294,11 +321,10 @@ ser_windows_read_prim (struct serial *scb, size_t count) { struct ser_windows_state *state; OVERLAPPED ov; - DWORD bytes_read, bytes_read_tmp; + DWORD bytes_read; HANDLE h; - gdb_byte *p; - state = scb->state; + state = (struct ser_windows_state *) scb->state; if (state->in_progress) { WaitForSingleObject (state->ov.hEvent, INFINITE); @@ -324,7 +350,6 @@ ser_windows_read_prim (struct serial *scb, size_t count) static int ser_windows_write_prim (struct serial *scb, const void *buf, size_t len) { - struct ser_windows_state *state; OVERLAPPED ov; DWORD bytes_written; HANDLE h; @@ -343,28 +368,171 @@ ser_windows_write_prim (struct serial *scb, const void *buf, size_t len) return bytes_written; } +/* On Windows, gdb_select is implemented using WaitForMulpleObjects. + A "select thread" is created for each file descriptor. These + threads looks for activity on the corresponding descriptor, using + whatever techniques are appropriate for the descriptor type. When + that activity occurs, the thread signals an appropriate event, + which wakes up WaitForMultipleObjects. + + Each select thread is in one of two states: stopped or started. + Select threads begin in the stopped state. When gdb_select is + called, threads corresponding to the descriptors of interest are + started by calling a wait_handle function. Each thread that + notices activity signals the appropriate event and then reenters + the stopped state. Before gdb_select returns it calls the + wait_handle_done functions, which return the threads to the stopped + state. */ + +enum select_thread_state { + STS_STARTED, + STS_STOPPED +}; + struct ser_console_state { + /* Signaled by the select thread to indicate that data is available + on the file descriptor. */ HANDLE read_event; + /* Signaled by the select thread to indicate that an exception has + occurred on the file descriptor. */ HANDLE except_event; + /* Signaled by the select thread to indicate that it has entered the + started state. HAVE_STARTED and HAVE_STOPPED are never signaled + simultaneously. */ + HANDLE have_started; + /* Signaled by the select thread to indicate that it has stopped, + either because data is available (and READ_EVENT is signaled), + because an exception has occurred (and EXCEPT_EVENT is signaled), + or because STOP_SELECT was signaled. */ + HANDLE have_stopped; + /* Signaled by the main program to tell the select thread to enter + the started state. */ HANDLE start_select; + /* Signaled by the main program to tell the select thread to enter + the stopped state. */ HANDLE stop_select; + /* Signaled by the main program to tell the select thread to + exit. */ HANDLE exit_select; - HANDLE have_stopped; + /* The handle for the select thread. */ HANDLE thread; + /* The state of the select thread. This field is only accessed in + the main program, never by the select thread itself. */ + enum select_thread_state thread_state; }; +/* Called by a select thread to enter the stopped state. This + function does not return until the thread has re-entered the + started state. */ +static void +select_thread_wait (struct ser_console_state *state) +{ + HANDLE wait_events[2]; + + /* There are two things that can wake us up: a request that we enter + the started state, or that we exit this thread. */ + wait_events[0] = state->start_select; + wait_events[1] = state->exit_select; + if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) + != WAIT_OBJECT_0) + /* Either the EXIT_SELECT event was signaled (requesting that the + thread exit) or an error has occurred. In either case, we exit + the thread. */ + ExitThread (0); + + /* We are now in the started state. */ + SetEvent (state->have_started); +} + +typedef DWORD WINAPI (*thread_fn_type)(void *); + +/* Create a new select thread for SCB executing THREAD_FN. The STATE + will be filled in by this function before return. */ +static void +create_select_thread (thread_fn_type thread_fn, + struct serial *scb, + struct ser_console_state *state) +{ + DWORD threadId; + + /* Create all of the events. These are all auto-reset events. */ + state->read_event = CreateEvent (NULL, FALSE, FALSE, NULL); + state->except_event = CreateEvent (NULL, FALSE, FALSE, NULL); + state->have_started = CreateEvent (NULL, FALSE, FALSE, NULL); + state->have_stopped = CreateEvent (NULL, FALSE, FALSE, NULL); + state->start_select = CreateEvent (NULL, FALSE, FALSE, NULL); + state->stop_select = CreateEvent (NULL, FALSE, FALSE, NULL); + state->exit_select = CreateEvent (NULL, FALSE, FALSE, NULL); + + state->thread = CreateThread (NULL, 0, thread_fn, scb, 0, &threadId); + /* The thread begins in the stopped state. */ + state->thread_state = STS_STOPPED; +} + +/* Destroy the select thread indicated by STATE. */ +static void +destroy_select_thread (struct ser_console_state *state) +{ + /* Ask the thread to exit. */ + SetEvent (state->exit_select); + /* Wait until it does. */ + WaitForSingleObject (state->thread, INFINITE); + + /* Destroy the events. */ + CloseHandle (state->read_event); + CloseHandle (state->except_event); + CloseHandle (state->have_started); + CloseHandle (state->have_stopped); + CloseHandle (state->start_select); + CloseHandle (state->stop_select); + CloseHandle (state->exit_select); +} + +/* Called by gdb_select to start the select thread indicated by STATE. + This function does not return until the thread has started. */ +static void +start_select_thread (struct ser_console_state *state) +{ + /* Ask the thread to start. */ + SetEvent (state->start_select); + /* Wait until it does. */ + WaitForSingleObject (state->have_started, INFINITE); + /* The thread is now started. */ + state->thread_state = STS_STARTED; +} + +/* Called by gdb_select to stop the select thread indicated by STATE. + This function does not return until the thread has stopped. */ +static void +stop_select_thread (struct ser_console_state *state) +{ + /* If the thread is already in the stopped state, we have nothing to + do. Some of the wait_handle functions avoid calling + start_select_thread if they notice activity on the relevant file + descriptors. The wait_handle_done functions still call + stop_select_thread -- but it is already stopped. */ + if (state->thread_state != STS_STARTED) + return; + /* Ask the thread to stop. */ + SetEvent (state->stop_select); + /* Wait until it does. */ + WaitForSingleObject (state->have_stopped, INFINITE); + /* The thread is now stopped. */ + state->thread_state = STS_STOPPED; +} + static DWORD WINAPI console_select_thread (void *arg) { - struct serial *scb = arg; + struct serial *scb = (struct serial *) arg; struct ser_console_state *state; int event_index; HANDLE h; - state = scb->state; + state = (struct ser_console_state *) scb->state; h = (HANDLE) _get_osfhandle (scb->fd); while (1) @@ -373,75 +541,72 @@ console_select_thread (void *arg) INPUT_RECORD record; DWORD n_records; - SetEvent (state->have_stopped); - - wait_events[0] = state->start_select; - wait_events[1] = state->exit_select; - - if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) - return 0; + select_thread_wait (state); - ResetEvent (state->have_stopped); - - retry: - wait_events[0] = state->stop_select; - wait_events[1] = h; + while (1) + { + wait_events[0] = state->stop_select; + wait_events[1] = h; - event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE); + event_index = WaitForMultipleObjects (2, wait_events, + FALSE, INFINITE); - if (event_index == WAIT_OBJECT_0 - || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) - continue; + if (event_index == WAIT_OBJECT_0 + || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) + break; - if (event_index != WAIT_OBJECT_0 + 1) - { - /* Wait must have failed; assume an error has occured, e.g. - the handle has been closed. */ - SetEvent (state->except_event); - continue; - } + if (event_index != WAIT_OBJECT_0 + 1) + { + /* Wait must have failed; assume an error has occured, e.g. + the handle has been closed. */ + SetEvent (state->except_event); + break; + } - /* We've got a pending event on the console. See if it's - of interest. */ - if (!PeekConsoleInput (h, &record, 1, &n_records) || n_records != 1) - { - /* Something went wrong. Maybe the console is gone. */ - SetEvent (state->except_event); - continue; - } + /* We've got a pending event on the console. See if it's + of interest. */ + if (!PeekConsoleInput (h, &record, 1, &n_records) || n_records != 1) + { + /* Something went wrong. Maybe the console is gone. */ + SetEvent (state->except_event); + break; + } - if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown) - { - WORD keycode = record.Event.KeyEvent.wVirtualKeyCode; - - /* Ignore events containing only control keys. We must - recognize "enhanced" keys which we are interested in - reading via getch, if they do not map to ASCII. But we - do not want to report input available for e.g. the - control key alone. */ - - if (record.Event.KeyEvent.uChar.AsciiChar != 0 - || keycode == VK_PRIOR - || keycode == VK_NEXT - || keycode == VK_END - || keycode == VK_HOME - || keycode == VK_LEFT - || keycode == VK_UP - || keycode == VK_RIGHT - || keycode == VK_DOWN - || keycode == VK_INSERT - || keycode == VK_DELETE) + if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown) { - /* This is really a keypress. */ - SetEvent (state->read_event); - continue; + WORD keycode = record.Event.KeyEvent.wVirtualKeyCode; + + /* Ignore events containing only control keys. We must + recognize "enhanced" keys which we are interested in + reading via getch, if they do not map to ASCII. But we + do not want to report input available for e.g. the + control key alone. */ + + if (record.Event.KeyEvent.uChar.AsciiChar != 0 + || keycode == VK_PRIOR + || keycode == VK_NEXT + || keycode == VK_END + || keycode == VK_HOME + || keycode == VK_LEFT + || keycode == VK_UP + || keycode == VK_RIGHT + || keycode == VK_DOWN + || keycode == VK_INSERT + || keycode == VK_DELETE) + { + /* This is really a keypress. */ + SetEvent (state->read_event); + break; + } } + + /* Otherwise discard it and wait again. */ + ReadConsoleInput (h, &record, 1, &n_records); } - /* Otherwise discard it and wait again. */ - ReadConsoleInput (h, &record, 1, &n_records); - goto retry; + SetEvent(state->have_stopped); } + return 0; } static int @@ -453,97 +618,111 @@ fd_is_pipe (int fd) return 0; } +static int +fd_is_file (int fd) +{ + if (GetFileType ((HANDLE) _get_osfhandle (fd)) == FILE_TYPE_DISK) + return 1; + else + return 0; +} + static DWORD WINAPI pipe_select_thread (void *arg) { - struct serial *scb = arg; + struct serial *scb = (struct serial *) arg; struct ser_console_state *state; - int event_index; HANDLE h; - state = scb->state; + state = (struct ser_console_state *) scb->state; h = (HANDLE) _get_osfhandle (scb->fd); while (1) { - HANDLE wait_events[2]; DWORD n_avail; - SetEvent (state->have_stopped); - - wait_events[0] = state->start_select; - wait_events[1] = state->exit_select; + select_thread_wait (state); - if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) - return 0; + /* Wait for something to happen on the pipe. */ + while (1) + { + if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL)) + { + SetEvent (state->except_event); + break; + } - ResetEvent (state->have_stopped); + if (n_avail > 0) + { + SetEvent (state->read_event); + break; + } - retry: - if (!PeekNamedPipe (h, NULL, 0, NULL, &n_avail, NULL)) - { - SetEvent (state->except_event); - continue; + /* Delay 10ms before checking again, but allow the stop + event to wake us. */ + if (WaitForSingleObject (state->stop_select, 10) == WAIT_OBJECT_0) + break; } - if (n_avail > 0) - { - SetEvent (state->read_event); - continue; - } + SetEvent (state->have_stopped); + } + return 0; +} + +static DWORD WINAPI +file_select_thread (void *arg) +{ + struct serial *scb = (struct serial *) arg; + struct ser_console_state *state; + HANDLE h; + + state = (struct ser_console_state *) scb->state; + h = (HANDLE) _get_osfhandle (scb->fd); + + while (1) + { + select_thread_wait (state); - /* Delay 10ms before checking again, but allow the stop event - to wake us. */ - if (WaitForSingleObject (state->stop_select, 10) == WAIT_OBJECT_0) - continue; + if (SetFilePointer (h, 0, NULL, FILE_CURRENT) + == INVALID_SET_FILE_POINTER) + SetEvent (state->except_event); + else + SetEvent (state->read_event); - goto retry; + SetEvent (state->have_stopped); } + return 0; } static void ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) { - struct ser_console_state *state = scb->state; + struct ser_console_state *state = (struct ser_console_state *) scb->state; if (state == NULL) { - DWORD threadId; + thread_fn_type thread_fn; int is_tty; is_tty = isatty (scb->fd); - if (!is_tty && !fd_is_pipe (scb->fd)) + if (!is_tty && !fd_is_file (scb->fd) && !fd_is_pipe (scb->fd)) { *read = NULL; *except = NULL; return; } - state = xmalloc (sizeof (struct ser_console_state)); - memset (state, 0, sizeof (struct ser_console_state)); + state = XCNEW (struct ser_console_state); scb->state = state; - /* Create auto reset events to wake, stop, and exit the select - thread. */ - state->start_select = CreateEvent (0, FALSE, FALSE, 0); - state->stop_select = CreateEvent (0, FALSE, FALSE, 0); - state->exit_select = CreateEvent (0, FALSE, FALSE, 0); - - /* Create a manual reset event to signal whether the thread is - stopped. This must be manual reset, because we may wait on - it multiple times without ever starting the thread. */ - state->have_stopped = CreateEvent (0, TRUE, FALSE, 0); - - /* Create our own events to report read and exceptions separately. */ - state->read_event = CreateEvent (0, FALSE, FALSE, 0); - state->except_event = CreateEvent (0, FALSE, FALSE, 0); - if (is_tty) - state->thread = CreateThread (NULL, 0, console_select_thread, scb, 0, - &threadId); + thread_fn = console_select_thread; + else if (fd_is_pipe (scb->fd)) + thread_fn = pipe_select_thread; else - state->thread = CreateThread (NULL, 0, pipe_select_thread, scb, 0, - &threadId); + thread_fn = file_select_thread; + + create_select_thread (thread_fn, scb, state); } *read = state->read_event; @@ -566,40 +745,28 @@ ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) } /* Otherwise, start the select thread. */ - SetEvent (state->start_select); + start_select_thread (state); } static void ser_console_done_wait_handle (struct serial *scb) { - struct ser_console_state *state = scb->state; + struct ser_console_state *state = (struct ser_console_state *) scb->state; if (state == NULL) return; - SetEvent (state->stop_select); - WaitForSingleObject (state->have_stopped, INFINITE); + stop_select_thread (state); } static void ser_console_close (struct serial *scb) { - struct ser_console_state *state = scb->state; + struct ser_console_state *state = (struct ser_console_state *) scb->state; if (scb->state) { - SetEvent (state->exit_select); - - WaitForSingleObject (state->thread, INFINITE); - - CloseHandle (state->start_select); - CloseHandle (state->stop_select); - CloseHandle (state->exit_select); - CloseHandle (state->have_stopped); - - CloseHandle (state->read_event); - CloseHandle (state->except_event); - + destroy_select_thread (state); xfree (scb->state); } } @@ -615,7 +782,8 @@ ser_console_get_tty_state (struct serial *scb) if (isatty (scb->fd)) { struct ser_console_ttystate *state; - state = (struct ser_console_ttystate *) xmalloc (sizeof *state); + + state = XNEW (struct ser_console_ttystate); state->is_a_tty = 1; return state; } @@ -640,9 +808,8 @@ struct pipe_state static struct pipe_state * make_pipe_state (void) { - struct pipe_state *ps = XMALLOC (struct pipe_state); + struct pipe_state *ps = XCNEW (struct pipe_state); - memset (ps, 0, sizeof (*ps)); ps->wait.read_event = INVALID_HANDLE_VALUE; ps->wait.except_event = INVALID_HANDLE_VALUE; ps->wait.start_select = INVALID_HANDLE_VALUE; @@ -657,19 +824,7 @@ free_pipe_state (struct pipe_state *ps) int saved_errno = errno; if (ps->wait.read_event != INVALID_HANDLE_VALUE) - { - SetEvent (ps->wait.exit_select); - - WaitForSingleObject (ps->wait.thread, INFINITE); - - CloseHandle (ps->wait.start_select); - CloseHandle (ps->wait.stop_select); - CloseHandle (ps->wait.exit_select); - CloseHandle (ps->wait.have_stopped); - - CloseHandle (ps->wait.read_event); - CloseHandle (ps->wait.except_event); - } + destroy_select_thread (&ps->wait); /* Close the pipe to the child. We must close the pipe before calling pex_free because pex_free will wait for the child to exit @@ -677,45 +832,56 @@ free_pipe_state (struct pipe_state *ps) if (ps->input) fclose (ps->input); if (ps->pex) - pex_free (ps->pex); - /* pex_free closes ps->output. */ + { + pex_free (ps->pex); + /* pex_free closes ps->output. */ + } + else if (ps->output) + fclose (ps->output); xfree (ps); errno = saved_errno; } -static void -cleanup_pipe_state (void *untyped) +struct pipe_state_destroyer { - struct pipe_state *ps = untyped; + void operator() (pipe_state *ps) const + { + free_pipe_state (ps); + } +}; - free_pipe_state (ps); -} +typedef std::unique_ptr pipe_state_up; static int pipe_windows_open (struct serial *scb, const char *name) { - char **argv = buildargv (name); - struct cleanup *back_to = make_cleanup_freeargv (argv); + FILE *pex_stderr; + + if (name == NULL) + error_no_arg (_("child command")); + + gdb_argv argv (name); + if (! argv[0] || argv[0][0] == '\0') - error ("missing child command"); + error (_("missing child command")); - struct pipe_state *ps = make_pipe_state (); - make_cleanup (cleanup_pipe_state, ps); + pipe_state_up ps (make_pipe_state ()); ps->pex = pex_init (PEX_USE_PIPES, "target remote pipe", NULL); if (! ps->pex) - goto fail; + return -1; ps->input = pex_input_pipe (ps->pex, 1); if (! ps->input) - goto fail; + return -1; { int err; const char *err_msg - = pex_run (ps->pex, PEX_SEARCH | PEX_BINARY_INPUT | PEX_BINARY_OUTPUT, - argv[0], argv, NULL, NULL, + = pex_run (ps->pex, PEX_SEARCH | PEX_BINARY_INPUT | PEX_BINARY_OUTPUT + | PEX_STDERR_TO_PIPE, + argv[0], argv.get (), NULL, NULL, &err); if (err_msg) @@ -725,34 +891,58 @@ pipe_windows_open (struct serial *scb, const char *name) all the same information here, plus err_msg provided by pex_run, so we just raise the error here. */ if (err) - error ("error starting child process '%s': %s: %s", + error (_("error starting child process '%s': %s: %s"), name, err_msg, safe_strerror (err)); else - error ("error starting child process '%s': %s", + error (_("error starting child process '%s': %s"), name, err_msg); } } ps->output = pex_read_output (ps->pex, 1); if (! ps->output) + return -1; + scb->fd = fileno (ps->output); + + pex_stderr = pex_read_err (ps->pex, 1); + if (! pex_stderr) + return -1; + scb->error_fd = fileno (pex_stderr); + + scb->state = ps.release (); + + return 0; +} + +static int +pipe_windows_fdopen (struct serial *scb, int fd) +{ + struct pipe_state *ps; + + ps = make_pipe_state (); + + ps->input = fdopen (fd, "r+"); + if (! ps->input) goto fail; - scb->fd = fileno (ps->output); + ps->output = fdopen (fd, "r+"); + if (! ps->output) + goto fail; + + scb->fd = fd; scb->state = (void *) ps; - discard_cleanups (back_to); return 0; fail: - do_cleanups (back_to); + free_pipe_state (ps); return -1; } - static void pipe_windows_close (struct serial *scb) { - struct pipe_state *ps = scb->state; + struct pipe_state *ps = (struct pipe_state *) scb->state; /* In theory, we should try to kill the subprocess here, but the pex interface doesn't give us enough information to do that. Usually @@ -766,17 +956,18 @@ static int pipe_windows_read (struct serial *scb, size_t count) { HANDLE pipeline_out = (HANDLE) _get_osfhandle (scb->fd); + DWORD available; + DWORD bytes_read; + if (pipeline_out == INVALID_HANDLE_VALUE) return -1; - DWORD available; if (! PeekNamedPipe (pipeline_out, NULL, 0, NULL, &available, NULL)) return -1; if (count > available) count = available; - DWORD bytes_read; if (! ReadFile (pipeline_out, scb->buf, count, &bytes_read, NULL)) return -1; @@ -787,16 +978,18 @@ pipe_windows_read (struct serial *scb, size_t count) static int pipe_windows_write (struct serial *scb, const void *buf, size_t count) { - struct pipe_state *ps = scb->state; + struct pipe_state *ps = (struct pipe_state *) scb->state; + HANDLE pipeline_in; + DWORD written; + int pipeline_in_fd = fileno (ps->input); if (pipeline_in_fd < 0) return -1; - HANDLE pipeline_in = (HANDLE) _get_osfhandle (pipeline_in_fd); + pipeline_in = (HANDLE) _get_osfhandle (pipeline_in_fd); if (pipeline_in == INVALID_HANDLE_VALUE) return -1; - DWORD written; if (! WriteFile (pipeline_in, buf, count, &written, NULL)) return -1; @@ -807,32 +1000,12 @@ pipe_windows_write (struct serial *scb, const void *buf, size_t count) static void pipe_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) { - struct pipe_state *ps = scb->state; + struct pipe_state *ps = (struct pipe_state *) scb->state; /* Have we allocated our events yet? */ if (ps->wait.read_event == INVALID_HANDLE_VALUE) - { - DWORD threadId; - - /* Create auto reset events to wake, stop, and exit the select - thread. */ - ps->wait.start_select = CreateEvent (0, FALSE, FALSE, 0); - ps->wait.stop_select = CreateEvent (0, FALSE, FALSE, 0); - ps->wait.exit_select = CreateEvent (0, FALSE, FALSE, 0); - - /* Create a manual reset event to signal whether the thread is - stopped. This must be manual reset, because we may wait on - it multiple times without ever starting the thread. */ - ps->wait.have_stopped = CreateEvent (0, TRUE, FALSE, 0); - - /* Create our own events to report read and exceptions separately. - The exception event is currently never used. */ - ps->wait.read_event = CreateEvent (0, FALSE, FALSE, 0); - ps->wait.except_event = CreateEvent (0, FALSE, FALSE, 0); - - /* Start the select thread. */ - CreateThread (NULL, 0, pipe_select_thread, scb, 0, &threadId); - } + /* Start the thread. */ + create_select_thread (pipe_select_thread, scb, &ps->wait); *read = ps->wait.read_event; *except = ps->wait.except_event; @@ -842,170 +1015,168 @@ pipe_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) ResetEvent (ps->wait.except_event); ResetEvent (ps->wait.stop_select); - /* Start the select thread. */ - SetEvent (ps->wait.start_select); + start_select_thread (&ps->wait); } static void pipe_done_wait_handle (struct serial *scb) { - struct pipe_state *ps = scb->state; + struct pipe_state *ps = (struct pipe_state *) scb->state; /* Have we allocated our events yet? */ if (ps->wait.read_event == INVALID_HANDLE_VALUE) return; - SetEvent (ps->wait.stop_select); - WaitForSingleObject (ps->wait.have_stopped, INFINITE); + stop_select_thread (&ps->wait); } -struct net_windows_state +static int +pipe_avail (struct serial *scb, int fd) { - HANDLE read_event; - HANDLE except_event; + HANDLE h = (HANDLE) _get_osfhandle (fd); + DWORD numBytes; + BOOL r = PeekNamedPipe (h, NULL, 0, NULL, &numBytes, NULL); - HANDLE start_select; - HANDLE stop_select; - HANDLE exit_select; - HANDLE have_stopped; + if (r == FALSE) + numBytes = 0; + return numBytes; +} - HANDLE sock_event; +int +gdb_pipe (int pdes[2]) +{ + if (_pipe (pdes, 512, _O_BINARY | _O_NOINHERIT) == -1) + return -1; + return 0; +} - HANDLE thread; +struct net_windows_state +{ + struct ser_console_state base; + + HANDLE sock_event; }; +/* Check whether the socket has any pending data to be read. If so, + set the select thread's read event. On error, set the select + thread's except event. If any event was set, return true, + otherwise return false. */ + +static int +net_windows_socket_check_pending (struct serial *scb) +{ + struct net_windows_state *state = (struct net_windows_state *) scb->state; + unsigned long available; + + if (ioctlsocket (scb->fd, FIONREAD, &available) != 0) + { + /* The socket closed, or some other error. */ + SetEvent (state->base.except_event); + return 1; + } + else if (available > 0) + { + SetEvent (state->base.read_event); + return 1; + } + + return 0; +} + static DWORD WINAPI net_windows_select_thread (void *arg) { - struct serial *scb = arg; - struct net_windows_state *state, state_copy; + struct serial *scb = (struct serial *) arg; + struct net_windows_state *state; int event_index; - state = scb->state; + state = (struct net_windows_state *) scb->state; while (1) { HANDLE wait_events[2]; WSANETWORKEVENTS events; - SetEvent (state->have_stopped); - - wait_events[0] = state->start_select; - wait_events[1] = state->exit_select; - - if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0) - return 0; + select_thread_wait (&state->base); - ResetEvent (state->have_stopped); - - wait_events[0] = state->stop_select; + wait_events[0] = state->base.stop_select; wait_events[1] = state->sock_event; - event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE); + /* Wait for something to happen on the socket. */ + while (1) + { + event_index = WaitForMultipleObjects (2, wait_events, FALSE, INFINITE); - if (event_index == WAIT_OBJECT_0 - || WaitForSingleObject (state->stop_select, 0) == WAIT_OBJECT_0) - continue; + if (event_index == WAIT_OBJECT_0 + || WaitForSingleObject (state->base.stop_select, 0) == WAIT_OBJECT_0) + { + /* We have been requested to stop. */ + break; + } - if (event_index != WAIT_OBJECT_0 + 1) - { - /* Some error has occured. Assume that this is an error - condition. */ - SetEvent (state->except_event); - continue; - } + if (event_index != WAIT_OBJECT_0 + 1) + { + /* Some error has occured. Assume that this is an error + condition. */ + SetEvent (state->base.except_event); + break; + } - /* Enumerate the internal network events, and reset the object that - signalled us to catch the next event. */ - WSAEnumNetworkEvents (scb->fd, state->sock_event, &events); + /* Enumerate the internal network events, and reset the + object that signalled us to catch the next event. */ + if (WSAEnumNetworkEvents (scb->fd, state->sock_event, &events) != 0) + { + /* Something went wrong. Maybe the socket is gone. */ + SetEvent (state->base.except_event); + break; + } - gdb_assert (events.lNetworkEvents & (FD_READ | FD_CLOSE)); + if (events.lNetworkEvents & FD_READ) + { + if (net_windows_socket_check_pending (scb)) + break; - if (events.lNetworkEvents & FD_READ) - SetEvent (state->read_event); + /* Spurious wakeup. That is, the socket's event was + signalled before we last called recv. */ + } - if (events.lNetworkEvents & FD_CLOSE) - SetEvent (state->except_event); + if (events.lNetworkEvents & FD_CLOSE) + { + SetEvent (state->base.except_event); + break; + } + } + + SetEvent (state->base.have_stopped); } + return 0; } static void net_windows_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) { - struct net_windows_state *state = scb->state; + struct net_windows_state *state = (struct net_windows_state *) scb->state; /* Start from a clean slate. */ - ResetEvent (state->read_event); - ResetEvent (state->except_event); - ResetEvent (state->stop_select); - - *read = state->read_event; - *except = state->except_event; + ResetEvent (state->base.read_event); + ResetEvent (state->base.except_event); + ResetEvent (state->base.stop_select); - /* Check any pending events. This both avoids starting the thread - unnecessarily, and handles stray FD_READ events (see below). */ - if (WaitForSingleObject (state->sock_event, 0) == WAIT_OBJECT_0) - { - WSANETWORKEVENTS events; - int any = 0; - - /* Enumerate the internal network events, and reset the object that - signalled us to catch the next event. */ - WSAEnumNetworkEvents (scb->fd, state->sock_event, &events); - - /* You'd think that FD_READ or FD_CLOSE would be set here. But, - sometimes, neither is. I suspect that the FD_READ is set and - the corresponding event signalled while recv is running, and - the FD_READ is then lowered when recv consumes all the data, - but there's no way to un-signal the event. This isn't a - problem for the call in net_select_thread, since any new - events after this point will not have been drained by recv. - It just means that we can't have the obvious assert here. */ - - /* If there is a read event, it might be still valid, or it might - not be - it may have been signalled before we last called - recv. Double-check that there is data. */ - if (events.lNetworkEvents & FD_READ) - { - unsigned long available; - - if (ioctlsocket (scb->fd, FIONREAD, &available) == 0 - && available > 0) - { - SetEvent (state->read_event); - any = 1; - } - else - /* Oops, no data. This call to recv will cause future - data to retrigger the event, e.g. while we are - in net_select_thread. */ - recv (scb->fd, NULL, 0, 0); - } + *read = state->base.read_event; + *except = state->base.except_event; - /* If there's a close event, then record it - it is obviously - still valid, and it will not be resignalled. */ - if (events.lNetworkEvents & FD_CLOSE) - { - SetEvent (state->except_event); - any = 1; - } - - /* If we set either handle, there's no need to wake the thread. */ - if (any) - return; - } - - /* Start the select thread. */ - SetEvent (state->start_select); + /* Check any pending events. Otherwise, start the select + thread. */ + if (!net_windows_socket_check_pending (scb)) + start_select_thread (&state->base); } static void net_windows_done_wait_handle (struct serial *scb) { - struct net_windows_state *state = scb->state; + struct net_windows_state *state = (struct net_windows_state *) scb->state; - SetEvent (state->stop_select); - WaitForSingleObject (state->have_stopped, INFINITE); + stop_select_thread (&state->base); } static int @@ -1013,38 +1184,20 @@ net_windows_open (struct serial *scb, const char *name) { struct net_windows_state *state; int ret; - DWORD threadId; ret = net_open (scb, name); if (ret != 0) return ret; - state = xmalloc (sizeof (struct net_windows_state)); - memset (state, 0, sizeof (struct net_windows_state)); + state = XCNEW (struct net_windows_state); scb->state = state; - /* Create auto reset events to wake, stop, and exit the select - thread. */ - state->start_select = CreateEvent (0, FALSE, FALSE, 0); - state->stop_select = CreateEvent (0, FALSE, FALSE, 0); - state->exit_select = CreateEvent (0, FALSE, FALSE, 0); - - /* Create a manual reset event to signal whether the thread is - stopped. This must be manual reset, because we may wait on - it multiple times without ever starting the thread. */ - state->have_stopped = CreateEvent (0, TRUE, FALSE, 0); - /* Associate an event with the socket. */ state->sock_event = CreateEvent (0, TRUE, FALSE, 0); WSAEventSelect (scb->fd, state->sock_event, FD_READ | FD_CLOSE); - /* Create our own events to report read and close separately. */ - state->read_event = CreateEvent (0, FALSE, FALSE, 0); - state->except_event = CreateEvent (0, FALSE, FALSE, 0); - - /* And finally start the select thread. */ - state->thread = CreateThread (NULL, 0, net_windows_select_thread, scb, 0, - &threadId); + /* Start the thread. */ + create_select_thread (net_windows_select_thread, scb, &state->base); return 0; } @@ -1053,19 +1206,9 @@ net_windows_open (struct serial *scb, const char *name) static void net_windows_close (struct serial *scb) { - struct net_windows_state *state = scb->state; - - SetEvent (state->exit_select); - WaitForSingleObject (state->thread, INFINITE); - - CloseHandle (state->read_event); - CloseHandle (state->except_event); - - CloseHandle (state->start_select); - CloseHandle (state->stop_select); - CloseHandle (state->exit_select); - CloseHandle (state->have_stopped); + struct net_windows_state *state = (struct net_windows_state *) scb->state; + destroy_select_thread (&state->base); CloseHandle (state->sock_event); xfree (scb->state); @@ -1073,93 +1216,148 @@ net_windows_close (struct serial *scb) net_close (scb); } +/* The serial port driver. */ + +static const struct serial_ops hardwire_ops = +{ + "hardwire", + ser_windows_open, + ser_windows_close, + NULL, + ser_base_readchar, + ser_base_write, + ser_windows_flush_output, + ser_windows_flush_input, + ser_windows_send_break, + ser_windows_raw, + /* These are only used for stdin; we do not need them for serial + ports, so supply the standard dummies. */ + ser_base_get_tty_state, + ser_base_copy_tty_state, + ser_base_set_tty_state, + ser_base_print_tty_state, + ser_windows_setbaudrate, + ser_windows_setstopbits, + ser_windows_setparity, + ser_windows_drain_output, + ser_base_async, + ser_windows_read_prim, + ser_windows_write_prim, + NULL, + ser_windows_wait_handle +}; + +/* The dummy serial driver used for terminals. We only provide the + TTY-related methods. */ + +static const struct serial_ops tty_ops = +{ + "terminal", + NULL, + ser_console_close, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + ser_console_get_tty_state, + ser_base_copy_tty_state, + ser_base_set_tty_state, + ser_base_print_tty_state, + NULL, + NULL, + NULL, + ser_base_drain_output, + NULL, + NULL, + NULL, + NULL, + ser_console_wait_handle, + ser_console_done_wait_handle +}; + +/* The pipe interface. */ + +static const struct serial_ops pipe_ops = +{ + "pipe", + pipe_windows_open, + pipe_windows_close, + pipe_windows_fdopen, + ser_base_readchar, + ser_base_write, + ser_base_flush_output, + ser_base_flush_input, + ser_base_send_break, + ser_base_raw, + ser_base_get_tty_state, + ser_base_copy_tty_state, + ser_base_set_tty_state, + ser_base_print_tty_state, + ser_base_setbaudrate, + ser_base_setstopbits, + ser_base_setparity, + ser_base_drain_output, + ser_base_async, + pipe_windows_read, + pipe_windows_write, + pipe_avail, + pipe_wait_handle, + pipe_done_wait_handle +}; + +/* The TCP/UDP socket driver. */ + +static const struct serial_ops tcp_ops = +{ + "tcp", + net_windows_open, + net_windows_close, + NULL, + ser_base_readchar, + ser_base_write, + ser_base_flush_output, + ser_base_flush_input, + ser_tcp_send_break, + ser_base_raw, + ser_base_get_tty_state, + ser_base_copy_tty_state, + ser_base_set_tty_state, + ser_base_print_tty_state, + ser_base_setbaudrate, + ser_base_setstopbits, + ser_base_setparity, + ser_base_drain_output, + ser_base_async, + net_read_prim, + net_write_prim, + NULL, + net_windows_wait_handle, + net_windows_done_wait_handle +}; + void _initialize_ser_windows (void) { WSADATA wsa_data; - struct serial_ops *ops; - /* First register the serial port driver. */ + HMODULE hm = NULL; - ops = XMALLOC (struct serial_ops); - memset (ops, 0, sizeof (struct serial_ops)); - ops->name = "hardwire"; - ops->next = 0; - ops->open = ser_windows_open; - ops->close = ser_windows_close; - - ops->flush_output = ser_windows_flush_output; - ops->flush_input = ser_windows_flush_input; - ops->send_break = ser_windows_send_break; + /* First find out if kernel32 exports CancelIo function. */ + hm = LoadLibrary ("kernel32.dll"); + if (hm) + { + CancelIo = (CancelIo_ftype *) GetProcAddress (hm, "CancelIo"); + FreeLibrary (hm); + } + else + CancelIo = NULL; - /* These are only used for stdin; we do not need them for serial - ports, so supply the standard dummies. */ - ops->get_tty_state = ser_base_get_tty_state; - ops->set_tty_state = ser_base_set_tty_state; - ops->print_tty_state = ser_base_print_tty_state; - ops->noflush_set_tty_state = ser_base_noflush_set_tty_state; - - ops->go_raw = ser_windows_raw; - ops->setbaudrate = ser_windows_setbaudrate; - ops->setstopbits = ser_windows_setstopbits; - ops->drain_output = ser_windows_drain_output; - ops->readchar = ser_base_readchar; - ops->write = ser_base_write; - ops->async = ser_base_async; - ops->read_prim = ser_windows_read_prim; - ops->write_prim = ser_windows_write_prim; - ops->wait_handle = ser_windows_wait_handle; - - serial_add_interface (ops); - - /* Next create the dummy serial driver used for terminals. We only - provide the TTY-related methods. */ - - ops = XMALLOC (struct serial_ops); - memset (ops, 0, sizeof (struct serial_ops)); - - ops->name = "terminal"; - ops->next = 0; - - ops->close = ser_console_close; - ops->get_tty_state = ser_console_get_tty_state; - ops->set_tty_state = ser_base_set_tty_state; - ops->print_tty_state = ser_base_print_tty_state; - ops->noflush_set_tty_state = ser_base_noflush_set_tty_state; - ops->drain_output = ser_base_drain_output; - ops->wait_handle = ser_console_wait_handle; - ops->done_wait_handle = ser_console_done_wait_handle; - - serial_add_interface (ops); - - /* The pipe interface. */ - - ops = XMALLOC (struct serial_ops); - memset (ops, 0, sizeof (struct serial_ops)); - ops->name = "pipe"; - ops->next = 0; - ops->open = pipe_windows_open; - ops->close = pipe_windows_close; - ops->readchar = ser_base_readchar; - ops->write = ser_base_write; - ops->flush_output = ser_base_flush_output; - ops->flush_input = ser_base_flush_input; - ops->send_break = ser_base_send_break; - ops->go_raw = ser_base_raw; - ops->get_tty_state = ser_base_get_tty_state; - ops->set_tty_state = ser_base_set_tty_state; - ops->print_tty_state = ser_base_print_tty_state; - ops->noflush_set_tty_state = ser_base_noflush_set_tty_state; - ops->setbaudrate = ser_base_setbaudrate; - ops->setstopbits = ser_base_setstopbits; - ops->drain_output = ser_base_drain_output; - ops->async = ser_base_async; - ops->read_prim = pipe_windows_read; - ops->write_prim = pipe_windows_write; - ops->wait_handle = pipe_wait_handle; - ops->done_wait_handle = pipe_done_wait_handle; - - serial_add_interface (ops); + serial_add_interface (&hardwire_ops); + serial_add_interface (&tty_ops); + serial_add_interface (&pipe_ops); /* If WinSock works, register the TCP/UDP socket driver. */ @@ -1167,29 +1365,5 @@ _initialize_ser_windows (void) /* WinSock is unavailable. */ return; - ops = XMALLOC (struct serial_ops); - memset (ops, 0, sizeof (struct serial_ops)); - ops->name = "tcp"; - ops->next = 0; - ops->open = net_windows_open; - ops->close = net_windows_close; - ops->readchar = ser_base_readchar; - ops->write = ser_base_write; - ops->flush_output = ser_base_flush_output; - ops->flush_input = ser_base_flush_input; - ops->send_break = ser_base_send_break; - ops->go_raw = ser_base_raw; - ops->get_tty_state = ser_base_get_tty_state; - ops->set_tty_state = ser_base_set_tty_state; - ops->print_tty_state = ser_base_print_tty_state; - ops->noflush_set_tty_state = ser_base_noflush_set_tty_state; - ops->setbaudrate = ser_base_setbaudrate; - ops->setstopbits = ser_base_setstopbits; - ops->drain_output = ser_base_drain_output; - ops->async = ser_base_async; - ops->read_prim = net_read_prim; - ops->write_prim = net_write_prim; - ops->wait_handle = net_windows_wait_handle; - ops->done_wait_handle = net_windows_done_wait_handle; - serial_add_interface (ops); + serial_add_interface (&tcp_ops); }