daily update
[deliverable/binutils-gdb.git] / gdb / ser-mingw.c
index 17f8320b276952b2b59e6a6c0460142544a2be94..dca3b9065979e4fdb96f63f10619f32cc17144c8 100644 (file)
@@ -1,7 +1,6 @@
 /* Serial interface for local (hardwired) serial ports on Windows systems
 
-   Copyright (C) 2006
-   Free Software Foundation, Inc.
+   Copyright (C) 2006, 2007 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -454,6 +453,15 @@ 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)
 {
@@ -502,6 +510,42 @@ pipe_select_thread (void *arg)
     }
 }
 
+static DWORD WINAPI
+file_select_thread (void *arg)
+{
+  struct serial *scb = arg;
+  struct ser_console_state *state;
+  int event_index;
+  HANDLE h;
+
+  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;
+
+      if (WaitForMultipleObjects (2, wait_events, FALSE, INFINITE) != WAIT_OBJECT_0)
+       return 0;
+
+      ResetEvent (state->have_stopped);
+
+      if (SetFilePointer (h, 0, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
+       {
+         SetEvent (state->except_event);
+         continue;
+       }
+
+      SetEvent (state->read_event);
+    }
+}
+
 static void
 ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
 {
@@ -513,7 +557,7 @@ ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
       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;
@@ -542,9 +586,12 @@ ser_console_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
       if (is_tty)
        state->thread = CreateThread (NULL, 0, console_select_thread, scb, 0,
                                      &threadId);
-      else
+      else if (fd_is_pipe (scb->fd))
        state->thread = CreateThread (NULL, 0, pipe_select_thread, scb, 0,
                                      &threadId);
+      else
+       state->thread = CreateThread (NULL, 0, file_select_thread, scb, 0,
+                                     &threadId);
     }
 
   *read = state->read_event;
@@ -624,6 +671,266 @@ ser_console_get_tty_state (struct serial *scb)
     return NULL;
 }
 
+struct pipe_state
+{
+  /* Since we use the pipe_select_thread for our select emulation,
+     we need to place the state structure it requires at the front
+     of our state.  */
+  struct ser_console_state wait;
+
+  /* The pex obj for our (one-stage) pipeline.  */
+  struct pex_obj *pex;
+
+  /* Streams for the pipeline's input and output.  */
+  FILE *input, *output;
+};
+
+static struct pipe_state *
+make_pipe_state (void)
+{
+  struct pipe_state *ps = XMALLOC (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;
+  ps->wait.stop_select = INVALID_HANDLE_VALUE;
+
+  return ps;
+}
+
+static void
+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);
+    }
+
+  /* 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
+     and the child will not exit until the pipe is closed.  */
+  if (ps->input)
+    fclose (ps->input);
+  if (ps->pex)
+    pex_free (ps->pex);
+  /* pex_free closes ps->output.  */
+
+  xfree (ps);
+
+  errno = saved_errno;
+}
+
+static void
+cleanup_pipe_state (void *untyped)
+{
+  struct pipe_state *ps = untyped;
+
+  free_pipe_state (ps);
+}
+
+static int
+pipe_windows_open (struct serial *scb, const char *name)
+{
+  struct pipe_state *ps;
+  FILE *pex_stderr;
+
+  char **argv = buildargv (name);
+  struct cleanup *back_to = make_cleanup_freeargv (argv);
+  if (! argv[0] || argv[0][0] == '\0')
+    error ("missing child command");
+
+
+  ps = make_pipe_state ();
+  make_cleanup (cleanup_pipe_state, ps);
+
+  ps->pex = pex_init (PEX_USE_PIPES, "target remote pipe", NULL);
+  if (! ps->pex)
+    goto fail;
+  ps->input = pex_input_pipe (ps->pex, 1);
+  if (! ps->input)
+    goto fail;
+
+  {
+    int err;
+    const char *err_msg
+      = pex_run (ps->pex, PEX_SEARCH | PEX_BINARY_INPUT | PEX_BINARY_OUTPUT
+                | PEX_STDERR_TO_PIPE,
+                 argv[0], argv, NULL, NULL,
+                 &err);
+
+    if (err_msg)
+      {
+        /* Our caller expects us to return -1, but all they'll do with
+           it generally is print the message based on errno.  We have
+           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",
+                 name, err_msg, safe_strerror (err));
+        else
+          error ("error starting child process '%s': %s",
+                 name, err_msg);
+      }
+  }
+
+  ps->output = pex_read_output (ps->pex, 1);
+  if (! ps->output)
+    goto fail;
+  scb->fd = fileno (ps->output);
+
+  pex_stderr = pex_read_err (ps->pex, 1);
+  if (! pex_stderr)
+    goto fail;
+  scb->error_fd = fileno (pex_stderr);
+
+  scb->state = (void *) ps;
+
+  discard_cleanups (back_to);
+  return 0;
+
+ fail:
+  do_cleanups (back_to);
+  return -1;
+}
+
+
+static void
+pipe_windows_close (struct serial *scb)
+{
+  struct pipe_state *ps = 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
+     closing the input pipe will get the message across.  */
+
+  free_pipe_state (ps);
+}
+
+
+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;
+
+  if (! PeekNamedPipe (pipeline_out, NULL, 0, NULL, &available, NULL))
+    return -1;
+
+  if (count > available)
+    count = available;
+
+  if (! ReadFile (pipeline_out, scb->buf, count, &bytes_read, NULL))
+    return -1;
+
+  return bytes_read;
+}
+
+
+static int
+pipe_windows_write (struct serial *scb, const void *buf, size_t count)
+{
+  struct pipe_state *ps = scb->state;
+  HANDLE pipeline_in;
+  DWORD written;
+
+  int pipeline_in_fd = fileno (ps->input);
+  if (pipeline_in_fd < 0)
+    return -1;
+
+  pipeline_in = (HANDLE) _get_osfhandle (pipeline_in_fd);
+  if (pipeline_in == INVALID_HANDLE_VALUE)
+    return -1;
+
+  if (! WriteFile (pipeline_in, buf, count, &written, NULL))
+    return -1;
+
+  return written;
+}
+
+
+static void
+pipe_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except)
+{
+  struct pipe_state *ps = 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);
+    }
+
+  *read = ps->wait.read_event;
+  *except = ps->wait.except_event;
+
+  /* Start from a blank state.  */
+  ResetEvent (ps->wait.read_event);
+  ResetEvent (ps->wait.except_event);
+  ResetEvent (ps->wait.stop_select);
+
+  /* Start the select thread.  */
+  SetEvent (ps->wait.start_select);
+}
+
+static void
+pipe_done_wait_handle (struct serial *scb)
+{
+  struct pipe_state *ps = 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);
+}
+
+static int
+pipe_avail (struct serial *scb, int fd)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (fd);
+  DWORD numBytes;
+  BOOL r = PeekNamedPipe (h, NULL, 0, NULL, &numBytes, NULL);
+  if (r == FALSE)
+    numBytes = 0;
+  return numBytes;
+}
+
 struct net_windows_state
 {
   HANDLE read_event;
@@ -897,6 +1204,36 @@ _initialize_ser_windows (void)
 
   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;
+  ops->avail = pipe_avail;
+
+  serial_add_interface (ops);
+
   /* If WinSock works, register the TCP/UDP socket driver.  */
 
   if (WSAStartup (MAKEWORD (1, 0), &wsa_data) != 0)
This page took 0.02729 seconds and 4 git commands to generate.