Package: pose
Version: 3.5-7
Severity: normal
Tags: patch

Summary: The previous code incorrectly tested the file descriptor
coming from an open function with <=0 as being an error along with
using 0 as an invalid file descriptor.  This caused pose to no longer
be able to open a serial port until it was restarted if a serial port
open failed.  I also corrected the PRINTF statements to display the
correct function name.

More detail: When the port is available pose opens it, reads it and
closes it when finished.  The previous pose code would check the
return value of open with <=0 and treat it as an error when the
statement is true.  When it detected an error it set the file
descriptor to 0 and returned an error code from the open member
function.  When the port can't be opened, DestroyCommThreads and
CloseCommPort are both called twice.  DestroyCommThreads bails without
doing anything, but CloseCommPort closes file descriptor 0 each time,
even though EmHostTransportSerial object doesn't have any open file
descriptors.  The first time it closes file descriptor 0 pose looses
standard input, the second time the close call returns error, but pose
doesn't check.  Once that happens OpenCommPort always fails because
the serial port will get the next available file descriptor which is
now zero and the check <=0 assumes it failed, even when the serial
port was successfully opened.  From here on out the pose will not be
able to use the serial port until it is restarted. 

This is also the case if someone specifies a bad or invalid device
file for the serial port, pose will not work again until restarted.
The fix is relatively trivial, it involves treating -1 as the only
invalid file descriptor and protects against multiple calls to the
CloseCommPort function.  As to why close is being called twice I don't
know.  I didn't look into that.

I also included a little utility I wrote that I found useful to use
the pseudo terminals instead of two real serial ports and a NULL
modem.  It is the last two patches, although I'm not sure where to put
them in the pose source tree.

I modified the PRINTF at the top (the default behavior is the previous
code), which you may or may not want to submit upstream.  I needed it
to give me more information such as time to compare with strace.


Index: EmTransportSerialUnix.cpp
===================================================================
RCS file: 
/data/debian/pose/cvs_dir/pose-3.5_source/SrcUnix/EmTransportSerialUnix.cpp,v
retrieving revision 1.1
retrieving revision 1.5
diff -u -p -r1.1 -r1.5
--- EmTransportSerialUnix.cpp   21 Jun 2005 01:24:01 -0000      1.1
+++ EmTransportSerialUnix.cpp   4 Jul 2005 16:06:43 -0000       1.5
@@ -25,7 +25,16 @@
 #include <termios.h>                   // struct termios
 
 
+#if DEBUG > 0
+#include <sys/time.h>
+#include <time.h>
+#define PRINTF(...) do { struct timeval tv; gettimeofday(&tv, NULL); \
+       fprintf(stderr, "%ld:%ld:%ld.%06ld ", (tv.tv_sec/3600)%60, 
(tv.tv_sec/60)%60, \
+               tv.tv_sec%60, tv.tv_usec); \
+       fprintf(stderr, __VA_ARGS__) ; fprintf(stderr, "\n"); } while(0)
+#else
 #define PRINTF if (!LogSerial ()) ; else LogAppendMsg
