diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
commit | 1513ff9b7899ab588401c89db0e99903dbf5f886 (patch) | |
tree | f69cc81a940a502ea23d664c3ffb2d215a479667 /net/unix |
Import of Linus's Linux 1.1.68
Diffstat (limited to 'net/unix')
-rw-r--r-- | net/unix/Makefile | 33 | ||||
-rw-r--r-- | net/unix/proc.c | 100 | ||||
-rw-r--r-- | net/unix/sock.c | 901 | ||||
-rw-r--r-- | net/unix/unix.h | 69 |
4 files changed, 1103 insertions, 0 deletions
diff --git a/net/unix/Makefile b/net/unix/Makefile new file mode 100644 index 000000000..e4fb629bd --- /dev/null +++ b/net/unix/Makefile @@ -0,0 +1,33 @@ +# +# Makefile for the UNIX Protocol Family. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +.c.o: + $(CC) $(CFLAGS) -c $< +.s.o: + $(AS) -o $*.o $< +.c.s: + $(CC) $(CFLAGS) -S $< + +OBJS = sock.o proc.o + +unix.o: $(OBJS) + $(LD) -r -o unix.o $(OBJS) + +dep: + $(CPP) -M *.c > .depend + +tar: + tar -cvf /dev/f1 . + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/net/unix/proc.c b/net/unix/proc.c new file mode 100644 index 000000000..36cd8b1c6 --- /dev/null +++ b/net/unix/proc.c @@ -0,0 +1,100 @@ +/* + * UNIX An implementation of the AF_UNIX network domain for the + * LINUX operating system. UNIX is implemented using the + * BSD Socket interface as the means of communication with + * the user level. + * + * The functions in this file provide an interface between + * the PROC file system and the "unix" family of networking + * protocols. It is mainly used for debugging and statistics. + * + * Version: @(#)proc.c 1.0.4 05/23/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de> + * Fred Baumgarten, <dc6iq@insu1.etec.uni-kalrsruhe.de> + * + * Fixes: + * Dmitry Gorodchanin : /proc locking fix + * Mathijs Maassen : unbound /proc fix. + * Alan Cox : Fix sock=NULL race + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/autoconf.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/socket.h> +#include <linux/net.h> +#include <linux/un.h> +#include <linux/param.h> +#include "unix.h" + + +/* Called from PROCfs. */ +int unix_get_info(char *buffer, char **start, off_t offset, int length) +{ + off_t pos=0; + off_t begin=0; + int len=0; + int i; + unsigned long flags; + socket_state s_state; + short s_type; + long s_flags; + + len += sprintf(buffer, "Num RefCount Protocol Flags Type St Path\n"); + + for(i = 0; i < NSOCKETS; i++) + { + save_flags(flags); + cli(); + if (unix_datas[i].refcnt>0 && unix_datas[i].socket!=NULL) + { + /* sprintf is slow... lock only for the variable reads */ + s_type=unix_datas[i].socket->type; + s_flags=unix_datas[i].socket->flags; + s_state=unix_datas[i].socket->state; + restore_flags(flags); + len += sprintf(buffer+len, "%2d: %08X %08X %08lX %04X %02X", i, + unix_datas[i].refcnt, + unix_datas[i].protocol, + s_flags, + s_type, + s_state + ); + + /* If socket is bound to a filename, we'll print it. */ + if(unix_datas[i].sockaddr_len>0) + { + len += sprintf(buffer+len, " %s\n", + unix_datas[i].sockaddr_un.sun_path); + } + else + { /* just add a newline */ + buffer[len++]='\n'; + } + + pos=begin+len; + if(pos<offset) + { + len=0; + begin=pos; + } + if(pos>offset+length) + break; + } + else + restore_flags(flags); + } + + *start=buffer+(offset-begin); + len-=(offset-begin); + if(len>length) + len=length; + return len; +} diff --git a/net/unix/sock.c b/net/unix/sock.c new file mode 100644 index 000000000..73460bc7e --- /dev/null +++ b/net/unix/sock.c @@ -0,0 +1,901 @@ +/* + * UNIX An implementation of the AF_UNIX network domain for the + * LINUX operating system. UNIX is implemented using the + * BSD Socket interface as the means of communication with + * the user level. + * + * Version: @(#)sock.c 1.0.5 05/25/93 + * + * Authors: Orest Zborowski, <obz@Kodak.COM> + * Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * + * Fixes: + * Alan Cox : Verify Area + * NET2E Team : Page fault locks + * Dmitry Gorodchanin : /proc locking + * + * To Do: + * Some nice person is looking into Unix sockets done properly. NET3 + * will replace all of this and include datagram sockets and socket + * options - so please stop asking me for them 8-) + * + * + * 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; either version + * 2 of the License, or(at your option) any later version. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/stat.h> +#include <linux/socket.h> +#include <linux/un.h> +#include <linux/fcntl.h> +#include <linux/termios.h> +#include <linux/sockios.h> +#include <linux/net.h> +#include <linux/fs.h> +#include <linux/malloc.h> + +#include <asm/system.h> +#include <asm/segment.h> + +#include <stdarg.h> + +#include "unix.h" + +/* + * Because these have the address in them they casually waste an extra 8K of kernel data + * space that need not be wasted. + */ + +struct unix_proto_data unix_datas[NSOCKETS]; + +static int unix_proto_create(struct socket *sock, int protocol); +static int unix_proto_dup(struct socket *newsock, struct socket *oldsock); +static int unix_proto_release(struct socket *sock, struct socket *peer); +static int unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr, + int sockaddr_len); +static int unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr, + int sockaddr_len, int flags); +static int unix_proto_socketpair(struct socket *sock1, struct socket *sock2); +static int unix_proto_accept(struct socket *sock, struct socket *newsock, + int flags); +static int unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr, + int *usockaddr_len, int peer); +static int unix_proto_read(struct socket *sock, char *ubuf, int size, + int nonblock); +static int unix_proto_write(struct socket *sock, char *ubuf, int size, + int nonblock); +static int unix_proto_select(struct socket *sock, int sel_type, select_table * wait); +static int unix_proto_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg); +static int unix_proto_listen(struct socket *sock, int backlog); +static int unix_proto_send(struct socket *sock, void *buff, int len, + int nonblock, unsigned flags); +static int unix_proto_recv(struct socket *sock, void *buff, int len, + int nonblock, unsigned flags); +static int unix_proto_sendto(struct socket *sock, void *buff, int len, + int nonblock, unsigned flags, + struct sockaddr *addr, int addr_len); +static int unix_proto_recvfrom(struct socket *sock, void *buff, int len, + int nonblock, unsigned flags, + struct sockaddr *addr, int *addr_len); + +static int unix_proto_shutdown(struct socket *sock, int how); + +static int unix_proto_setsockopt(struct socket *sock, int level, int optname, + char *optval, int optlen); +static int unix_proto_getsockopt(struct socket *sock, int level, int optname, + char *optval, int *optlen); + + +static inline int min(int a, int b) +{ + if (a < b) + return(a); + return(b); +} + + + +/* Support routines doing anti page fault locking + * FvK & Matt Dillon (borrowed From NET2E3) + */ + +/* + * Locking for unix-domain sockets. We don't use the socket structure's + * wait queue because it is allowed to 'go away' outside of our control, + * whereas unix_proto_data structures stick around. + */ + +static void unix_lock(struct unix_proto_data *upd) +{ + while (upd->lock_flag) + sleep_on(&upd->wait); + upd->lock_flag = 1; +} + + +static void unix_unlock(struct unix_proto_data *upd) +{ + upd->lock_flag = 0; + wake_up(&upd->wait); +} + +/* + * We don't have to do anything. + */ + +static int unix_proto_listen(struct socket *sock, int backlog) +{ + return(0); +} + +/* + * Until the new NET3 Unix code is done we have no options. + */ + +static int unix_proto_setsockopt(struct socket *sock, int level, int optname, + char *optval, int optlen) +{ + return(-EOPNOTSUPP); +} + + +static int unix_proto_getsockopt(struct socket *sock, int level, int optname, + char *optval, int *optlen) +{ + return(-EOPNOTSUPP); +} + + +/* + * SendTo() doesn't matter as we also have no Datagram support! + */ + +static int unix_proto_sendto(struct socket *sock, void *buff, int len, int nonblock, + unsigned flags, struct sockaddr *addr, int addr_len) +{ + return(-EOPNOTSUPP); +} + +static int unix_proto_recvfrom(struct socket *sock, void *buff, int len, int nonblock, + unsigned flags, struct sockaddr *addr, int *addr_len) +{ + return(-EOPNOTSUPP); +} + +/* + * You can't shutdown a unix domain socket. + */ + +static int unix_proto_shutdown(struct socket *sock, int how) +{ + return(-EOPNOTSUPP); +} + + +/* + * Send data to a unix socket. + */ + +static int unix_proto_send(struct socket *sock, void *buff, int len, int nonblock, + unsigned flags) +{ + if (flags != 0) + return(-EINVAL); + return(unix_proto_write(sock, (char *) buff, len, nonblock)); +} + + +/* + * Receive data. This version of AF_UNIX also lacks MSG_PEEK 8( + */ + +static int unix_proto_recv(struct socket *sock, void *buff, int len, int nonblock, + unsigned flags) +{ + if (flags != 0) + return(-EINVAL); + return(unix_proto_read(sock, (char *) buff, len, nonblock)); +} + +/* + * Given an address and an inode go find a unix control structure + */ + +static struct unix_proto_data * +unix_data_lookup(struct sockaddr_un *sockun, int sockaddr_len, + struct inode *inode) +{ + struct unix_proto_data *upd; + + for(upd = unix_datas; upd <= last_unix_data; ++upd) + { + if (upd->refcnt > 0 && upd->socket && + upd->socket->state == SS_UNCONNECTED && + upd->sockaddr_un.sun_family == sockun->sun_family && + upd->inode == inode) + + return(upd); + } + return(NULL); +} + +/* + * We allocate a page of data for the socket. This is woefully inadequate and helps cause vast + * amounts of excess task switching and blocking when transferring stuff like bitmaps via X. + * It doesn't help this problem that the Linux scheduler is desperately in need of a major + * rewrite. Somewhere near 16K would be better maybe 32. + */ + +static struct unix_proto_data * +unix_data_alloc(void) +{ + struct unix_proto_data *upd; + + cli(); + for(upd = unix_datas; upd <= last_unix_data; ++upd) + { + if (!upd->refcnt) + { + upd->refcnt = -1; /* unix domain socket not yet initialised - bgm */ + sti(); + upd->socket = NULL; + upd->sockaddr_len = 0; + upd->sockaddr_un.sun_family = 0; + upd->buf = NULL; + upd->bp_head = upd->bp_tail = 0; + upd->inode = NULL; + upd->peerupd = NULL; + return(upd); + } + } + sti(); + return(NULL); +} + +/* + * The data area is owned by all its users. Thus we need to track owners + * carefully and not free data at the wrong moment. These look like they need + * interrupt protection but they don't because no interrupt ever fiddles with + * these counts. With an SMP Linux you'll need to protect these! + */ + +static inline void unix_data_ref(struct unix_proto_data *upd) +{ + if (!upd) + { + return; + } + ++upd->refcnt; +} + + +static void unix_data_deref(struct unix_proto_data *upd) +{ + if (!upd) + { + return; + } + if (upd->refcnt == 1) + { + if (upd->buf) + { + free_page((unsigned long)upd->buf); + upd->buf = NULL; + upd->bp_head = upd->bp_tail = 0; + } + } + --upd->refcnt; +} + + +/* + * Upon a create, we allocate an empty protocol data, + * and grab a page to buffer writes. + */ + +static int unix_proto_create(struct socket *sock, int protocol) +{ + struct unix_proto_data *upd; + + /* + * No funny SOCK_RAW stuff + */ + + if (protocol != 0) + { + return(-EINVAL); + } + + if (!(upd = unix_data_alloc())) + { + printk("UNIX: create: can't allocate buffer\n"); + return(-ENOMEM); + } + if (!(upd->buf = (char*) get_free_page(GFP_USER))) + { + printk("UNIX: create: can't get page!\n"); + unix_data_deref(upd); + return(-ENOMEM); + } + upd->protocol = protocol; + upd->socket = sock; + UN_DATA(sock) = upd; + upd->refcnt = 1; /* Now its complete - bgm */ + return(0); +} + +/* + * Duplicate a socket. + */ + +static int unix_proto_dup(struct socket *newsock, struct socket *oldsock) +{ + struct unix_proto_data *upd = UN_DATA(oldsock); + return(unix_proto_create(newsock, upd->protocol)); +} + + +/* + * Release a Unix domain socket. + */ + +static int unix_proto_release(struct socket *sock, struct socket *peer) +{ + struct unix_proto_data *upd = UN_DATA(sock); + + if (!upd) + return(0); + + if (upd->socket != sock) + { + printk("UNIX: release: socket link mismatch!\n"); + return(-EINVAL); + } + + if (upd->inode) + { + iput(upd->inode); + upd->inode = NULL; + } + + UN_DATA(sock) = NULL; + upd->socket = NULL; + + if (upd->peerupd) + unix_data_deref(upd->peerupd); + unix_data_deref(upd); + return(0); +} + + +/* + * Bind a name to a socket. + * This is where much of the work is done: we allocate a fresh page for + * the buffer, grab the appropriate inode and set things up. + * + * FIXME: what should we do if an address is already bound? + * Here we return EINVAL, but it may be necessary to re-bind. + * I think thats what BSD does in the case of datagram sockets... + */ + +static int unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr, + int sockaddr_len) +{ + char fname[UNIX_PATH_MAX + 1]; + struct unix_proto_data *upd = UN_DATA(sock); + unsigned long old_fs; + int i; + + if (sockaddr_len <= UN_PATH_OFFSET || + sockaddr_len > sizeof(struct sockaddr_un)) + { + return(-EINVAL); + } + if (upd->sockaddr_len || upd->inode) + { + /*printk("UNIX: bind: already bound!\n");*/ + return(-EINVAL); + } + memcpy(&upd->sockaddr_un, umyaddr, sockaddr_len); + upd->sockaddr_un.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0'; + if (upd->sockaddr_un.sun_family != AF_UNIX) + { + return(-EINVAL); + } + + memcpy(fname, upd->sockaddr_un.sun_path, sockaddr_len-UN_PATH_OFFSET); + fname[sockaddr_len-UN_PATH_OFFSET] = '\0'; + old_fs = get_fs(); + set_fs(get_ds()); + + i = do_mknod(fname, S_IFSOCK | S_IRWXUGO, 0); + + if (i == 0) + i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL); + set_fs(old_fs); + if (i < 0) + { +/* printk("UNIX: bind: can't open socket %s\n", fname);*/ + if(i==-EEXIST) + i=-EADDRINUSE; + return(i); + } + upd->sockaddr_len = sockaddr_len; /* now its legal */ + + return(0); +} + + +/* + * Perform a connection. we can only connect to unix sockets + * (I can't for the life of me find an application where that + * wouldn't be the case!) + */ + +static int unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr, + int sockaddr_len, int flags) +{ + char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1]; + struct sockaddr_un sockun; + struct unix_proto_data *serv_upd; + struct inode *inode; + unsigned long old_fs; + int i; + + if (sockaddr_len <= UN_PATH_OFFSET || + sockaddr_len > sizeof(struct sockaddr_un)) + { + return(-EINVAL); + } + + if (sock->state == SS_CONNECTING) + return(-EINPROGRESS); + if (sock->state == SS_CONNECTED) + return(-EISCONN); + + memcpy(&sockun, uservaddr, sockaddr_len); + sockun.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0'; + if (sockun.sun_family != AF_UNIX) + { + return(-EINVAL); + } + +/* + * Try to open the name in the filesystem - this is how we + * identify ourselves and our server. Note that we don't + * hold onto the inode that long, just enough to find our + * server. When we're connected, we mooch off the server. + */ + + memcpy(fname, sockun.sun_path, sockaddr_len-UN_PATH_OFFSET); + fname[sockaddr_len-UN_PATH_OFFSET] = '\0'; + old_fs = get_fs(); + set_fs(get_ds()); + i = open_namei(fname, 0, S_IFSOCK, &inode, NULL); + set_fs(old_fs); + if (i < 0) + { + return(i); + } + + serv_upd = unix_data_lookup(&sockun, sockaddr_len, inode); + iput(inode); + if (!serv_upd) + { + return(-EINVAL); + } + + if ((i = sock_awaitconn(sock, serv_upd->socket)) < 0) + { + return(i); + } + + if (sock->conn) + { + unix_data_ref(UN_DATA(sock->conn)); + UN_DATA(sock)->peerupd = UN_DATA(sock->conn); /* ref server */ + } + return(0); +} + + +/* + * To do a socketpair, we just connect the two datas, easy! + * Since we always wait on the socket inode, they're no contention + * for a wait area, and deadlock prevention in the case of a process + * writing to itself is, ignored, in true unix fashion! + */ + +static int unix_proto_socketpair(struct socket *sock1, struct socket *sock2) +{ + struct unix_proto_data *upd1 = UN_DATA(sock1), *upd2 = UN_DATA(sock2); + + unix_data_ref(upd1); + unix_data_ref(upd2); + upd1->peerupd = upd2; + upd2->peerupd = upd1; + return(0); +} + + +/* + * On accept, we ref the peer's data for safe writes. + */ + +static int unix_proto_accept(struct socket *sock, struct socket *newsock, int flags) +{ + struct socket *clientsock; + +/* + * If there aren't any sockets awaiting connection, + * then wait for one, unless nonblocking. + */ + + while(!(clientsock = sock->iconn)) + { + if (flags & O_NONBLOCK) + return(-EAGAIN); + interruptible_sleep_on(sock->wait); + if (current->signal & ~current->blocked) + { + return(-ERESTARTSYS); + } + } + +/* + * Great. Finish the connection relative to server and client, + * wake up the client and return the new fd to the server. + */ + + sock->iconn = clientsock->next; + clientsock->next = NULL; + newsock->conn = clientsock; + clientsock->conn = newsock; + clientsock->state = SS_CONNECTED; + newsock->state = SS_CONNECTED; + unix_data_ref(UN_DATA(clientsock)); + UN_DATA(newsock)->peerupd = UN_DATA(clientsock); + UN_DATA(newsock)->sockaddr_un = UN_DATA(sock)->sockaddr_un; + UN_DATA(newsock)->sockaddr_len = UN_DATA(sock)->sockaddr_len; + wake_up_interruptible(clientsock->wait); + return(0); +} + + +/* + * Gets the current name or the name of the connected socket. + */ + +static int unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr, + int *usockaddr_len, int peer) +{ + struct unix_proto_data *upd; + int len; + + if (peer) + { + if (sock->state != SS_CONNECTED) + { + return(-EINVAL); + } + upd = UN_DATA(sock->conn); + } + else + upd = UN_DATA(sock); + + len = upd->sockaddr_len; + memcpy(usockaddr, &upd->sockaddr_un, len); + *usockaddr_len=len; + return(0); +} + + +/* + * We read from our own buf. + */ + +static int unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock) +{ + struct unix_proto_data *upd; + int todo, avail; + + if ((todo = size) <= 0) + return(0); + + upd = UN_DATA(sock); + while(!(avail = UN_BUF_AVAIL(upd))) + { + if (sock->state != SS_CONNECTED) + { + return((sock->state == SS_DISCONNECTING) ? 0 : -EINVAL); + } + if (nonblock) + return(-EAGAIN); + interruptible_sleep_on(sock->wait); + if (current->signal & ~current->blocked) + { + return(-ERESTARTSYS); + } + } + +/* + * Copy from the read buffer into the user's buffer, + * watching for wraparound. Then we wake up the writer. + */ + + unix_lock(upd); + do + { + int part, cando; + + if (avail <= 0) + { + printk("UNIX: read: AVAIL IS NEGATIVE!!!\n"); + send_sig(SIGKILL, current, 1); + return(-EPIPE); + } + + if ((cando = todo) > avail) + cando = avail; + if (cando >(part = BUF_SIZE - upd->bp_tail)) + cando = part; + memcpy_tofs(ubuf, upd->buf + upd->bp_tail, cando); + upd->bp_tail =(upd->bp_tail + cando) &(BUF_SIZE-1); + ubuf += cando; + todo -= cando; + if (sock->state == SS_CONNECTED) + wake_up_interruptible(sock->conn->wait); + avail = UN_BUF_AVAIL(upd); + } + while(todo && avail); + unix_unlock(upd); + return(size - todo); +} + + +/* + * We write to our peer's buf. When we connected we ref'd this + * peer so we are safe that the buffer remains, even after the + * peer has disconnected, which we check other ways. + */ + +static int unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock) +{ + struct unix_proto_data *pupd; + int todo, space; + + if ((todo = size) <= 0) + return(0); + if (sock->state != SS_CONNECTED) + { + if (sock->state == SS_DISCONNECTING) + { + send_sig(SIGPIPE, current, 1); + return(-EPIPE); + } + return(-EINVAL); + } + pupd = UN_DATA(sock)->peerupd; /* safer than sock->conn */ + + while(!(space = UN_BUF_SPACE(pupd))) + { + if (nonblock) + return(-EAGAIN); + interruptible_sleep_on(sock->wait); + if (current->signal & ~current->blocked) + { + return(-ERESTARTSYS); + } + if (sock->state == SS_DISCONNECTING) + { + send_sig(SIGPIPE, current, 1); + return(-EPIPE); + } + } + +/* + * Copy from the user's buffer to the write buffer, + * watching for wraparound. Then we wake up the reader. + */ + + unix_lock(pupd); + + do + { + int part, cando; + + if (space <= 0) + { + printk("UNIX: write: SPACE IS NEGATIVE!!!\n"); + send_sig(SIGKILL, current, 1); + return(-EPIPE); + } + + /* + * We may become disconnected inside this loop, so watch + * for it (peerupd is safe until we close). + */ + + if (sock->state == SS_DISCONNECTING) + { + send_sig(SIGPIPE, current, 1); + unix_unlock(pupd); + return(-EPIPE); + } + + if ((cando = todo) > space) + cando = space; + + if (cando >(part = BUF_SIZE - pupd->bp_head)) + cando = part; + + memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, cando); + pupd->bp_head =(pupd->bp_head + cando) &(BUF_SIZE-1); + ubuf += cando; + todo -= cando; + if (sock->state == SS_CONNECTED) + wake_up_interruptible(sock->conn->wait); + space = UN_BUF_SPACE(pupd); + } + while(todo && space); + + unix_unlock(pupd); + return(size - todo); +} + +/* + * Select on a unix domain socket. + */ + +static int unix_proto_select(struct socket *sock, int sel_type, select_table * wait) +{ + struct unix_proto_data *upd, *peerupd; + + /* + * Handle server sockets specially. + */ + if (sock->flags & SO_ACCEPTCON) + { + if (sel_type == SEL_IN) + { + if (sock->iconn) + return(1); + select_wait(sock->wait, wait); + return(sock->iconn ? 1 : 0); + } + select_wait(sock->wait, wait); + return(0); + } + + if (sel_type == SEL_IN) + { + upd = UN_DATA(sock); + if (UN_BUF_AVAIL(upd)) /* even if disconnected */ + return(1); + else if (sock->state != SS_CONNECTED) + { + return(1); + } + select_wait(sock->wait,wait); + return(0); + } + + if (sel_type == SEL_OUT) + { + if (sock->state != SS_CONNECTED) + { + return(1); + } + peerupd = UN_DATA(sock->conn); + if (UN_BUF_SPACE(peerupd) > 0) + return(1); + select_wait(sock->wait,wait); + return(0); + } + + /* + * Exceptions - SEL_EX + */ + + return(0); +} + + +/* + * ioctl() calls sent to an AF_UNIX socket + */ + +static int unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct unix_proto_data *upd, *peerupd; + int er; + + upd = UN_DATA(sock); + peerupd = (sock->state == SS_CONNECTED) ? UN_DATA(sock->conn) : NULL; + + switch(cmd) + { + case TIOCINQ: + if (sock->flags & SO_ACCEPTCON) + return(-EINVAL); + er=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long)); + if(er) + return er; + if (UN_BUF_AVAIL(upd) || peerupd) + put_fs_long(UN_BUF_AVAIL(upd),(unsigned long *)arg); + else + put_fs_long(0,(unsigned long *)arg); + break; + case TIOCOUTQ: + if (sock->flags & SO_ACCEPTCON) + return(-EINVAL); + er=verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long)); + if(er) + return er; + if (peerupd) + put_fs_long(UN_BUF_SPACE(peerupd),(unsigned long *)arg); + else + put_fs_long(0,(unsigned long *)arg); + break; + default: + return(-EINVAL); + } + return(0); +} + + +static struct proto_ops unix_proto_ops = { + AF_UNIX, + unix_proto_create, + unix_proto_dup, + unix_proto_release, + unix_proto_bind, + unix_proto_connect, + unix_proto_socketpair, + unix_proto_accept, + unix_proto_getname, + unix_proto_read, + unix_proto_write, + unix_proto_select, + unix_proto_ioctl, + unix_proto_listen, + unix_proto_send, + unix_proto_recv, + unix_proto_sendto, + unix_proto_recvfrom, + unix_proto_shutdown, + unix_proto_setsockopt, + unix_proto_getsockopt, + NULL /* unix_proto_fcntl */ +}; + +/* + * Initialise the Unix domain protocol. + */ + +void unix_proto_init(struct net_proto *pro) +{ + struct unix_proto_data *upd; + + /* + * Tell SOCKET that we are alive... + */ + + (void) sock_register(unix_proto_ops.family, &unix_proto_ops); + + for(upd = unix_datas; upd <= last_unix_data; ++upd) + { + upd->refcnt = 0; + } +} diff --git a/net/unix/unix.h b/net/unix/unix.h new file mode 100644 index 000000000..4d9ee251a --- /dev/null +++ b/net/unix/unix.h @@ -0,0 +1,69 @@ +/* + * UNIX An implementation of the AF_UNIX network domain for the + * LINUX operating system. UNIX is implemented using the + * BSD Socket interface as the means of communication with + * the user level. + * + * This file describes some things of the UNIX protocol family + * module. It is mainly used for the "proc" sub-module now, + * but it may be useful for cleaning up the UNIX module as a + * whole later. + * + * Version: @(#)unix.h 1.0.3 05/25/93 + * + * Authors: Orest Zborowski, <obz@Kodak.COM> + * Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * + * Fixes: + * Dmitry Gorodchanin - proc locking + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ + + +#ifdef _LINUX_UN_H + + +struct unix_proto_data { + int refcnt; /* cnt of reference 0=free */ + /* -1=not initialised -bgm */ + struct socket *socket; /* socket we're bound to */ + int protocol; + struct sockaddr_un sockaddr_un; + short sockaddr_len; /* >0 if name bound */ + char *buf; + int bp_head, bp_tail; + struct inode *inode; + struct unix_proto_data *peerupd; + struct wait_queue *wait; /* Lock across page faults (FvK) */ + int lock_flag; +}; + +extern struct unix_proto_data unix_datas[NSOCKETS]; + + +#define last_unix_data (unix_datas + NSOCKETS - 1) + + +#define UN_DATA(SOCK) ((struct unix_proto_data *)(SOCK)->data) +#define UN_PATH_OFFSET ((unsigned long)((struct sockaddr_un *)0) \ + ->sun_path) + +/* + * Buffer size must be power of 2. buffer mgmt inspired by pipe code. + * note that buffer contents can wraparound, and we can write one byte less + * than full size to discern full vs empty. + */ +#define BUF_SIZE PAGE_SIZE +#define UN_BUF_AVAIL(UPD) (((UPD)->bp_head - (UPD)->bp_tail) & \ + (BUF_SIZE-1)) +#define UN_BUF_SPACE(UPD) ((BUF_SIZE-1) - UN_BUF_AVAIL(UPD)) + +#endif /* _LINUX_UN_H */ + + +extern void unix_proto_init(struct net_proto *pro); |