/* $XConsortium: fsio.c,v 1.37 95/04/05 19:58:13 kaleb Exp $ */ /* $XFree86: xc/lib/font/fc/fsio.c,v 3.5.2.2 1999/07/23 13:22:20 hohndel Exp $ */ /* * Copyright 1990 Network Computing Devices * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Network Computing Devices not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. Network Computing * Devices makes no representations about the suitability of this software * for any purpose. It is provided "as is" without express or implied * warranty. * * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE * OR PERFORMANCE OF THIS SOFTWARE. * * Author: Dave Lemke, Network Computing Devices, Inc */ /* * font server i/o routines */ #ifdef WIN32 #define _WILLWINSOCK_ #endif #include "FS.h" #include "FSproto.h" #include "X11/Xtrans.h" #include "X11/Xpoll.h" #include "fontmisc.h" #include "fsio.h" #include #include #include #if !defined(WIN32) && !defined(AMOEBA) && !defined(_MINIX) #ifndef Lynx #include #else #include #endif #endif #include #ifdef X_NOT_STDC_ENV extern int errno; #endif #ifdef WIN32 #define EWOULDBLOCK WSAEWOULDBLOCK #undef EINTR #define EINTR WSAEINTR #endif #ifdef MINIX #include #define select(n,r,w,x,t) nbio_select(n,r,w,x,t) #endif #ifdef __EMX__ #define select(n,r,w,x,t) os2PseudoSelect(n,r,w,x,t) #endif /* check for both EAGAIN and EWOULDBLOCK, because some supposedly POSIX * systems are broken and return EWOULDBLOCK when they should return EAGAIN */ #ifdef WIN32 #define ETEST() (WSAGetLastError() == WSAEWOULDBLOCK) #else #if defined(EAGAIN) && defined(EWOULDBLOCK) #define ETEST() (errno == EAGAIN || errno == EWOULDBLOCK) #else #ifdef EAGAIN #define ETEST() (errno == EAGAIN) #else #define ETEST() (errno == EWOULDBLOCK) #endif #endif #endif #ifdef WIN32 #define ECHECK(err) (WSAGetLastError() == err) #define ESET(val) WSASetLastError(val) #else #ifdef ISC #define ECHECK(err) ((errno == err) || ETEST()) #else #define ECHECK(err) (errno == err) #endif #define ESET(val) errno = val #endif static int padlength[4] = {0, 3, 2, 1}; fd_set _fs_fd_mask; int _fs_wait_for_readable(); #ifdef SIGNALRETURNSINT #define SIGNAL_T int #else #define SIGNAL_T void #endif /* ARGSUSED */ static SIGNAL_T _fs_alarm(foo) int foo; { return; } static XtransConnInfo _fs_connect(servername, timeout) char *servername; int timeout; { XtransConnInfo trans_conn; /* transport connection object */ int ret = -1; #ifdef SIGALRM unsigned oldTime; SIGNAL_T(*oldAlarm) (); #endif /* * Open the network connection. */ if( (trans_conn=_FontTransOpenCOTSClient(servername)) == NULL ) { return (NULL); } #ifdef SIGALRM oldTime = alarm((unsigned) 0); oldAlarm = signal(SIGALRM, _fs_alarm); alarm((unsigned) timeout); #endif ret = _FontTransConnect(trans_conn,servername); #ifdef SIGALRM alarm((unsigned) 0); signal(SIGALRM, oldAlarm); alarm(oldTime); #endif if (ret < 0) { _FontTransClose(trans_conn); return (NULL); } /* * Set the connection non-blocking since we use select() to block. */ _FontTransSetOption(trans_conn, TRANS_NONBLOCKING, 1); return trans_conn; } static int generationCount; /* ARGSUSED */ static Bool _fs_setup_connection(conn, servername, timeout, copy_name_p) FSFpePtr conn; char *servername; int timeout; Bool copy_name_p; { fsConnClientPrefix prefix; fsConnSetup rep; int setuplength; fsConnSetupAccept conn_accept; int endian; int i; int alt_len; char *auth_data = NULL, *vendor_string = NULL, *alt_data = NULL, *alt_dst; FSFpeAltPtr alts; int nalts; if ((conn->trans_conn = _fs_connect(servername, 5)) == NULL) return FALSE; conn->fs_fd = _FontTransGetConnectionNumber (conn->trans_conn); conn->generation = ++generationCount; /* send setup prefix */ endian = 1; if (*(char *) &endian) prefix.byteOrder = 'l'; else prefix.byteOrder = 'B'; prefix.major_version = FS_PROTOCOL; prefix.minor_version = FS_PROTOCOL_MINOR; /* XXX add some auth info here */ prefix.num_auths = 0; prefix.auth_len = 0; if (_fs_write(conn, (char *) &prefix, (unsigned long) SIZEOF(fsConnClientPrefix)) == -1) return FALSE; /* read setup info */ if (_fs_read(conn, (char *) &rep, (unsigned long) SIZEOF(fsConnSetup)) == -1) return FALSE; conn->fsMajorVersion = rep.major_version; if (rep.major_version > FS_PROTOCOL) return FALSE; alts = 0; /* parse alternate list */ if (nalts = rep.num_alternates) { setuplength = rep.alternate_len << 2; alts = (FSFpeAltPtr) xalloc(nalts * sizeof(FSFpeAltRec) + setuplength); if (!alts) { _FontTransClose(conn->trans_conn); errno = ENOMEM; return FALSE; } alt_data = (char *) (alts + nalts); if (_fs_read(conn, (char *) alt_data, (unsigned long) setuplength) == -1) { xfree(alts); return FALSE; } alt_dst = alt_data; for (i = 0; i < nalts; i++) { alts[i].subset = alt_data[0]; alt_len = alt_data[1]; alts[i].name = alt_dst; memmove(alt_dst, alt_data + 2, alt_len); alt_dst[alt_len] = '\0'; alt_dst += (alt_len + 1); alt_data += (2 + alt_len + padlength[(2 + alt_len) & 3]); } } if (conn->alts) xfree(conn->alts); conn->alts = alts; conn->numAlts = nalts; setuplength = rep.auth_len << 2; if (setuplength && !(auth_data = (char *) xalloc((unsigned int) setuplength))) { _FontTransClose(conn->trans_conn); errno = ENOMEM; return FALSE; } if (_fs_read(conn, (char *) auth_data, (unsigned long) setuplength) == -1) { xfree(auth_data); return FALSE; } if (rep.status != AuthSuccess) { xfree(auth_data); _FontTransClose(conn->trans_conn); errno = EPERM; return FALSE; } /* get rest */ if (_fs_read(conn, (char *) &conn_accept, (long) SIZEOF(fsConnSetupAccept)) == -1) { xfree(auth_data); return FALSE; } if ((vendor_string = (char *) xalloc((unsigned) conn_accept.vendor_len + 1)) == NULL) { xfree(auth_data); _FontTransClose(conn->trans_conn); errno = ENOMEM; return FALSE; } if (_fs_read_pad(conn, (char *) vendor_string, conn_accept.vendor_len) == -1) { xfree(vendor_string); xfree(auth_data); return FALSE; } xfree(auth_data); xfree(vendor_string); if (copy_name_p) { conn->servername = (char *) xalloc(strlen(servername) + 1); if (conn->servername == NULL) return FALSE; strcpy(conn->servername, servername); } else conn->servername = servername; return TRUE; } static Bool _fs_try_alternates(conn, timeout) FSFpePtr conn; int timeout; { int i; for (i = 0; i < conn->numAlts; i++) if (_fs_setup_connection(conn, conn->alts[i].name, timeout, TRUE)) return TRUE; return FALSE; } #define FS_OPEN_TIMEOUT 30 #define FS_REOPEN_TIMEOUT 10 FSFpePtr _fs_open_server(servername) char *servername; { FSFpePtr conn; conn = (FSFpePtr) xalloc(sizeof(FSFpeRec)); if (!conn) { errno = ENOMEM; return (FSFpePtr) NULL; } bzero((char *) conn, sizeof(FSFpeRec)); if (!_fs_setup_connection(conn, servername, FS_OPEN_TIMEOUT, TRUE)) { if (!_fs_try_alternates(conn, FS_OPEN_TIMEOUT)) { xfree(conn->alts); xfree(conn); return (FSFpePtr) NULL; } } return conn; } Bool _fs_reopen_server(conn) FSFpePtr conn; { if (_fs_setup_connection(conn, conn->servername, FS_REOPEN_TIMEOUT, FALSE)) return TRUE; if (_fs_try_alternates(conn, FS_REOPEN_TIMEOUT)) return TRUE; return FALSE; } /* * expects everything to be here. *not* to be called when reading huge * numbers of replies, but rather to get each chunk */ _fs_read(conn, data, size) FSFpePtr conn; char *data; unsigned long size; { long bytes_read; #if defined(SVR4) && defined(i386) int num_failed_reads = 0; #endif if (size == 0) { #ifdef DEBUG fprintf(stderr, "tried to read 0 bytes \n"); #endif return 0; } ESET(0); /* * For SVR4 with a unix-domain connection, ETEST() after selecting * readable means the server has died. To do this here, we look for * two consecutive reads returning ETEST(). */ while ((bytes_read = _FontTransRead(conn->trans_conn, data, (int) size)) != size) { if (bytes_read > 0) { size -= bytes_read; data += bytes_read; #if defined(SVR4) && defined(i386) num_failed_reads = 0; #endif } else if (ETEST()) { /* in a perfect world, this shouldn't happen */ /* ... but then, its less than perfect... */ if (_fs_wait_for_readable(conn) == -1) { /* check for error */ _fs_connection_died(conn); ESET(EPIPE); return -1; } #if defined(SVR4) && defined(i386) num_failed_reads++; if (num_failed_reads > 1) { _fs_connection_died(conn); ESET(EPIPE); return -1; } #endif ESET(0); } else if (ECHECK(EINTR)) { #if defined(SVR4) && defined(i386) num_failed_reads = 0; #endif continue; } else { /* something bad happened */ if (conn->fs_fd > 0) _fs_connection_died(conn); ESET(EPIPE); return -1; } } return 0; } _fs_write(conn, data, size) FSFpePtr conn; char *data; unsigned long size; { long bytes_written; if (size == 0) { #ifdef DEBUG fprintf(stderr, "tried to write 0 bytes \n"); #endif return 0; } /* XXX - hack. The right fix is to remember that the font server has gone away when we first discovered it. */ if (!conn->trans_conn) return -1; ESET(0); while ((bytes_written = _FontTransWrite(conn->trans_conn, data, (int) size)) != size) { if (bytes_written > 0) { size -= bytes_written; data += bytes_written; } else if (ETEST()) { /* XXX -- we assume this can't happen */ #ifdef DEBUG fprintf(stderr, "fs_write blocking\n"); #endif } else if (ECHECK(EINTR)) { continue; } else { /* something bad happened */ _fs_connection_died(conn); ESET(EPIPE); return -1; } } return 0; } _fs_read_pad(conn, data, len) FSFpePtr conn; char *data; int len; { char pad[3]; if (_fs_read(conn, data, len) == -1) return -1; /* read the junk */ if (padlength[len & 3]) { return _fs_read(conn, pad, padlength[len & 3]); } return 0; } _fs_write_pad(conn, data, len) FSFpePtr conn; char *data; int len; { static char pad[3]; if (_fs_write(conn, data, len) == -1) return -1; /* write the pad */ if (padlength[len & 3]) { return _fs_write(conn, pad, padlength[len & 3]); } return 0; } /* * returns the amount of data waiting to be read */ int _fs_data_ready(conn) FSFpePtr conn; { BytesReadable_t readable; if (_FontTransBytesReadable(conn->trans_conn, &readable) < 0) return -1; return readable; } int _fs_wait_for_readable(conn) FSFpePtr conn; { #ifndef AMOEBA fd_set r_mask; fd_set e_mask; int result; #ifdef DEBUG fprintf(stderr, "read would block\n"); #endif do { FD_ZERO(&r_mask); #ifndef MINIX FD_ZERO(&e_mask); #endif FD_SET(conn->fs_fd, &r_mask); FD_SET(conn->fs_fd, &e_mask); result = Select(conn->fs_fd + 1, &r_mask, NULL, &e_mask, NULL); if (result == -1) { if (ECHECK(EINTR) || ECHECK(EAGAIN)) continue; else return -1; } if (result && FD_ISSET(conn->fs_fd, &e_mask)) return -1; } while (result <= 0); return 0; #else printf("fs_wait_for_readable(): fail\n"); return -1; #endif } int _fs_set_bit(mask, fd) fd_set* mask; int fd; { FD_SET(fd, mask); return fd; } int _fs_is_bit_set(mask, fd) fd_set* mask; int fd; { return FD_ISSET(fd, mask); } void _fs_bit_clear(mask, fd) fd_set* mask; int fd; { FD_CLR(fd, mask); } int _fs_any_bit_set(mask) fd_set* mask; { XFD_ANYSET(mask); } int _fs_or_bits(dst, m1, m2) fd_set* dst; fd_set* m1; fd_set* m2; { #ifdef WIN32 int i; if (dst != m1) { for (i = m1->fd_count; --i >= 0; ) { if (!FD_ISSET(m1->fd_array[i], dst)) FD_SET(m1->fd_array[i], dst); } } if (dst != m2) { for (i = m2->fd_count; --i >= 0; ) { if (!FD_ISSET(m2->fd_array[i], dst)) FD_SET(m2->fd_array[i], dst); } } #else XFD_ORSET(dst, m1, m2); #endif } _fs_drain_bytes(conn, len) FSFpePtr conn; int len; { char buf[128]; #ifdef DEBUG fprintf(stderr, "draining wire\n"); #endif while (len > 0) { if (_fs_read(conn, buf, (len < 128) ? len : 128) < 0) return -1; len -= 128; } return 0; } _fs_drain_bytes_pad(conn, len) FSFpePtr conn; int len; { _fs_drain_bytes(conn, len); /* read the junk */ if (padlength[len & 3]) { _fs_drain_bytes(conn, padlength[len & 3]); } } _fs_eat_rest_of_error(conn, err) FSFpePtr conn; fsError *err; { int len = (err->length - (SIZEOF(fsGenericReply) >> 2)) << 2; #ifdef DEBUG fprintf(stderr, "clearing error\n"); #endif _fs_drain_bytes(conn, len); }