+#endif
 
 
 /***********************************************************************
@@ -596,9 +605,9 @@ void EmTransportSerial::HostGetSerialBau
 EmHostTransportSerial::EmHostTransportSerial (void) :
        fReadThread (NULL),
        fWriteThread (NULL),
-       fCommHandle (0),
-       fCommSignalPipeA (0),
-       fCommSignalPipeB (0),
+       fCommHandle (-1),
+       fCommSignalPipeA (-1),
+       fCommSignalPipeB (-1),
        fTimeToQuit (false),
        fDataMutex (),
        fDataCondition (&fDataMutex),
@@ -626,9 +635,9 @@ EmHostTransportSerial::~EmHostTransportS
 {
        EmAssert (fReadThread == NULL);
        EmAssert (fWriteThread == NULL);
-       EmAssert (fCommHandle == 0);
-       EmAssert (fCommSignalPipeA == 0);
-       EmAssert (fCommSignalPipeB == 0);
+       EmAssert (fCommHandle == -1);
+       EmAssert (fCommSignalPipeA == -1);
+       EmAssert (fCommSignalPipeB == -1);
 }
 
 
@@ -648,12 +657,17 @@ ErrCode EmHostTransportSerial::OpenCommP
 {
        EmTransportSerial::PortName     portName = config.fPort;
 
-       PRINTF ("EmTransportSerial::HostOpen: attempting to open port \"%s\"",
+       PRINTF ("EmHostTransportSerial::OpenCommPort: attempting to open port 
\"%s\"",
                        portName.c_str());
 
+       if (fCommHandle != -1)
+       {
+               PRINTF ("EmHostTransportSerial::OpenCommPort: port already 
open");
+               return -1;
+       }
        if (!portName.empty ())
        {
-               PRINTF ("EmTransportSerial::HostOpen: Opening serial port...");
+               PRINTF ("EmHostTransportSerial::OpenCommPort: Opening serial 
port...");
 
                // An execllent article on serial programming under UNIX 
("Linux Serial Port
                // Programming Mini-Howto") says to set the following flags in 
the open call.
@@ -662,16 +676,14 @@ ErrCode EmHostTransportSerial::OpenCommP
 
                fCommHandle = open(portName.c_str (), O_RDWR | O_NOCTTY | 
O_NDELAY);
 
-               if (fCommHandle <= 0)
+               if (fCommHandle == -1)
                {
-                       fCommHandle = 0;
-
                        return errno;
                }
        }
        else
        {
-               PRINTF ("EmTransportSerial::HostOpen: No port selected in the 
Properties dialog box...");
+               PRINTF ("EmHostTransportSerial::OpenCommPort: No port selected 
in the Properties dialog box...");
                return -1;      // !!! better error number
        }
 
@@ -694,25 +706,29 @@ ErrCode EmHostTransportSerial::OpenCommP
 
 ErrCode EmHostTransportSerial::CreateCommThreads (const 
EmTransportSerial::ConfigSerial& /*config*/)
 {
-       if (fCommHandle)
+       if (fCommHandle == -1)
        {
-               PRINTF ("EmTransportSerial::HostOpen: Creating serial port 
handler threads...");
+               PRINTF ("EmHostTransportSerial::CreateCommThreads: Serial port 
not open...");
+               return -1;
+       }
 
-               // Create the pipe used to communicate with CommRead.
+       PRINTF ("EmHostTransportSerial::CreateCommThreads: Creating serial port 
handler threads...");
 
-               int filedes[] = { 0, 0 };
-               if (pipe (filedes) == 0)
-               {
-                       fCommSignalPipeA = filedes[0];  // for reading
-                       fCommSignalPipeB = filedes[1];  // for writing
-               }
+       // Create the pipe used to communicate with CommRead.
 
-               // Create the threads and start them up.
-
-               fTimeToQuit = false;
-               fReadThread = omni_thread::create (CommRead, this);
-               fWriteThread = omni_thread::create (CommWrite, this);
+       int filedes[] = { 0, 0 };
+       if(pipe (filedes) == -1 )
+       {
+               return errno;
        }
+       fCommSignalPipeA = filedes[0];  // for reading
+       fCommSignalPipeB = filedes[1];  // for writing
+
+       // Create the threads and start them up.
+
+       fTimeToQuit = false;
+       fReadThread = omni_thread::create (CommRead, this);
+       fWriteThread = omni_thread::create (CommWrite, this);
 
        return errNone;
 }
@@ -732,9 +748,10 @@ ErrCode EmHostTransportSerial::CreateCom
 
 ErrCode EmHostTransportSerial::DestroyCommThreads (void)
 {
+       PRINTF ("EmHostTransportSerial::DestroyCommThreads: Shutdown and 
destroy the comm threads.");
        // If never created, nothing to destroy.
 
-       if (!fCommSignalPipeA)
+       if (fCommSignalPipeA == -1)
                return errNone;
 
        // Signal the threads to quit.
@@ -767,7 +784,7 @@ ErrCode EmHostTransportSerial::DestroyCo
        close (fCommSignalPipeA);
        close (fCommSignalPipeB);
 
-       fCommSignalPipeA = fCommSignalPipeB = 0;
+       fCommSignalPipeA = fCommSignalPipeB = -1;
 
        return errNone;
 }
@@ -787,9 +804,14 @@ ErrCode EmHostTransportSerial::DestroyCo
 
 ErrCode EmHostTransportSerial::CloseCommPort (void)
 {
+       if(fCommHandle == -1)
+       {
+               return -1;
+       }
+
        (void) close (fCommHandle);
 
-       fCommHandle = 0;
+       fCommHandle = -1;
 
        return errNone;
 }
@@ -981,7 +1003,7 @@ void* EmHostTransportSerial::CommRead (v
 {
        EmHostTransportSerial*  This = (EmHostTransportSerial*) data;
 
-       PRINTF ("CommRead starting.");
+       PRINTF ("EmHostTransportSerial::CommRead: starting.");
 
        while (!This->fTimeToQuit)
        {
@@ -998,7 +1020,10 @@ void* EmHostTransportSerial::CommRead (v
                status = select (maxfd + 1, &read_fds, NULL, NULL, NULL);
 
                if (This->fTimeToQuit)
+               {
+                       PRINTF ("EmHostTransportSerial::CommRead: after select 
fTimeToQuit is true");
                        break;
+               }
 
                if (status > 0) // data available
                {
@@ -1008,8 +1033,18 @@ void* EmHostTransportSerial::CommRead (v
                                int             len = 1024;
                                len = read (fd1, buf, len);
 
+                               // Break if read returns error
+                               if (len == -1)
+                               {
+                                       PRINTF 
("EmHostTransportSerial::CommRead: read error %s",
+                                               strerror(errno));
+                                       break;
+                               }
                                if (len == 0)
-                                       break; // port closed
+                               {
+                                       PRINTF 
("EmHostTransportSerial::CommRead: read %d bytes", len);
+                                       break; // port error?
+                               }
 
                                // Log the data.
                                if (LogSerialData ())
@@ -1024,7 +1059,7 @@ void* EmHostTransportSerial::CommRead (v
                }
        }
 
-       PRINTF ("CommRead exitting.");
+       PRINTF ("EmHostTransportSerial::CommRead: exitting.");
 
        return NULL;
 }
@@ -1050,7 +1085,7 @@ void* EmHostTransportSerial::CommWrite (
 {
        EmHostTransportSerial*  This = (EmHostTransportSerial*) data;
 
-       PRINTF ("CommWrite starting.");
+       PRINTF ("EmHostTransportSerial::CommWrite: starting.");
 
        omni_mutex_lock lock (This->fDataMutex);
 
@@ -1093,7 +1128,7 @@ void* EmHostTransportSerial::CommWrite (
                Platform::DisposeMemory (buf);
        }
 
-       PRINTF ("CommWrite exitting.");
+       PRINTF ("EmHostTransportSerial::CommWrite: exitting.");
 
        return NULL;
 }





Index: Makefile
===================================================================
RCS file: Makefile
diff -N Makefile
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ Makefile    22 Jun 2005 02:49:46 -0000      1.1
@@ -0,0 +1,3 @@
+CXXFLAGS=-g -Wall
+
+pty2pty:
Index: pty2pty.cc
===================================================================
RCS file: pty2pty.cc
diff -N pty2pty.cc
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ pty2pty.cc  4 Jul 2005 16:44:50 -0000       1.9
@@ -0,0 +1,229 @@
+/* Allows two programs using the pseuto ttys to open and close their
+ * device at will without giving the other program a notification of
+ * the open or close.  Very useful with pose as it tends to open and
+ * close the serial port frequently in the middle of a hotsync
+ * operation.  It also removes the ordering which program had to be
+ * run first and be waiting for input.
+ *
+ * Copyright (C) 2005 David Fries <[EMAIL PROTECTED]>
+ *
+ *  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; using version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This program allows two programs on the same computer to talk
+ * through a tty local null modem like cable.  Things like baud rates
+ * are ignored, along with I'm sure other real hardware serial
+ * interfaces, but the termios calls do not fail which allows at least
+ * some programs to work without a problem using it.
+ * 
+ * The Unix Palm Pilot utilities use the serial port to connect to the
+ * Palm Pilot's serial port for synchronization etc.  The Palm Pilot
+ * emulator has the ability to use a Unix serial port as if it was the
+ * serial port on the Palm Pilot.  The normal case as listed in the
+ * emulator manual is to connect a physical serial null modem between
+ * the two ports and talk over that.  It is closer to simulating the
+ * connection to a real Palm handheld, but it seems silly to use up
+ * two serial ports to talk from one program to another on the same
+ * computer.  The Unix /dev/pty /dev/tty pairs provide a terminal
+ * interface that allows the normal serial setup calls to be executed
+ * ignoring what doesn't make sense for the real hardware such as baud
+ * rate.  The problem is the order of operation.  The /dev/pty?? must
+ * be opened first, then the /dev/tty?? and if either of them close
+ * the sequence must be started over.  Unfortunately pose frequently
+ * closes the serial port making it hard to complete even a single
+ * hotsync operation.
+ *
+ * That is the reason for this program.  It opens two /dev/pty ports
+ * and relays data from one to the other.  If a program closes the
+ * /dev/tty port this program gets a read error which it retries after
+ * a delay until the /dev/tty port is opened and data can again flow.
+ * The other program need not be notified.  An alternate way to
+ * delaying and retrying the read operation is to close the /dev/pty
+ * and open it again.  This has the advantage that read will block
+ * until there is data instead of returning EIO.  Unfortunately this
+ * introduces a race condition, which with pose frequently opening and
+ * closing the device comes up rather quickly.  The race condition is,
+ * pose closes port, pty2pty gets EIO from read, pose opens port,
+ * pty2pty closes port, pty2pty opens port.  The pty must be opened
+ * first, and if the pty is closed while the tty is still open the pty
+ * can't be opened again until the tty is closed.  At this point pose
+ * has the tty open and pty2pty gets EIO each time it tries to open
+ * the port.  This is a real race condition I'm seeing and polling
+ * read is the only way I see to get around it.
+ *
+ * Future improvement might include some circular buffers.  For now it
+ * is keep the program simple stupid, besides the kernel has buffers,
+ * why not use them?
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/select.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+static const char *version = "$Id: pty2pty.cc,v 1.9 2005/07/04 16:44:50 david 
Exp $";
+
+//#define DEBUG 1
+
+int main(int argc, char **argv)
+{
+       char *device_name[2] = { NULL, NULL};
+       int fd[2] = { -1, -1};
+       const int buf_size = 4096;
+       int buf[2][buf_size];
+       int buf_count[2] = {0,0};
+       int buf_loc[2] = {0,0};
+       if(argc != 3)
+       {
+               fprintf(stderr, "Usage: %s /dev/pty?? /dev/pty??\n"
+                       "\tBoth pty devices are opened and data is forwarded"
+                       "from one to the other\n%s\n", argv[0], version);
+               exit(1);
+       }
+       device_name[0] = argv[1];
+       device_name[1] = argv[2];
+
+       fd_set readfds, writefds;
+       int events, result, i;
+
+       /* Open the ports */
+       for(i=0; i<2; ++i)
+       {
+               fd[i] = open(device_name[i], O_RDWR | O_NONBLOCK);
+               if(fd[i] == -1)
+               {
+                       fprintf(stderr, "Error opening port %s:%s\n",
+                               device_name[i], strerror(errno));
+                       exit(1);
+               }
+               struct termios t;
+               if(tcgetattr(fd[i], &t)==-1)
+               {
+                       perror("Error getting terminal settings\n");
+                       exit(1);
+               }
+               cfmakeraw(&t);
+               if(tcsetattr(fd[i], TCSANOW, &t)==-1)
+               {
+                       perror("Error setting terminal settings\n");
+                       exit(1);
+               }
+       }
+
+       bool read_EIO[2] = {false,false};
+       struct timeval timeout, *t_ptr;
+       for(;;)
+       {
+               FD_ZERO(&readfds);
+               FD_ZERO(&writefds);
+               t_ptr = NULL;
+               /* Setup select. */
+               for(i=0; i<2; ++i)
+               {
+
+                       /* Setup select call. */
+                       /* Simple buffer, read or write, if there is data
+                        * write, if there isn't data read, setup select
+                        * operations for that direction only.
+                        */
+                       if(read_EIO[i])
+                       {
+                               /* Do a timeout when the last pass returned
+                                * and EIO error.
+                                */
+                               t_ptr = &timeout;
+                               timeout.tv_sec = 0;
+                               /* Time timeout can't be too big or the
+                                * pilot-xfer gets confused.  15 milliseconds
+                                * was working pretty good.
+                                */
+                               timeout.tv_usec = 15*1000;
+                       }
+                       else
+                       {
+                               if(buf_count[i])
+                                       FD_SET(fd[(i+1)%2], &writefds);
+                               else
+                                       FD_SET(fd[i], &readfds);
+                       }
+               }
+               events = select(1+(fd[0]>fd[1] ? fd[0] : fd[1]), &readfds,
+                       &writefds, NULL, t_ptr);
+               if(events == -1)
+               {
+                       perror("Error calling select");
+                       exit(1);
+               }
+               for(i=0; i<2; ++i)
+               {
+                       /* Always clear the read error and try again next
+                        * pass.
+                        */
+                       read_EIO[i] = false;
+                       if(FD_ISSET(fd[i], &readfds))
+                       {
+                               result = read(fd[i], buf[i], buf_size);
+                               if(result == -1)
+                               {
+                                       if(errno == EAGAIN)
+                                       {
+                                               perror(device_name[i]);
+                                               break;
+                                       }
+                                       #if DEBUG
+                                       fprintf(stderr, "Error reading %s: "
+                                               "%s\n", device_name[i],
+                                               strerror(errno));
+                                       #endif
+                                       read_EIO[i] = true;
+                                       break;
+                               }
+                               else
+                               {
+                                       buf_count[i] = result;
+                                       buf_loc[i] = 0;
+                               }
+                       }
+                       if(FD_ISSET(fd[(i+1)%2], &writefds))
+                       {
+                               result = write(fd[(i+1)%2], buf[i]+buf_loc[i],
+                                       buf_count[i]);
+                               if(result == -1)
+                               {
+                                       #if DEBUG
+                                       fprintf(stderr, "Error writing %s: "
+                                               "%s\n", device_name[(i+1)%2],
+                                               strerror(errno));
+                                       #endif
+                                       struct timespec req = {100*1000*1000,0};
+                                       nanosleep(&req, NULL);
+                                       break;
+                               }
+                               else
+                               {
+                                       buf_loc[i] += result;
+                                       buf_count[i] -= result;
+                               }
+                       }
+               }
+       }
+
+       return 1;
+}

-- System Information:
Debian Release: 3.1
  APT prefers unstable
  APT policy: (500, 'unstable')
Architecture: i386 (i686)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.6.12-rc6
Locale: LANG=C, LC_CTYPE=C (charmap=ANSI_X3.4-1968)

Versions of packages pose depends on:
ii  libc6                    2.3.2.ds1-22    GNU C Library: Shared libraries an
ii  libfltk1.1c102           1.1.6-5         Fast Light Toolkit shared librarie
ii  libgcc1                  1:4.0.0-11      GCC support library
ii  libstdc++5               1:3.3.6-5       The GNU Standard C++ Library v3
ii  libx11-6                 4.3.0.dfsg.1-13 X Window System protocol client li
ii  libxext6                 4.3.0.dfsg.1-13 X Window System miscellaneous exte
ii  xlibmesa-gl [libgl1]     4.3.0.dfsg.1-13 Mesa 3D graphics library [XFree86]
ii  xlibmesa-glu [libglu1]   4.3.0.dfsg.1-13 Mesa OpenGL utility library [XFree
ii  xlibs                    4.3.0.dfsg.1-13 X Keyboard Extension (XKB) configu

-- no debconf information


-- 
To UNSUBSCRIBE, email to [EMAIL PROTECTED]
with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]

Reply via email to