WvStreams
|
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 _MSC_VER 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 int socketpair(int family, int type, int protocol, int *sb) 00114 { 00115 SOCKET insock, outsock, newsock; 00116 struct sockaddr_in sock_in; 00117 00118 if (type != SOCK_STREAM) 00119 return -1; 00120 00121 newsock = socket(AF_INET, type, 0); 00122 if (newsock == INVALID_SOCKET) 00123 return -1; 00124 00125 sock_in.sin_family = AF_INET; 00126 sock_in.sin_port = 0; 00127 sock_in.sin_addr.s_addr = INADDR_ANY; 00128 if (bind(newsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0) 00129 return -1; 00130 00131 int len = sizeof (sock_in); 00132 if (getsockname(newsock, (struct sockaddr *)&sock_in, &len) < 0) 00133 return -1; 00134 00135 if (listen(newsock, 2) < 0) 00136 return -1; 00137 00138 outsock = socket(AF_INET, type, 0); 00139 if (outsock == INVALID_SOCKET) 00140 return -1; 00141 00142 sock_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 00143 if (connect(outsock, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0) 00144 return -1; 00145 00146 /* For stream sockets, accept the connection and close the listener */ 00147 len = sizeof(sock_in); 00148 insock = accept(newsock, (struct sockaddr *)&sock_in, &len); 00149 if (insock == INVALID_SOCKET) 00150 return -1; 00151 00152 if (closesocket(newsock) < 0) 00153 return -1; 00154 00155 sb[0] = insock; 00156 sb[1] = outsock; 00157 return 0; 00158 } 00159 00160 00161 static void CALLBACK completion(DWORD error, DWORD nread, LPOVERLAPPED ov) 00162 { 00163 } 00164 00165 00166 static size_t fake_read(int fd, void *buf, size_t len) 00167 { 00168 HANDLE h = (HANDLE)_get_osfhandle(fd); 00169 INPUT_RECORD p; 00170 DPRINTF("fake_read(%d/%d,%p,%d) = ", fd, (int)h, buf, (int)len); 00171 00172 DWORD ret = 0; 00173 OVERLAPPED ov; 00174 memset(&ov, 0, sizeof(ov)); 00175 ov.Offset = SetFilePointer(h, 0, NULL, FILE_CURRENT); 00176 00177 if (PeekNamedPipe(h, NULL, 0, NULL, &ret, NULL)) 00178 { 00179 // cygwin sshd/telnetd uses named pipes for stdin. We have to 00180 // support these separately. Getting stuck in ReadFile on a named 00181 // pipe appears to freeze up gethostbyname() for some reason on win2k! 00182 DPRINTF("(stdin is a pipe)\n"); 00183 while (PeekNamedPipe(h, NULL, 0, NULL, &ret, NULL) && !ret) 00184 { 00185 DPRINTF("."); 00186 Sleep(100); 00187 } 00188 ReadFile(h, buf, len, &ret, NULL); 00189 } 00190 else if (PeekConsoleInput(h, &p, 1, &ret)) 00191 { 00192 // a typical stdin/out pair refers to a console. Unfortunately, 00193 // console I/O is stupid: you can poll it to see if it's ready, but 00194 // if you have it in line mode, then it's not *really* ready. 00195 // ReadConsole/ReadFile will only return after the user hits enter. 00196 // Unfortunately, it seems the only way around this is to disable 00197 // line/echo mode and fake it ourselves. Hopefully this isn't too 00198 // ugly... 00199 DPRINTF("(stdin is a console)\n"); 00200 00201 size_t used = 0; 00202 char *xbuf = (char *)buf; 00203 HANDLE hout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, 00204 FILE_SHARE_READ | FILE_SHARE_WRITE, 00205 NULL, OPEN_EXISTING, 0, 0); 00206 00207 DWORD conmode = 0; 00208 GetConsoleMode(h, &conmode); 00209 SetConsoleMode(h, conmode & 00210 ~(ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT | ENABLE_ECHO_INPUT)); 00211 00212 while (PeekConsoleInput(h, &p, 1, &ret)) 00213 { 00214 DWORD tmp; 00215 if (ret) 00216 { 00217 ReadConsoleInput(h, &p, 1, &ret); 00218 assert(ret); 00219 if (p.EventType == KEY_EVENT && p.Event.KeyEvent.bKeyDown) 00220 { 00221 int key = p.Event.KeyEvent.uChar.AsciiChar; 00222 if (key == '\r') // end of line 00223 { 00224 xbuf[used++] = '\n'; 00225 WriteConsole(hout, "\r\n", 2, &tmp, NULL); 00226 ret = used; 00227 break; 00228 } 00229 else if (key == '\b' && used > 0) 00230 { 00231 used--; 00232 WriteConsole(hout, "\b \b", 3, &tmp, NULL); 00233 } 00234 else if (key && used < len-1) 00235 { 00236 xbuf[used++] = key; 00237 WriteConsole(hout, xbuf+used-1, 1, &tmp, NULL); 00238 } 00239 } 00240 } 00241 else 00242 { 00243 DPRINTF("."); 00244 WaitForSingleObjectEx(h, 1000, true); 00245 } 00246 } 00247 00248 CloseHandle(hout); 00249 } 00250 else 00251 { 00252 // stdin might be redirected from a file, in which case we can 00253 // probably safely (heh) assume it'll never block. Still, try 00254 // ReadFileEx with a timeout first and see if that works. 00255 DPRINTF("(stdin is a file)\n"); 00256 while (!ret) 00257 { 00258 DPRINTF("."); 00259 int rv = 0; 00260 if (ReadFileEx(h, buf, 0, &ov, &completion)) 00261 { 00262 rv = SleepEx(1000, true); 00263 CancelIo(h); 00264 DPRINTF("(rv is %d)\n", rv); 00265 if (rv == WAIT_IO_COMPLETION) 00266 { 00267 ReadFile(h, buf, len, &ret, NULL); 00268 break; 00269 } 00270 else if (!rv) // timed out 00271 Sleep(1); // ensure lock is released for nonzero time (1ms) 00272 else 00273 return 0; // unknown problem: assume EOF 00274 } 00275 else 00276 { 00277 // can't do ReadFileEx: probably stupid Win9x. 00278 ReadFile(h, buf, len, &ret, NULL); 00279 break; 00280 } 00281 } 00282 } 00283 00284 DPRINTF("[%d]\n", ret); 00285 return ret; 00286 } 00287 00288 00289 DWORD WINAPI fd2socket_fwd(LPVOID lpThreadParameter) 00290 { 00291 // return 0; 00292 DWORD retval = 0; 00293 const int BUFSIZE = 512; 00294 socket_fd_pair *pair = (socket_fd_pair *)lpThreadParameter; 00295 00296 // fprintf(stderr, "forwarding %d -> %d\n", 00297 // pair->fd, pair->socket); fflush(stderr); 00298 00299 char buf[BUFSIZE]; 00300 while (true) 00301 { 00302 char *ptr = buf; 00303 00304 size_t bytes = fake_read(pair->fd, ptr, BUFSIZE); 00305 if (bytes <= 0) { retval = bytes; break; } 00306 while (bytes > 0) 00307 { 00308 int written = send(pair->socket, ptr, bytes, 0); 00309 if (written < 0) { retval = written; break; } 00310 00311 bytes -= written; 00312 ptr += written; 00313 } 00314 } 00315 00316 shutdown(pair->socket, SD_BOTH); 00317 closesocket(pair->socket); 00318 // fprintf(stderr, "TERMINATING-%d\n", pair->fd); fflush(stderr); 00319 return retval; 00320 } 00321 00322 00323 DWORD WINAPI socket2fd_fwd(LPVOID lpThreadParameter) 00324 { 00325 DWORD retval = 0; 00326 const int BUFSIZE = 512; 00327 socket_fd_pair *pair = (socket_fd_pair *)lpThreadParameter; 00328 00329 char buf[BUFSIZE]; 00330 while (true) 00331 { 00332 char *ptr = buf; 00333 int bytes = recv(pair->socket, ptr, BUFSIZE, 0); 00334 if (bytes <= 0) { retval = bytes; break; } 00335 while (bytes > 0) 00336 { 00337 int written = _write(pair->fd, ptr, bytes); 00338 if (written < 0) { retval = written; break; } 00339 bytes -= written; 00340 ptr += written; 00341 } 00342 } 00343 shutdown(pair->socket, SD_BOTH); 00344 closesocket(pair->socket); 00345 // fprintf(stderr, "TERMINATING-%d\n", pair->fd); fflush(stderr); 00346 return retval; 00347 } 00348 00349 00350 SocketFromFDMaker::SocketFromFDMaker(int fd, 00351 LPTHREAD_START_ROUTINE lpStartAddress, bool wait) 00352 : m_hThread(0), m_socket(INVALID_SOCKET), m_wait(wait) 00353 { 00354 // might do this twice 00355 WSAData wsaData; 00356 WSAStartup(MAKEWORD(2,0), &wsaData); 00357 00358 int s[2], result; 00359 result = socketpair(AF_INET, SOCK_STREAM, 0, s); 00360 assert(result == 0); 00361 00362 m_pair.fd = fd; 00363 m_pair.socket = s[0]; 00364 m_socket = s[1]; 00365 00366 DWORD threadid; 00367 m_hThread = CreateThread( 00368 NULL, 00369 0, 00370 lpStartAddress, 00371 &m_pair, 00372 0, 00373 &threadid 00374 ); 00375 assert(m_hThread); 00376 } 00377 00378 00379 SocketFromFDMaker::~SocketFromFDMaker() 00380 { 00381 int result; 00382 // fprintf(stderr, "shutting down #%d\n", m_socket); 00383 if (m_socket != INVALID_SOCKET) 00384 { 00385 result = shutdown(m_socket, SD_BOTH); 00386 00387 // this assertion will fail if someone has already closed the 00388 // socket; eg. if you give the socket to a WvFDStream and then let 00389 // him close it. But you shouldn't do that, because nobody is 00390 // supposed to close stdin/stdout/stderr! 00391 if (result != 0) 00392 { 00393 int e = GetLastError(); 00394 if (e == WSASYSNOTREADY || e == WSANOTINITIALISED) 00395 { 00396 fprintf(stderr, "Abnormal termination. Skipping cleanup.\n"); 00397 _exit(42); 00398 } 00399 else 00400 { 00401 fprintf(stderr, 00402 "ERROR! Socket #%d was already shut down! (%d)\n", 00403 m_socket, e); 00404 assert(result == 0); 00405 } 00406 } 00407 00408 if (m_wait) // wait for socket->fd copier 00409 { 00410 // wait for thread to terminate. Since it's reading from a 00411 // socket (m_wait==true), this will be safe, because we know 00412 // it'll die politely when it should. 00413 WaitForSingleObject(m_hThread, INFINITE); 00414 } 00415 else 00416 { 00417 // FIXME: fd->socket copier will never die politely. It gets 00418 // stuck in _read(), which enters a critical section and 00419 // then blocks until input is available. Unfortunately, it's 00420 // impossible to make input *not* available. 00421 // 00422 // TerminateThread() is generally evil, and doesn't help here 00423 // anyway: it just leaves that critical section locked forever, so 00424 // no operation on that fd will *ever* finish. 00425 // 00426 // Answer: just do nothing. Someone will clean up the broken 00427 // thread eventually, I guess. (ExitProcess() claims to do 00428 // this, but I hope it doesn't get stuck in a critical section...) 00429 } 00430 00431 close(m_socket); 00432 } 00433 CloseHandle(m_hThread); 00434 }