patch-1.3.16 linux/net/unix/af_unix.c

Next file: linux/net/unix/proc.c
Previous file: linux/net/unix/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.15/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
@@ -0,0 +1,1016 @@
+/*
+ * NET3:	Implementation of BSD Unix domain sockets.
+ *
+ * Authors:	Alan Cox, <alan@cymru.net>
+ *
+ *		Currently this contains all but the file descriptor passing code.
+ *		Before that goes in the odd bugs in the iovec handlers need 
+ *		fixing, and this bit testing. BSD fd passing is a trivial part
+ *		of the exercise.
+ *
+ *		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/socket.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <asm/segment.h>
+#include <linux/skbuff.h>
+/*#include <linux/netprotocol.h>*/
+#include <linux/netdevice.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+#include <net/af_unix.h>
+
+static unix_socket *volatile unix_socket_list=NULL;
+
+#define min(a,b)	(((a)<(b))?(a):(b))
+
+/*
+ * Make sure the unix name is null-terminated.
+ */
+static inline void unix_mkname(struct sockaddr_un * sun, unsigned long len)
+{
+	if (len >= sizeof(*sun))
+		len = sizeof(*sun)-1;
+	((char *)sun)[len]=0;
+}
+
+/*
+ *	Note: Sockets may not be removed _during_ an interrupt or net_bh
+ *	handler using this technique. They can be added although we do not
+ *	use this facility.
+ */
+ 
+static void unix_remove_socket(unix_socket *sk)
+{
+	unix_socket *s;
+	
+	cli();
+	s=unix_socket_list;
+	if(s==sk)
+	{
+		unix_socket_list=s->next;
+		sti();
+		return;
+	}
+	while(s && s->next)
+	{
+		if(s->next==sk)
+		{
+			s->next=sk->next;
+			sti();
+			return;
+		}
+		s=s->next;
+	}
+	sti();
+}
+
+static void unix_insert_socket(unix_socket *sk)
+{
+	cli();
+	sk->next=unix_socket_list;
+	unix_socket_list=sk;
+	sti();
+}
+
+static unix_socket *unix_find_socket(struct inode *i)
+{
+	unix_socket *s;
+	cli();
+	s=unix_socket_list;
+	while(s)
+	{
+		if(s->protinfo.af_unix.inode==i)
+		{
+			sti();
+			return(s);
+		}
+		s=s->next;
+	}
+	sti();
+	return(NULL);
+}
+
+/*
+ *	Delete a unix socket. We have to allow for deferring this on a timer.
+ */
+
+static void unix_destroy_timer(unsigned long data)
+{
+	unix_socket *sk=(unix_socket *)data;
+	if(sk->protinfo.af_unix.locks==0 && sk->wmem_alloc==0)
+	{
+		if(sk->protinfo.af_unix.name)
+			kfree(sk->protinfo.af_unix.name);
+		kfree_s(sk,sizeof(*sk));
+		return;
+	}
+	
+	/*
+	 *	Retry;
+	 */
+	 
+	init_timer(&sk->timer);
+	sk->timer.expires=jiffies+10*HZ;	/* No real hurry try it every 10 seconds or so */
+	add_timer(&sk->timer);
+}
+	 
+	 
+static void unix_delayed_delete(unix_socket *sk)
+{
+	init_timer(&sk->timer);
+	sk->timer.data=(unsigned long)sk;
+	sk->timer.expires=jiffies+HZ;		/* Normally 1 second after will clean up. After that we try every 10 */
+	sk->timer.function=unix_destroy_timer;
+	add_timer(&sk->timer);
+}
+	
+static void unix_destroy_socket(unix_socket *sk)
+{
+	struct sk_buff *skb;
+	unix_remove_socket(sk);
+	
+	while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
+	{
+		if(sk->state==TCP_LISTEN)
+		{
+			unix_socket *osk=skb->sk;
+			osk->state=TCP_CLOSE;
+			kfree_skb(skb, FREE_WRITE);	/* Now surplus - free the skb first before the socket */
+			osk->state_change(osk);		/* So the connect wakes and cleans up (if any) */
+			/* osk will be destroyed when it gets to close or the timer fires */			
+		}
+		else
+		{
+/*			unix_kill_credentials(skb);	*//* Throw out any passed fd's */
+			kfree_skb(skb,FREE_WRITE);
+		}
+	}
+	
+	if(sk->protinfo.af_unix.inode!=NULL)
+	{
+		iput(sk->protinfo.af_unix.inode);
+		sk->protinfo.af_unix.inode=NULL;
+	}
+	
+	if(--sk->protinfo.af_unix.locks==0 && sk->wmem_alloc==0)
+	{
+		if(sk->protinfo.af_unix.name)
+			kfree(sk->protinfo.af_unix.name);
+		kfree_s(sk,sizeof(*sk));
+	}
+	else
+	{
+		sk->dead=1;
+		unix_delayed_delete(sk);	/* Try every so often until buffers are all freed */
+	}
+}
+
+/*
+ *	Fixme: We need async I/O on AF_UNIX doing next.
+ */
+ 
+static int unix_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	return -EINVAL;
+}
+
+/*
+ *	Yes socket options work with the new unix domain socketry!!!!!!!
+ */
+ 
+static int unix_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+{
+	unix_socket *sk=sock->data;
+	if(level!=SOL_SOCKET)
+		return -EOPNOTSUPP;
+	return sock_setsockopt(sk,level,optname,optval,optlen);	
+}
+
+static int unix_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
+{
+	unix_socket *sk=sock->data;
+	if(level!=SOL_SOCKET)
+		return -EOPNOTSUPP;
+	return sock_getsockopt(sk,level,optname,optval,optlen);
+}
+
+static int unix_listen(struct socket *sock, int backlog)
+{
+	unix_socket *sk=sock->data;
+	if(sk->type!=SOCK_STREAM)
+		return -EOPNOTSUPP;		/* Only stream sockets accept */
+	sk->max_ack_backlog=backlog;
+	sk->state=TCP_LISTEN;
+	return 0;
+}
+
+static void def_callback1(struct sock *sk)
+{
+	if(!sk->dead)
+		wake_up_interruptible(sk->sleep);
+}
+
+static void def_callback2(struct sock *sk, int len)
+{
+	if(!sk->dead)
+		wake_up_interruptible(sk->sleep);
+}
+
+static int unix_create(struct socket *sock, int protocol)
+{
+	unix_socket *sk;
+/*	printk("Unix create\n");*/
+	if(protocol)
+		return -EPROTONOSUPPORT;
+	sk=(unix_socket *)kmalloc(sizeof(*sk),GFP_KERNEL);
+	if(sk==NULL)
+		return -ENOMEM;
+	sk->type=sock->type;
+	switch(sock->type)
+	{
+		case SOCK_STREAM:
+			break;
+		case SOCK_DGRAM:
+			break;
+		default:
+			kfree_s(sk,sizeof(*sk));
+			return -ESOCKTNOSUPPORT;
+	}
+	skb_queue_head_init(&sk->write_queue);
+	skb_queue_head_init(&sk->receive_queue);
+	skb_queue_head_init(&sk->back_log);
+	sk->protinfo.af_unix.family=AF_UNIX;
+	sk->protinfo.af_unix.inode=NULL;
+	sk->protinfo.af_unix.locks=1;	/* Us */
+	sk->protinfo.af_unix.readsem=MUTEX;	/* single task reading lock */
+	sk->protinfo.af_unix.name=NULL;
+	sk->protinfo.af_unix.other=NULL;
+	sk->protocol=0;
+	sk->rmem_alloc=0;
+	sk->wmem_alloc=0;
+	sk->dead=0;
+	sk->next=NULL;
+	sk->broadcast=0;
+	sk->rcvbuf=SK_RMEM_MAX;
+	sk->sndbuf=SK_WMEM_MAX;
+	sk->inuse=0;
+	sk->debug=0;
+	sk->prot=NULL;
+	sk->err=0;
+	sk->localroute=0;
+	sk->send_head=NULL;
+	sk->state=TCP_CLOSE;
+	sk->priority=SOPRI_NORMAL;
+	sk->ack_backlog=0;
+	sk->shutdown=0;
+	sk->state_change=def_callback1;
+	sk->data_ready=def_callback2;
+	sk->write_space=def_callback1;
+	sk->error_report=def_callback1;
+	sk->mtu=4096;
+	sk->socket=sock;
+	sock->data=(void *)sk;
+	sk->sleep=sock->wait;
+	sk->zapped=0;
+	unix_insert_socket(sk);
+	return 0;
+}
+
+static int unix_dup(struct socket *newsock, struct socket *oldsock)
+{
+	return unix_create(newsock,0);
+}
+
+static int unix_release(struct socket *sock, struct socket *peer)
+{
+	unix_socket *sk=sock->data;
+	unix_socket *skpair;
+	
+	/* May not have data attached */
+	
+	if(sk==NULL)
+		return 0;
+		
+	sk->state_change(sk);
+	sk->dead=1;
+	skpair=(unix_socket *)sk->protinfo.af_unix.other;	/* Person we send to (default) */
+	if(sk->type==SOCK_STREAM && skpair!=NULL && skpair->state!=TCP_LISTEN)
+	{
+		skpair->shutdown=SHUTDOWN_MASK;		/* No more writes */
+		skpair->state_change(skpair);		/* Wake any blocked writes */
+	}
+	if(skpair!=NULL)
+		skpair->protinfo.af_unix.locks--;		/* It may now die */
+	sk->protinfo.af_unix.other=NULL;			/* No pair */
+	unix_destroy_socket(sk);			/* Try and flush out this socket. Throw our buffers at least */
+	return 0;
+}
+
+
+static unix_socket *unix_find_other(char *path, int *error)
+{
+	int old_fs;
+	int err;
+	struct inode *inode;
+	unix_socket *u;
+	
+	old_fs=get_fs();
+	set_fs(get_ds());
+	err = open_namei(path, 2, S_IFSOCK, &inode, NULL);
+	set_fs(old_fs);
+	if(err<0)
+	{
+		*error=err;
+		return NULL;
+	}
+	u=unix_find_socket(inode);
+	iput(inode);
+	if(u==NULL)
+	{
+		*error=-ECONNREFUSED;
+		return NULL;
+	}
+	return u;
+}
+
+
+static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+	struct sockaddr_un *sun=(struct sockaddr_un *)uaddr;
+	unix_socket *sk=sock->data;
+	int old_fs;
+	int err;
+	
+	if(addr_len>sizeof(struct sockaddr_un) || addr_len<3 || sun->sun_family!=AF_UNIX)
+		return -EINVAL;
+	unix_mkname(sun, addr_len);
+	/*
+	 *	Put ourselves in the filesystem
+	 */
+	if(sk->protinfo.af_unix.inode!=NULL)
+		return -EINVAL;
+	
+	sk->protinfo.af_unix.name=kmalloc(addr_len+1, GFP_KERNEL);
+	if(sk->protinfo.af_unix.name==NULL)
+		return -ENOMEM;
+	memcpy(sk->protinfo.af_unix.name, sun->sun_path, addr_len+1);
+	
+	old_fs=get_fs();
+	set_fs(get_ds());
+	
+	err=do_mknod(sk->protinfo.af_unix.name,S_IFSOCK|S_IRWXUGO,0);
+	if(err==0)
+		err=open_namei(sk->protinfo.af_unix.name, 2, S_IFSOCK, &sk->protinfo.af_unix.inode, NULL);
+	
+	set_fs(old_fs);
+	
+	if(err<0)
+	{
+		kfree_s(sk->protinfo.af_unix.name,addr_len+1);
+		sk->protinfo.af_unix.name=NULL;
+		if(err==-EEXIST)
+			return -EADDRINUSE;
+		else
+			return err;
+	}
+	
+	return 0;
+	
+}
+
+static int unix_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+{
+	unix_socket *sk=sock->data;
+	struct sockaddr_un *sun=(struct sockaddr_un *)uaddr;
+	unix_socket *other;
+	struct sk_buff *skb;
+	int err;
+
+	unix_mkname(sun, addr_len);
+	if(sk->type==SOCK_STREAM && sk->protinfo.af_unix.other)
+	{
+		if(sock->state==SS_CONNECTING && sk->state==TCP_ESTABLISHED)
+		{
+			sock->state=SS_CONNECTED;
+			return 0;
+		}
+		if(sock->state==SS_CONNECTING && sk->state == TCP_CLOSE)
+		{
+			sock->state=SS_UNCONNECTED;
+			return -ECONNREFUSED;
+		}
+		if(sock->state==SS_CONNECTING)
+			return -EALREADY;
+		return -EISCONN;
+	}
+	
+	if(sun->sun_family!=AF_UNIX)
+		return -EINVAL;
+
+	if(sk->type==SOCK_DGRAM && sk->protinfo.af_unix.other)
+	{
+		sk->protinfo.af_unix.other->protinfo.af_unix.locks--;
+		sk->protinfo.af_unix.other=NULL;
+		sock->state=SS_UNCONNECTED;
+	}
+
+	if(sock->type==SOCK_DGRAM)
+	{
+		sock->state=SS_CONNECTED;
+		sk->state=TCP_ESTABLISHED;
+		return 0;			/* Done */
+	}
+	
+
+	if(sock->state==SS_UNCONNECTED)
+	{
+		/*
+		 *	Now ready to connect
+		 */
+	 
+		skb=sock_alloc_send_skb(sk, 0, 0, &err); /* Marker object */
+		if(skb==NULL)
+			return err;
+		skb->sk=sk;				/* So they know it is us */
+		skb->free=1;
+		sk->state=TCP_CLOSE;
+		unix_mkname(sun, addr_len);
+		other=unix_find_other(sun->sun_path, &err);
+		if(other==NULL)
+		{
+			kfree_skb(skb, FREE_WRITE);
+			return err;
+		}
+		other->protinfo.af_unix.locks++;		/* Lock the other socket so it doesn't run off for a moment */
+		other->ack_backlog++;
+		sk->protinfo.af_unix.other=other;
+		skb_queue_tail(&other->receive_queue,skb);
+		sk->state=TCP_SYN_SENT;
+		sock->state=SS_CONNECTING;
+		sti();
+		other->data_ready(other,0);		/* Wake up ! */		
+	}
+			
+	
+	/* Wait for an accept */
+	
+	cli();
+	while(sk->state==TCP_SYN_SENT)
+	{
+		if(flags&O_NONBLOCK)
+		{
+			sti();
+			return -EINPROGRESS;
+		}
+		interruptible_sleep_on(sk->sleep);
+		if(current->signal & ~current->blocked)
+		{
+			sti();
+			return -ERESTARTSYS;
+		}
+	}
+	
+	/*
+	 *	Has the other end closed on us ?
+	 */
+	 
+	if(sk->state==TCP_CLOSE)
+	{
+		sk->protinfo.af_unix.other->protinfo.af_unix.locks--;
+		sk->protinfo.af_unix.other=NULL;
+		sock->state=SS_UNCONNECTED;
+		return -ECONNREFUSED;
+	}
+	
+	/*
+	 *	Amazingly it has worked
+	 */
+	 
+	sock->state=SS_CONNECTED;
+	return 0;
+	
+}
+
+static int unix_socketpair(struct socket *a, struct socket *b)
+{
+	int err;
+	unix_socket *ska,*skb;	
+	
+	err=unix_create(a, 0);
+	if(err)
+		return err;
+	err=unix_create(b, 0);
+	if(err)
+	{
+		unix_release(a, NULL);
+		a->data=NULL;
+		return err;
+	}
+
+	ska=a->data;
+	skb=b->data;
+
+	/* Join our sockets back to back */
+	ska->protinfo.af_unix.locks++;
+	skb->protinfo.af_unix.locks++;
+	ska->protinfo.af_unix.other=skb;
+	skb->protinfo.af_unix.other=ska;
+	ska->state=TCP_ESTABLISHED;
+	skb->state=TCP_ESTABLISHED;
+	return 0;
+}
+
+static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	unix_socket *sk=sock->data;
+	unix_socket *newsk, *tsk;
+	struct sk_buff *skb;
+	
+	if(sk->type!=SOCK_STREAM)
+	{
+		return -EOPNOTSUPP;
+	}
+	if(sk->state!=TCP_LISTEN)
+	{
+		return -EINVAL;
+	}
+		
+	newsk=newsock->data;
+	if(sk->protinfo.af_unix.name!=NULL)
+	{
+		newsk->protinfo.af_unix.name=kmalloc(strlen(sk->protinfo.af_unix.name)+1, GFP_KERNEL);
+		if(newsk->protinfo.af_unix.name==NULL)
+			return -ENOMEM;
+		strcpy(newsk->protinfo.af_unix.name, sk->protinfo.af_unix.name);
+	}
+		
+	do
+	{
+		cli();
+		skb=skb_dequeue(&sk->receive_queue);
+		if(skb==NULL)
+		{
+			if(flags&O_NONBLOCK)
+			{
+				sti();
+				return -EAGAIN;
+			}
+			interruptible_sleep_on(sk->sleep);
+			if(current->signal & ~current->blocked)
+			{
+				sti();
+				return -ERESTARTSYS;
+			}
+			sti();
+		}
+	}
+	while(skb==NULL);
+	tsk=skb->sk;
+	kfree_skb(skb, FREE_WRITE);	/* The buffer is just used as a tag */
+	sk->ack_backlog--;
+	newsk->protinfo.af_unix.other=tsk;
+	tsk->protinfo.af_unix.other=newsk;
+	tsk->state=TCP_ESTABLISHED;
+	newsk->state=TCP_ESTABLISHED;
+	newsk->protinfo.af_unix.locks++;	/* Swap lock over */
+	sk->protinfo.af_unix.locks--;	/* Locked to child socket not master */
+	tsk->protinfo.af_unix.locks++;	/* Back lock */
+	sti();
+	tsk->state_change(tsk);		/* Wake up any sleeping connect */
+	return 0;
+}
+
+static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
+{
+	unix_socket *sk=sock->data;
+	struct sockaddr_un *sun=(struct sockaddr_un *)uaddr;
+	
+	if(peer)
+	{
+		if(sk->protinfo.af_unix.other==NULL)
+			return -ENOTCONN;
+		sk=sk->protinfo.af_unix.other;
+	}
+	sun->sun_family=AF_UNIX;
+	if(sk->protinfo.af_unix.name==NULL)
+	{
+		*sun->sun_path=0;
+		*uaddr_len=3;
+		return 0;		/* Not bound */
+	}
+	*uaddr_len=sizeof(short)+strlen(sk->protinfo.af_unix.name)+1;
+	strcpy(sun->sun_path,sk->protinfo.af_unix.name);		/* 108 byte limited */
+	return 0;
+}
+
+static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
+{
+	unix_socket *sk=sock->data;
+	unix_socket *other;
+	struct sockaddr_un *sun=msg->msg_name;
+	int err,size;
+	struct sk_buff *skb;
+
+	if(sk->err)
+	{
+		cli();
+		err=sk->err;
+		sk->err=0;
+		sti();
+		return -err;
+	}
+	
+	if(flags || msg->msg_accrights)	/* For now */
+		return -EINVAL;
+		
+	if(sun!=NULL)
+	{
+		if(sock->type==SOCK_STREAM)
+		{
+			if(sk->state==TCP_ESTABLISHED)
+				return -EISCONN;
+			else
+				return -EOPNOTSUPP;
+		}
+	}
+	if(sun==NULL)
+	{
+		if(sk->protinfo.af_unix.other==NULL)
+			return -EINVAL;
+	}
+
+	/*
+	 *	Optimisation for the fact that under 0.01% of X messages typically
+	 *	need breaking up.
+	 */
+
+	if(len>(sk->sndbuf-sizeof(struct sk_buff))/2)	/* Keep two messages in the pipe so it schedules better */
+	{
+		if(sock->type==SOCK_DGRAM)
+			return -EMSGSIZE;
+		len=(sk->sndbuf-sizeof(struct sk_buff))/2;
+	}
+	 
+	size=/*protocol_size(&proto_unix)+*/len;
+	skb=sock_alloc_send_skb(sk,size,nonblock, &err);
+	if(skb==NULL)
+		return err;
+/*	protocol_adjust(skb,&proto_unix);*/
+	skb->sk=sk;
+	skb->free=1;
+	memcpy_fromiovec(skb_put(skb,len),msg->msg_iov, len);
+
+	cli();
+	if(sun==NULL)
+	{
+		other=sk->protinfo.af_unix.other;
+	}
+	else
+	{
+		unix_mkname(sun, msg->msg_namelen);
+		other=unix_find_other(sun->sun_path, &err);
+		if(other==NULL)
+		{
+			kfree_skb(skb, FREE_WRITE);
+			return err;
+		}
+	}
+	skb_queue_tail(&other->receive_queue, skb);
+	sti();
+	other->data_ready(other,len);
+	return len;
+}
+		
+static int unix_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
+{
+	unix_socket *sk=sock->data;
+	struct sockaddr_un *sun=msg->msg_name;
+	int err;
+	struct sk_buff *skb;
+	int copied=0;
+	unsigned char *sp;
+	int len;
+	int num;
+	struct iovec *iov=msg->msg_iov;
+	int ct=msg->msg_iovlen;
+	
+	if(addr_len)
+		*addr_len=0;
+		
+	if(sk->err)
+	{
+		cli();
+		err=sk->err;
+		sk->err=0;
+		sti();
+		return -sk->err;
+	}
+	
+/*	printk("get rcv sem\n");*/
+	down(&sk->protinfo.af_unix.readsem);		/* Lock the socket */
+/*	printk("got rcv sem\n");*/
+
+	while(ct--)
+	{
+		int done=0;
+		sp=iov->iov_base;
+		len=iov->iov_len;
+		iov++;
+		
+		while(done<len)
+		{
+			if(copied & (flags&MSG_PEEK))
+			{
+				up(&sk->protinfo.af_unix.readsem);
+				return copied;
+			}
+			cli();
+			skb=skb_peek(&sk->receive_queue);
+			if(skb==NULL)
+			{
+				up(&sk->protinfo.af_unix.readsem);
+				if(sk->shutdown&RCV_SHUTDOWN)
+					return copied;
+				if(noblock)
+				{
+					if(copied)
+						return copied;
+					return -EAGAIN;
+				}
+				interruptible_sleep_on(sk->sleep);
+				if( current->signal & ~current->blocked)
+				{
+					sti();
+					if(copied)
+						return copied;
+					return -ERESTARTSYS;
+				}
+				sti();
+				down(&sk->protinfo.af_unix.readsem);
+				continue;
+			}
+			if(msg->msg_name!=NULL)
+			{
+				sun->sun_family=AF_UNIX;
+				if(skb->sk->protinfo.af_unix.name)
+				{
+					memcpy(sun->sun_path, skb->sk->protinfo.af_unix.name, 108);
+					if(addr_len)
+						*addr_len=strlen(sun->sun_path)+sizeof(short);
+				}
+				else
+					if(addr_len)
+						*addr_len=sizeof(short);
+			}
+			num=min(skb->len,size-copied);
+			copied+=num;
+			done+=num;
+			if(flags&MSG_PEEK)
+			{
+				memcpy_tofs(sp, skb->data, num);
+				break;
+			}
+			else
+			{
+				memcpy_tofs(sp, skb->data,num);
+				skb_pull(skb,num);
+				sp+=num;
+				if(skb->len==0)
+				{
+					skb_unlink(skb);
+					kfree_skb(skb, FREE_WRITE);
+					if(sock->type==SOCK_DGRAM)
+						break;
+				}
+			}
+		}	
+	}	
+	up(&sk->protinfo.af_unix.readsem);
+	return copied;
+}
+
+static int unix_shutdown(struct socket *sock, int mode)
+{
+	unix_socket *sk=(unix_socket *)sock->data;
+	unix_socket *other=sk->protinfo.af_unix.other;
+	if(mode&SEND_SHUTDOWN)
+	{
+		sk->shutdown|=SEND_SHUTDOWN;
+		sk->state_change(sk);
+		if(other)
+		{
+			other->shutdown|=RCV_SHUTDOWN;
+			other->state_change(other);
+		}
+	}
+	other=sk->protinfo.af_unix.other;
+	if(mode&RCV_SHUTDOWN)
+	{
+		sk->shutdown|=RCV_SHUTDOWN;
+		sk->state_change(sk);
+		if(other)
+		{
+			other->shutdown|=SEND_SHUTDOWN;
+			other->state_change(other);
+		}
+	}
+	return 0;
+}
+
+		
+static int unix_select(struct socket *sock,  int sel_type, select_table *wait)
+{
+	return datagram_select(sock->data,sel_type,wait);
+}
+
+static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	unix_socket *sk=sock->data;
+	int err;
+	long amount=0;
+			
+	switch(cmd)
+	{
+	
+		case TIOCOUTQ:
+			err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long));
+			if(err)
+				return err;
+			amount=sk->sndbuf-sk->wmem_alloc;
+			if(amount<0)
+				amount=0;
+			put_fs_long(amount,(unsigned long *)arg);
+			return 0;
+		case TIOCINQ:
+		{
+			struct sk_buff *skb;
+			if(sk->state==TCP_LISTEN)
+				return -EINVAL;
+			/* These two are safe on a single CPU system as only user tasks fiddle here */
+			if((skb=skb_peek(&sk->receive_queue))!=NULL)
+				amount=skb->len;
+			err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long));
+			put_fs_long(amount,(unsigned long *)arg);
+			return 0;
+		}
+
+		default:
+			return -EINVAL;
+	}
+	/*NOTREACHED*/
+	return(0);
+}
+
+/* Exported for 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;
+	unix_socket *s=unix_socket_list;
+	
+	len+= sprintf(buffer,"Num       RefCount Protocol Flags    Type St Path\n");
+	
+	while(s!=NULL)
+	{
+		len+=sprintf(buffer+len,"%p: %08X %08X %08lX %04X %02X",
+			s,
+			s->protinfo.af_unix.locks,
+			0,
+			s->socket->flags,
+			s->socket->type,
+			s->socket->state);
+		if(s->protinfo.af_unix.name!=NULL)
+			len+=sprintf(buffer+len, " %s\n", s->protinfo.af_unix.name);
+		else
+			buffer[len++]='\n';
+		
+		pos=begin+len;
+		if(pos<offset)
+		{
+			len=0;
+			begin=pos;
+		}
+		if(pos>offset+length)
+			break;
+		s=s->next;
+	}
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;
+	return len;
+}
+
+/*
+ *	For AF_UNIX we flip everything into an iovec. If this doesnt do any speed harm then it will
+ *	be easier for all the low levels to be totally iovec based.
+ */
+ 
+static int unix_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, unsigned flags,
+		struct sockaddr *sa, int *addr_len)
+{
+	struct iovec iov;
+	struct msghdr msg;
+	iov.iov_base=ubuf;
+	iov.iov_len=size;
+	msg.msg_name=(void *)sa;
+	msg.msg_namelen=get_user(addr_len);
+	msg.msg_accrights=NULL;
+	msg.msg_iov=&iov;
+	msg.msg_iovlen=1;
+	return unix_recvmsg(sock,&msg,size,noblock,flags,addr_len);	
+}
+
+static int unix_read(struct socket *sock, char *ubuf, int size, int noblock)
+{
+	return unix_recvfrom(sock,ubuf,size,noblock,0,NULL,NULL);
+}
+
+static int unix_recv(struct socket *sock, void *ubuf, int size, int noblock, unsigned int flags)
+{
+	return unix_recvfrom(sock,ubuf,size,noblock,flags,NULL,NULL);
+}
+
+static int unix_sendto(struct socket *sock, const void *ubuf, int size, int noblock, unsigned flags,
+		struct sockaddr *sa, int addr_len)
+{
+	struct iovec iov;
+	struct msghdr msg;
+	iov.iov_base=(void *)ubuf;
+	iov.iov_len=size;
+	msg.msg_name=(void *)sa;
+	msg.msg_namelen=addr_len;
+	msg.msg_accrights=NULL;
+	msg.msg_iov=&iov;
+	msg.msg_iovlen=1;
+	return unix_sendmsg(sock,&msg,size,noblock,flags);	
+}
+
+static int unix_write(struct socket *sock, const char *ubuf, int size, int noblock)
+{	
+	return unix_sendto(sock,ubuf,size,noblock, 0, NULL, 0);
+}
+
+static int unix_send(struct socket *sock, const void *ubuf, int size, int noblock, unsigned int flags)
+{
+	return unix_sendto(sock,ubuf,size,noblock, flags, NULL, 0);
+}
+
+
+static struct proto_ops unix_proto_ops = {
+	AF_UNIX,
+	
+	unix_create,
+	unix_dup,
+	unix_release,
+	unix_bind,
+	unix_connect,
+	unix_socketpair,
+	unix_accept,
+	unix_getname,
+	unix_read,
+	unix_write,
+	unix_select,
+	unix_ioctl,
+	unix_listen,
+	unix_send,
+	unix_recv,
+	unix_sendto,
+	unix_recvfrom,
+	unix_shutdown,
+	unix_setsockopt,
+	unix_getsockopt,
+	unix_fcntl,
+	unix_sendmsg,
+	unix_recvmsg
+};
+
+
+void unix_proto_init(struct net_proto *pro)
+{
+	printk("NET3: Unix domain sockets 0.07 BETA for Linux NET3.030.\n");
+	sock_register(unix_proto_ops.family, &unix_proto_ops);
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this