streams.cc

00001 #include "streams.h"
00002 #include "wvstring.h"
00003 #include <assert.h>
00004 #include <errno.h>
00005 #include <conio.h>
00006 #include <stdio.h>
00007 #include <io.h>
00008 
00009 #if WIN32
00010 // MS Visual C++ doesn't support varags preproc macros
00011 # define DPRINTF
00012 #else
00013 #if 0
00014 # define DPRINTF(x, args...) do { \
00015         printf(x, ## args); fflush(stdout); \
00016     } while (0)
00017 #else
00018 # define DPRINTF(x, args...) do { } while(0)
00019 #endif
00020 #endif
00021 
00022 
00023 // this class changes the default libc stdout buffering to "line buffered"
00024 // and stderr to "unbuffered", like they should be in any sane system.
00025 // Apparently they start off as "fully buffered" in most Windows systems.
00026 class FixLibcIoBuffers
00027 {
00028 public:
00029     FixLibcIoBuffers()
00030     {
00031         setvbuf(stdout, NULL, _IOLBF, 0);
00032         setvbuf(stderr, NULL, _IONBF, 0);
00033     }
00034 };
00035 static FixLibcIoBuffers fixbufs;
00036 
00037 
00038 // these versions of close/read/write try to work with both sockets and
00039 // msvcrt file descriptors! (I hope we never get a socket with the same
00040 // VALUE as a file descriptor!)
00041  
00042 
00043 static void errcode(int err)
00044 {
00045     if (err == EIO)
00046         err = EBADF; // sometimes we get EIO when Unix would be EBADF
00047     if (err == WSAENOTSOCK)
00048         err = EBADF; // if it's not a socket, it's also not a fd
00049     SetLastError(err);
00050     errno = err;
00051 }
00052 
00053 
00054 static bool is_socket(int fd)
00055 {
00056     // if _get_osfhandle doesn't work, it must not be a fd, so assume it's
00057     // a socket.
00058     return (HANDLE)_get_osfhandle(fd) == INVALID_HANDLE_VALUE;
00059 }
00060 
00061 
00062 int close(int fd)
00063 {
00064     int ret;
00065     if (is_socket(fd))
00066     {
00067         ret = closesocket(fd);
00068         errcode(GetLastError());
00069     }
00070     else
00071     {
00072         ret = _close(fd);
00073         errcode(errno);
00074     }
00075     return ret;
00076 }
00077 
00078 
00079 int read(int fd, void *buf, size_t count)
00080 {
00081     int ret;
00082     if (is_socket(fd))
00083     {
00084         ret = recv(fd, (char *)buf, count, 0);
00085         errcode(GetLastError());
00086     }
00087     else
00088     {
00089         ret = _read(fd, buf, count);
00090         errcode(errno);
00091     }
00092     return ret;
00093 }
00094 
00095 
00096 int write(int fd, const void *buf, size_t count)
00097 {
00098     int ret;
00099     if (is_socket(fd))
00100     {
00101         ret = send(fd, (char *)buf, count, 0);
00102         errcode(GetLastError());
00103     }
00104     else
00105     {
00106         ret = _write(fd, buf, count);
00107         errcode(errno);
00108     }
00109     return ret;
00110 }
00111 
00112 
00113 unsigned int sleep(unsigned int seconds)
00114 {
00115     Sleep(seconds * 1000);
00116     return 0;
00117 }
00118 
00119 
00120 int socketpair(int family, int type, int protocol, int *sb)
00121 {
00122     SOCKET insock, outsock, newsock;
00123     struct sockaddr_in sock_in;
00124 
00125     if (type != SOCK_STREAM)
00126         return -1;
00127 
00128     newsock = socket(AF_INET, type, 0);
00129     if (newsock == INVALID_SOCKET)
00130         return -1;
00131 
00132     sock_in.sin_family = AF_INET;
00133     sock_in.sin_port = 0;
00134     sock_in.sin_addr.s_addr = INADDR_ANY;
00135     if (bind(newsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0)
00136         return -1;
00137 
00138     int len = sizeof (sock_in);
00139     if (getsockname(newsock, (struct sockaddr *)&sock_in, &len) < 0)
00140         return -1;
00141 
00142     if (listen(newsock, 2) < 0)
00143         return -1;
00144 
00145     outsock = socket(AF_INET, type, 0);
00146     if (outsock == INVALID_SOCKET)
00147         return -1;
00148 
00149     sock_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
00150     if (connect(outsock, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0)
00151         return -1;
00152 
00153     /* For stream sockets, accept the connection and close the listener */
00154     len = sizeof(sock_in);
00155     insock = accept(newsock, (struct sockaddr *)&sock_in, &len);
00156     if (insock == INVALID_SOCKET)
00157         return -1;
00158 
00159     if (closesocket(newsock) < 0)
00160         return -1;
00161 
00162     sb[0] = insock;
00163     sb[1] = outsock;
00164     return 0;
00165 }
00166 
00167 
00168 static void CALLBACK completion(DWORD error, DWORD nread, LPOVERLAPPED ov)
00169 {
00170 }
00171 
00172 
00173 static size_t fake_read(int fd, void *buf, size_t len)
00174 {
00175     HANDLE h = (HANDLE)_get_osfhandle(fd);
00176     INPUT_RECORD p;
00177     DPRINTF("fake_read(%d/%d,%p,%d) = ", fd, (int)h, buf, (int)len);
00178     
00179     DWORD ret = 0;
00180     OVERLAPPED ov;
00181     memset(&ov, 0, sizeof(ov));
00182     ov.Offset = SetFilePointer(h, 0, NULL, FILE_CURRENT);
00183     
00184     if (PeekNamedPipe(h, NULL, 0, NULL, &ret, NULL))
00185     {
00186         // cygwin sshd/telnetd uses named pipes for stdin.  We have to
00187         // support these separately.  Getting stuck in ReadFile on a named
00188         // pipe appears to freeze up gethostbyname() for some reason on win2k!
00189         DPRINTF("(stdin is a pipe)\n");
00190         while (PeekNamedPipe(h, NULL, 0, NULL, &ret, NULL) && !ret)
00191         {
00192             DPRINTF(".");
00193             Sleep(100);
00194         }
00195         ReadFile(h, buf, len, &ret, NULL);
00196     }
00197     else if (PeekConsoleInput(h, &p, 1, &ret))
00198     {
00199         // a typical stdin/out pair refers to a console.  Unfortunately,
00200         // console I/O is stupid: you can poll it to see if it's ready, but
00201         // if you have it in line mode, then it's not *really* ready.
00202         // ReadConsole/ReadFile will only return after the user hits enter.
00203         // Unfortunately, it seems the only way around this is to disable
00204         // line/echo mode and fake it ourselves.  Hopefully this isn't too
00205         // ugly...
00206         DPRINTF("(stdin is a console)\n");
00207         
00208         size_t used = 0;
00209         char *xbuf = (char *)buf;
00210         HANDLE hout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
00211                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
00212                                  NULL, OPEN_EXISTING, 0, 0);
00213         
00214         DWORD conmode = 0;
00215         GetConsoleMode(h, &conmode);
00216         SetConsoleMode(h, conmode & 
00217                ~(ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT | ENABLE_ECHO_INPUT));
00218         
00219         while (PeekConsoleInput(h, &p, 1, &ret))
00220         {
00221             DWORD tmp;
00222             if (ret)
00223             {
00224                 ReadConsoleInput(h, &p, 1, &ret);
00225                 assert(ret);
00226                 if (p.EventType == KEY_EVENT && p.Event.KeyEvent.bKeyDown)
00227                 {
00228                     int key = p.Event.KeyEvent.uChar.AsciiChar;
00229                     if (key == '\r') // end of line
00230                     {
00231                         xbuf[used++] = '\n';
00232                         WriteConsole(hout, "\r\n", 2, &tmp, NULL);
00233                         ret = used;
00234                         break;
00235                     }
00236                     else if (key == '\b' && used > 0)
00237                     {
00238                         used--;
00239                         WriteConsole(hout, "\b \b", 3, &tmp, NULL);
00240                     }
00241                     else if (key && used < len-1)
00242                     {
00243                         xbuf[used++] = key;
00244                         WriteConsole(hout, xbuf+used-1, 1, &tmp, NULL);
00245                     }
00246                 }
00247             }
00248             else
00249             {
00250                 DPRINTF(".");
00251                 WaitForSingleObjectEx(h, 1000, true);
00252             }
00253         }
00254         
00255         CloseHandle(hout);
00256     }
00257     else
00258     {
00259         // stdin might be redirected from a file, in which case we can
00260         // probably safely (heh) assume it'll never block.  Still, try
00261         // ReadFileEx with a timeout first and see if that works.
00262         DPRINTF("(stdin is a file)\n");
00263         while (!ret)
00264         {
00265             DPRINTF(".");
00266             int rv = 0;
00267             if (ReadFileEx(h, buf, 0, &ov, &completion))
00268             {
00269                 rv = SleepEx(1000, true);
00270                 CancelIo(h);
00271                 DPRINTF("(rv is %d)\n", rv);
00272                 if (rv == WAIT_IO_COMPLETION)
00273                 {
00274                     ReadFile(h, buf, len, &ret, NULL);
00275                     break;
00276                 }
00277                 else if (!rv) // timed out
00278                     Sleep(1); // ensure lock is released for nonzero time (1ms)
00279                 else
00280                     return 0; // unknown problem: assume EOF
00281             }
00282             else
00283             {
00284                 // can't do ReadFileEx: probably stupid Win9x.
00285                 ReadFile(h, buf, len, &ret, NULL);
00286                 break;
00287             }
00288         }
00289     }
00290     
00291     DPRINTF("[%d]\n", ret);
00292     return ret;
00293 }
00294 
00295 
00296 DWORD WINAPI fd2socket_fwd(LPVOID lpThreadParameter)
00297 {
00298 //    return 0;
00299     DWORD retval = 0;
00300     const int BUFSIZE = 512;
00301     socket_fd_pair *pair = (socket_fd_pair *)lpThreadParameter;
00302     
00303     // fprintf(stderr, "forwarding %d -> %d\n", 
00304     //         pair->fd, pair->socket); fflush(stderr);
00305     
00306     char buf[BUFSIZE];
00307     while (true)
00308     {
00309         char *ptr = buf;
00310         
00311         size_t bytes = fake_read(pair->fd, ptr, BUFSIZE);
00312         if (bytes <= 0) { retval = bytes; break; }
00313         while (bytes > 0)
00314         {
00315             int written = send(pair->socket, ptr, bytes, 0);
00316             if (written < 0) { retval = written; break; }
00317 
00318             bytes -= written;
00319             ptr += written;
00320         }
00321     }
00322 
00323     shutdown(pair->socket, SD_BOTH);
00324     closesocket(pair->socket);
00325     // fprintf(stderr, "TERMINATING-%d\n", pair->fd); fflush(stderr);
00326     return retval;
00327 }
00328 
00329 
00330 DWORD WINAPI socket2fd_fwd(LPVOID lpThreadParameter)
00331 {
00332     DWORD retval = 0;
00333     const int BUFSIZE = 512;
00334     socket_fd_pair *pair = (socket_fd_pair *)lpThreadParameter;
00335 
00336     char buf[BUFSIZE];
00337     while (true)
00338     {
00339         char *ptr = buf;
00340         int bytes = recv(pair->socket, ptr, BUFSIZE, 0);
00341         if (bytes <= 0) { retval = bytes; break; }
00342         while (bytes > 0)
00343         {
00344             int written = _write(pair->fd, ptr, bytes);
00345             if (written < 0) { retval = written; break; }
00346             bytes -= written;
00347             ptr += written;
00348         }
00349     }
00350     shutdown(pair->socket, SD_BOTH);
00351     closesocket(pair->socket);
00352     // fprintf(stderr, "TERMINATING-%d\n", pair->fd); fflush(stderr);
00353     return retval;
00354 }
00355 
00356 
00357 SocketFromFDMaker::SocketFromFDMaker(int fd,
00358                      LPTHREAD_START_ROUTINE lpStartAddress, bool wait)
00359     : m_hThread(0), m_socket(INVALID_SOCKET), m_wait(wait)
00360 {
00361     // might do this twice
00362     WSAData wsaData;
00363     WSAStartup(MAKEWORD(2,0), &wsaData);
00364 
00365     int s[2];
00366     socketpair(AF_INET, SOCK_STREAM, 0, s);
00367 
00368     m_pair.fd = fd;
00369     m_pair.socket = s[0];
00370     m_socket = s[1];
00371 
00372     DWORD threadid;
00373     m_hThread = CreateThread(
00374         NULL,
00375         0,
00376         lpStartAddress,
00377         &m_pair,
00378         0,
00379         &threadid
00380     );
00381     assert(m_hThread);
00382 }
00383 
00384 
00385 SocketFromFDMaker::~SocketFromFDMaker()
00386 {
00387     int result;
00388     if (m_socket != INVALID_SOCKET)
00389     {
00390         result = shutdown(m_socket, SD_BOTH);
00391         
00392         // this assertion will fail if someone has already closed the
00393         // socket; eg. if you give the socket to a WvFDStream and then let
00394         // him close it.  But you shouldn't do that, because nobody is
00395         // supposed to close stdin/stdout/stderr!
00396         assert(result == 0);
00397             
00398         if (m_wait) // wait for socket->fd copier
00399         {
00400             // wait for thread to terminate.  Since it's reading from a
00401             // socket (m_wait==true), this will be safe, because we know
00402             // it'll die politely when it should.
00403             WaitForSingleObject(m_hThread, INFINITE);
00404         }
00405         else
00406         {
00407             // FIXME: fd->socket copier will never die politely.  It gets
00408             // stuck in _read(), which enters a critical section and
00409             // then blocks until input is available.  Unfortunately, it's
00410             // impossible to make input *not* available.
00411             // 
00412             // TerminateThread() is generally evil, and doesn't help here
00413             // anyway: it just leaves that critical section locked forever, so
00414             // no operation on that fd will *ever* finish.
00415             // 
00416             // Answer: just do nothing.  Someone will clean up the broken
00417             // thread eventually, I guess.  (ExitProcess() claims to do
00418             // this, but I hope it doesn't get stuck in a critical section...)
00419         }
00420         
00421         close(m_socket);
00422     }
00423     CloseHandle(m_hThread);
00424 }

Generated on Sun Sep 24 20:10:47 2006 for WvStreams by  doxygen 1.4.7