/* ==== fd_sysv.c ============================================================ * Copyright (c) 1994 by Chris Provenzano, proven@mit.edu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Chris Provenzano. * 4. The name of Chris Provenzano may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY CHRIS PROVENZANO ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL CHRIS PROVENZANO BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Description : Transforms BSD socket calls to SYSV streams. * * 1.00 94/11/19 proven * -Started coding this file. */ #ifndef lint static const char rcsid[] = "$Id: fd_sysv.c,v 1.1 1998/07/21 13:19:59 peter Exp $"; #endif #include #include #include #include #if defined (HAVE_SYSCALL_PUTMSG) && defined (HAVE_SYSCALL_GETMSG) && !defined(HAVE_SYSCALL_SOCKETCALL) && !defined(HAVE_SYSCALL_SOCKET) #define HAVE_STREAMS 1 #include #include #include #include #include #include #include #include #include #define STREAM_BUF_SIZE sizeof(union T_primitives) + sizeof(struct sockaddr) extern struct pthread_queue fd_wait_read, fd_wait_write; /* ========================================================================== * putmsg_timedwait_basic() */ static int putmsg_timedwait_basic(int fd, struct strbuf * ctlptr, struct strbuf * dataptr, int flags, struct timespec * timeout) { int ret; pthread_run->sighandled=0; /* Added by monty */ while ((ret = machdep_sys_putmsg(fd_table[fd]->fd.i, ctlptr, dataptr, flags)) < OK) { if (!(fd_table[fd]->flags & __FD_NONBLOCK) && ((ret == -EWOULDBLOCK) || (ret == -EAGAIN))) { pthread_sched_prevent(); /* queue pthread for a FDW_WAIT */ SET_PF_WAIT_EVENT(pthread_run); pthread_run->data.fd.fd = fd_table[fd]->fd.i; pthread_queue_enq(&fd_wait_write, pthread_run); if (timeout) { /* get current time */ struct timespec current_time; machdep_gettimeofday(¤t_time); sleep_schedule(& current_time, timeout); pthread_resched_resume(PS_FDW_WAIT); /* We're awake */ pthread_sched_prevent(); if (sleep_cancel(pthread_run) == NOTOK) { CLEAR_PF_DONE_EVENT(pthread_run); pthread_sched_resume(); SET_ERRNO(ETIMEDOUT); ret = -ETIMEDOUT; break; } pthread_sched_resume(); } else { pthread_resched_resume(PS_FDW_WAIT); } CLEAR_PF_DONE_EVENT(pthread_run); if (pthread_run->sighandled) /* Added by monty */ { /* We where aborted */ SET_ERRNO(EINTR); ret= -EINTR; break; } } else { SET_ERRNO(-ret); break; } } return(ret); } /* ========================================================================== * putmsg_timedwait() */ int putmsg_timedwait(int fd, struct strbuf * ctlptr, struct strbuf * dataptr, int flags, struct timespec * timeout) { int ret; if ((ret = fd_lock(fd, FD_WRITE, timeout)) == OK) { ret = putmsg_timedwait_basic(fd, ctlptr, dataptr, flags, timeout); fd_unlock(fd, FD_WRITE); } return(ret); } /* ========================================================================== * putmsg() */ int putmsg(int fd, struct strbuf * ctlptr, struct strbuf * dataptr, int flags) { return(putmsg_timedwait(fd, ctlptr, dataptr, flags, NULL)); } /* ========================================================================== * getmsg_timedwait_basic() */ int getmsg_timedwait_basic(int fd, struct strbuf * ctlptr, struct strbuf * dataptr, int * flags, struct timespec * timeout) { int ret; pthread_run->sighandled=0; /* Added by monty */ while ((ret = machdep_sys_getmsg(fd_table[fd]->fd.i, ctlptr, dataptr, flags)) < OK) { if (!(fd_table[fd]->flags & __FD_NONBLOCK) && ((ret == -EWOULDBLOCK) || (ret == -EAGAIN))) { pthread_sched_prevent(); /* queue pthread for a FDR_WAIT */ SET_PF_WAIT_EVENT(pthread_run); pthread_run->data.fd.fd = fd_table[fd]->fd.i; pthread_queue_enq(&fd_wait_read, pthread_run); if (timeout) { /* get current time */ struct timespec current_time; machdep_gettimeofday(¤t_time); sleep_schedule(& current_time, timeout); pthread_resched_resume(PS_FDR_WAIT); /* We're awake */ pthread_sched_prevent(); if (sleep_cancel(pthread_run) == NOTOK) { CLEAR_PF_DONE_EVENT(pthread_run); pthread_sched_resume(); SET_ERRNO(ETIMEDOUT); ret = -ETIMEDOUT; break; } pthread_sched_resume(); } else { pthread_resched_resume(PS_FDR_WAIT); } CLEAR_PF_DONE_EVENT(pthread_run); if (pthread_run->sighandled) /* Added by monty */ { /* We where aborted */ SET_ERRNO(EINTR); ret= -EINTR; break; } } else { SET_ERRNO(-ret); break; } } return(ret); } /* ========================================================================== * getmsg_timedwait() */ int getmsg_timedwait(int fd, struct strbuf * ctlptr, struct strbuf * dataptr, int * flags, struct timespec * timeout) { int ret; if ((ret = fd_lock(fd, FD_READ, timeout)) == OK) { ret = getmsg_timedwait_basic(fd, ctlptr, dataptr, flags, timeout); fd_unlock(fd, FD_READ); } return (ret); } /* ========================================================================== * getmsg() */ int getmsg(int fd, struct strbuf * ctlptr, struct strbuf * dataptr, int * flags) { return(getmsg_timedwait(fd, ctlptr, dataptr, flags, NULL)); } #endif /* ========================================================================== * Here are the berkeley socket functions implemented with stream calls. * These are not POSIX. * ======================================================================= */ #if (!defined (HAVE_SYSCALL_BIND)) && defined(HAVE_STREAMS) /* ========================================================================== * bind() */ int bind(int fd, const struct sockaddr *name, int namelen) { char buf[STREAM_BUF_SIZE]; union T_primitives * res; struct T_bind_req * req; struct T_bind_ack * ack; struct strbuf strbuf; int flags, ret; if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) { req = (struct T_bind_req *)buf; req->PRIM_type = T_BIND_REQ; req->ADDR_length = namelen; req->ADDR_offset = sizeof(struct T_bind_req); req->CONIND_number = 4; memcpy(buf + sizeof(struct T_bind_req), name, namelen); strbuf.len = sizeof(struct T_bind_req) + namelen; strbuf.maxlen = STREAM_BUF_SIZE; strbuf.buf = buf; if ((ret=putmsg_timedwait_basic(fd, &strbuf, NULL, 0, NULL)) == OK) { memset(buf, 0, STREAM_BUF_SIZE); strbuf.len = sizeof(struct T_bind_ack) + namelen; strbuf.maxlen = STREAM_BUF_SIZE; strbuf.buf = buf; flags = 0; if ((ret = getmsg_timedwait_basic(fd, &strbuf, NULL, &flags, NULL)) >= OK) { res = (union T_primitives *)buf; switch(res->type) { case T_BIND_ACK: ret = OK; break; default: SET_ERRNO(EPROTO); /* What should this be? */ ret = NOTOK; break; } } else { SET_ERRNO(-ret); ret = NOTOK; } } else { SET_ERRNO(-ret); ret = NOTOK; } fd_unlock(fd, FD_RDWR); } return(ret); } #endif #if (!defined (HAVE_SYSCALL_CONNECT)) && defined(HAVE_STREAMS) /* ========================================================================== * connect() */ int connect(int fd, const struct sockaddr *name, int namelen) { char buf[STREAM_BUF_SIZE]; union T_primitives * res; struct T_conn_req * req; struct T_conn_con * con; struct T_ok_ack * ok; struct strbuf strbuf; int flags, ret; if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) { req = (struct T_conn_req *)buf; req->PRIM_type = T_CONN_REQ; req->DEST_length = namelen; req->DEST_offset = sizeof(struct T_conn_req); req->OPT_length = 0; req->OPT_offset = 0; memcpy(buf + sizeof(struct T_conn_req), name, namelen); strbuf.len = sizeof(struct T_conn_req) + namelen; strbuf.maxlen = STREAM_BUF_SIZE; strbuf.buf = buf; if ((ret=putmsg_timedwait_basic(fd, &strbuf, NULL, 0, NULL)) != OK) goto err; memset(buf, 0, STREAM_BUF_SIZE); ok = (struct T_ok_ack *)buf; strbuf.maxlen = STREAM_BUF_SIZE; strbuf.len = STREAM_BUF_SIZE; strbuf.buf = buf; flags = 0; if ((ret=getmsg_timedwait_basic(fd, &strbuf, NULL, &flags, NULL)) < OK) goto err; /* Fixed by monty */ if (ok->PRIM_type != T_OK_ACK) { ret= -EPROTO; /* What should this be? */ goto err; } memset(buf, 0, STREAM_BUF_SIZE); strbuf.maxlen = STREAM_BUF_SIZE; strbuf.len = STREAM_BUF_SIZE; strbuf.buf = buf; flags = 0; if ((ret=getmsg_timedwait_basic(fd, &strbuf, NULL, &flags, NULL) < OK)) goto err; res = (union T_primitives *) buf; switch(res->type) { case T_CONN_CON: ret = OK; break; case T_DISCON_IND: ret= -ECONNREFUSED; goto err; default: ret= -EPROTO; /* What should this be? */ goto err; } fd_unlock(fd, FD_RDWR); } return(ret); err: fd_unlock(fd, FD_RDWR); SET_ERRNO(-ret); /* Proably not needed... */ return NOTOK; } #endif #if (!defined (HAVE_SYSCALL_LISTEN)) && defined(HAVE_STREAMS) /* ========================================================================== * listen() */ int listen(int fd, int backlog) { return(OK); } #endif #if (!defined (HAVE_SYSCALL_SOCKET)) && defined(HAVE_STREAMS) extern ssize_t __fd_kern_write(); static pthread_ssize_t __fd_sysv_read(); extern int __fd_kern_close(); extern int __fd_kern_fcntl(); extern int __fd_kern_writev(); extern int __fd_kern_readv(); extern off_t __fd_kern_lseek(); /* Normal file operations */ static struct fd_ops __fd_sysv_ops = { __fd_kern_write, __fd_sysv_read, __fd_kern_close, __fd_kern_fcntl, __fd_kern_writev, __fd_kern_readv, __fd_kern_lseek, 1 }; /* ========================================================================== * read() */ static pthread_ssize_t __fd_sysv_read(union fd_data fd_data, int flags, void *buf, size_t nbytes, struct timespec * timeout) { struct strbuf dataptr; int fd = fd_data.i; int getmsg_flags; int ret; getmsg_flags = 0; dataptr.len = 0; dataptr.buf = buf; dataptr.maxlen = nbytes; pthread_run->sighandled=0; /* Added by monty */ while ((ret = machdep_sys_getmsg(fd, NULL, &dataptr, &getmsg_flags)) < OK) { if (!(fd_table[fd]->flags & __FD_NONBLOCK) && ((ret == -EWOULDBLOCK) || (ret == -EAGAIN))) { pthread_sched_prevent(); /* queue pthread for a FDR_WAIT */ pthread_run->data.fd.fd = fd; SET_PF_WAIT_EVENT(pthread_run); pthread_queue_enq(&fd_wait_read, pthread_run); if (timeout) { /* get current time */ struct timespec current_time; machdep_gettimeofday(¤t_time); sleep_schedule(& current_time, timeout); pthread_resched_resume(PS_FDR_WAIT); /* We're awake */ pthread_sched_prevent(); if (sleep_cancel(pthread_run) == NOTOK) { CLEAR_PF_DONE_EVENT(pthread_run); pthread_sched_resume(); SET_ERRNO(ETIMEDOUT); ret = -ETIMEDOUT; break; } pthread_sched_resume(); } else { pthread_resched_resume(PS_FDR_WAIT); } CLEAR_PF_DONE_EVENT(pthread_run); if (pthread_run->sighandled) /* Added by monty */ { /* We where aborted */ SET_ERRNO(EINTR); return(NOTOK); } } else { SET_ERRNO(-ret); return(NOTOK); break; } } return(dataptr.len); } /* ========================================================================== * socket_tcp() */ static int socket_tcp(int fd) { int ret; if ((ret = machdep_sys_open("/dev/tcp", O_RDWR | O_NONBLOCK, 0)) >= OK) { /* Should fstat the file to determine what type it is */ fd_table[fd]->ops = & __fd_sysv_ops; fd_table[fd]->type = FD_FULL_DUPLEX; fd_table[fd]->fd.i = ret; fd_table[fd]->flags = 0; } return(ret); } /* ========================================================================== * socket() */ int socket(int af, int type, int protocol) { int fd, fd_kern; if ((fd = fd_allocate()) < OK) return (fd); switch(af) { case AF_INET: switch(type) { case SOCK_STREAM: if ((fd_kern = socket_tcp(fd)) >= OK) return(fd); SET_ERRNO(-fd_kern); break; case SOCK_DGRAM: if ((fd_kern = machdep_sys_open("/dev/udp", O_RDWR | O_NONBLOCK, 0)) >= OK) { /* Should fstat the file to determine what type it is */ fd_table[fd]->ops = & __fd_sysv_ops; fd_table[fd]->type = FD_FULL_DUPLEX; fd_table[fd]->fd.i = fd_kern; fd_table[fd]->flags = 0; return(fd); } SET_ERRNO(-fd_kern); break; default: SET_ERRNO(EPROTONOSUPPORT); break; } break; case AF_UNIX: case AF_ISO: case AF_NS: default: SET_ERRNO(EPROTONOSUPPORT); break; } fd_table[fd]->count = 0; return(NOTOK); /* Fixed by monty */ } #endif #if (!defined (HAVE_SYSCALL_ACCEPT)) && defined(HAVE_STREAMS) /* ========================================================================== * accept_fd() */ static int accept_fd(int fd, struct sockaddr *name, int *namelen, char * buf, int SEQ_number) { struct T_conn_res * res; struct strbuf strbuf; int fd_new, fd_kern; /* Get a new table entry */ if ((fd_new = fd_allocate()) < OK) return(NOTOK); /* Get the new kernel entry */ if (!((fd_kern = socket_tcp(fd_new)) < OK)) { res = (struct T_conn_res *)buf; res->PRIM_type = T_CONN_RES; /* res->QUEUE_ptr = (queue_t *)&fd_kern; */ res->OPT_length = 0; res->OPT_offset = 0; res->SEQ_number = SEQ_number; strbuf.maxlen = sizeof(union T_primitives) +sizeof(struct sockaddr); strbuf.len = sizeof(struct T_conn_ind) + (*namelen); strbuf.buf = buf; { struct strfdinsert insert; insert.ctlbuf.maxlen = (sizeof(union T_primitives) + sizeof(struct sockaddr)); insert.ctlbuf.len = sizeof(struct T_conn_ind); insert.ctlbuf.buf = buf; insert.databuf.maxlen = 0; insert.databuf.len = 0; insert.databuf.buf = NULL; insert.flags = 0; insert.fildes = fd_kern; insert.offset = 4; /* Should the following be checked ? */ machdep_sys_ioctl(fd_table[fd]->fd.i, I_FDINSERT, &insert); } /* if (putmsg_timedwait_basic(fd, &strbuf, NULL, 0, NULL) == OK) { /* return(fd_new); */ { int flags = 0; int ret; /* Should the following be checked ? */ ret = getmsg_timedwait_basic(fd, &strbuf, NULL, &flags, NULL); return(fd_new); } machdep_sys_close(fd_kern); } fd_table[fd_new]->count = 0; return(NOTOK); } /* ========================================================================== * accept() */ int accept(int fd, struct sockaddr *name, int *namelen) { char buf[sizeof(union T_primitives) + sizeof(struct sockaddr)]; struct T_conn_ind * ind; struct strbuf strbuf; int flags, ret; if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) { ind = (struct T_conn_ind *)buf; ind->PRIM_type = T_CONN_IND; ind->SRC_length = (*namelen); ind->SRC_offset = sizeof(struct T_conn_ind); ind->OPT_length = 0; ind->OPT_offset = 0; ind->SEQ_number = 0; strbuf.maxlen = sizeof(union T_primitives) + sizeof(struct sockaddr); strbuf.len = sizeof(struct T_conn_ind) + (*namelen); strbuf.buf = buf; flags = 0; if ((ret=getmsg_timedwait_basic(fd, &strbuf, NULL, &flags, NULL)) < OK) { SET_ERRNO(-ret); ret= NOTOK; } else ret = accept_fd(fd, name, namelen, buf, ind->SEQ_number); fd_unlock(fd, FD_RDWR); } return(ret); } #endif /* HAVE_SYSCALL_ACCEPT */ #if (!defined (HAVE_SYSCALL_SENDTO)) && defined (HAVE_STREAMS) /* ========================================================================== * sendto_timedwait() */ ssize_t sendto_timedwait(int fd, const void * msg, size_t len, int flags, const struct sockaddr *name, int namelen, struct timespec * timeout) { char buf[STREAM_BUF_SIZE]; struct T_unitdata_req * req; struct strbuf dataptr; struct strbuf ctlptr; ssize_t ret, prio; req = (struct T_unitdata_req *)buf; req->PRIM_type = T_UNITDATA_REQ; req->DEST_length = namelen; req->DEST_offset = sizeof(struct T_unitdata_req); req->OPT_length = 0; req->OPT_offset = 0; memcpy(buf + sizeof(struct T_unitdata_req), name, namelen); ctlptr.len = sizeof(struct T_unitdata_req) + namelen; ctlptr.maxlen = STREAM_BUF_SIZE; ctlptr.buf = buf; dataptr.len = len; dataptr.maxlen = len; dataptr.buf = (void *)msg; if ((ret = putmsg_timedwait(fd, &ctlptr, &dataptr, 0, timeout)) == OK) { ret = len; } return(ret); } /* ========================================================================== * sendto() */ ssize_t sendto(int fd, const void * msg, size_t len, int flags, const struct sockaddr *to, int to_len) { return(sendto_timedwait(fd, msg, len, flags, to, to_len, NULL)); } #endif #if (!defined (HAVE_SYSCALL_SEND)) && defined (HAVE_STREAMS) /* ========================================================================== * send_timedwait() */ ssize_t send_timedwait(int fd, const void * msg, size_t len, int flags, struct timespec * timeout) { char buf[STREAM_BUF_SIZE]; struct T_unitdata_req * req; struct strbuf dataptr; struct strbuf ctlptr; ssize_t ret, prio; req = (struct T_unitdata_req *)buf; req->PRIM_type = T_UNITDATA_REQ; req->DEST_length = 0; req->DEST_offset = 0; req->OPT_length = 0; req->OPT_offset = 0; ctlptr.len = sizeof(struct T_unitdata_req); ctlptr.maxlen = STREAM_BUF_SIZE; ctlptr.buf = buf; dataptr.len = len; dataptr.maxlen = len; dataptr.buf = (void *)msg; if ((ret = putmsg_timedwait(fd, &ctlptr, &dataptr, 0, timeout)) == OK) { ret = len; } return(ret); } /* ========================================================================== * send() */ ssize_t send(int fd, const void * msg, size_t len, int flags) { return(send_timedwait(fd, msg, len, flags, NULL)); } #endif #if (!defined (HAVE_SYSCALL_RECVFROM)) && defined(HAVE_STREAMS) /* ========================================================================== * recvfrom_timedwait() */ ssize_t recvfrom_timedwait(int fd, void * msg, size_t len, int flags, struct sockaddr * name, int * namelen, struct timespec * timeout) { char buf[STREAM_BUF_SIZE]; struct T_unitdata_ind * ind; struct strbuf dataptr; struct strbuf ctlptr; int ret, prio; ctlptr.len = 0; ctlptr.maxlen = STREAM_BUF_SIZE; ctlptr.buf = buf; dataptr.maxlen = len; dataptr.len = 0; dataptr.buf = msg; prio = 0; ret = getmsg_timedwait(fd, &ctlptr, &dataptr, &prio, timeout); if (ret >= OK) { if (name != NULL) { ind = (struct T_unitdata_ind *)buf; if (*namelen > ind->SRC_length) *namelen = ind->SRC_length; memcpy(name, buf + ind->SRC_offset, *namelen); } ret = dataptr.len; } return(ret); } /* ========================================================================== * recvfrom() */ ssize_t recvfrom(int fd, void * buf, size_t len, int flags, struct sockaddr * from, int * from_len) { return(recvfrom_timedwait(fd, buf, len, flags, from, from_len, NULL)); } #endif #if (!defined (HAVE_SYSCALL_RECV)) && defined(HAVE_STREAMS) /* ========================================================================== * recv_timedwait() */ ssize_t recv_timedwait(int fd, void * msg, size_t len, int flags, struct timespec * timeout) { char buf[STREAM_BUF_SIZE]; struct T_unitdata_ind * ind; struct strbuf dataptr; struct strbuf ctlptr; int ret, prio; ctlptr.len = 0; ctlptr.maxlen = STREAM_BUF_SIZE; ctlptr.buf = buf; dataptr.maxlen = len; dataptr.len = 0; dataptr.buf = msg; prio = 0; ret = getmsg_timedwait(fd, &ctlptr, &dataptr, &prio, timeout); if (ret >= OK) ret = dataptr.len; return(ret); } /* ========================================================================== * recv() */ ssize_t recv(int fd, void * buf, size_t len, int flags, struct sockaddr * from, int * from_len) { return(recv_timedwait(fd, buf, len, flags, NULL)); } #endif #if (!defined (HAVE_SYSCALL_SETSOCKOPT)) && defined(HAVE_STREAMS) /* ========================================================================== * setsockopt() */ int setsockopt(int s, int level, int optname, const void *optval, int optlen) { return(0); } #endif struct foo { /* Used by getsockname and getpeername */ long a; int b; struct sockaddr *name; }; #if (!defined (HAVE_SYSCALL_GETSOCKNAME)) && defined(HAVE_STREAMS) /* ========================================================================== * getsockname() */ int getsockname(int s, struct sockaddr *name, int *namelen) { struct foo foo; int i; if (*namelen < sizeof(struct sockaddr)) { SET_ERRNO(ENOMEM); return(-1); } foo.a = 0x84; foo.b = 0; foo.name = name; i = ioctl(s, TI_GETMYNAME, &foo); *namelen = foo.b; return(i); } #endif #if (!defined (HAVE_SYSCALL_GETPEERNAME)) && defined(HAVE_STREAMS) /* ========================================================================== * getpeername() ; Added by Monty */ int getpeername(int s, struct sockaddr *name, int *namelen) { struct foo foo; int i; if (*namelen < sizeof(struct sockaddr)) { SET_ERRNO(ENOMEM); return(-1); } foo.a = 0x84; /* Max length ? */ foo.b = 0; /* Return length */ foo.name = name; /* Return buffer */ i = ioctl(s, TI_GETPEERNAME, &foo); *namelen = foo.b; return(i); } #endif #if (!defined (HAVE_SYSCALL_SHUTDOWN)) && defined(HAVE_STREAMS) /* ========================================================================== * shutdown() */ int shutdown(int s, int how) { return(0); } #endif