📄 af_unix.c
字号:
unix_state_runlock(other); other->data_ready(other, len); sock_put(other); return len;out_unlock: unix_state_runlock(other);out_free: kfree_skb(skb);out: if (other) sock_put(other); return err;} static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm){ struct sock *sk = sock->sk; unix_socket *other = NULL; struct sockaddr_un *sunaddr=msg->msg_name; int err,size; struct sk_buff *skb; int limit=0; int sent=0; err = -EOPNOTSUPP; if (msg->msg_flags&MSG_OOB) goto out_err; if (msg->msg_namelen) { err = (sk->state==TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP); goto out_err; } else { sunaddr = NULL; err = -ENOTCONN; other = unix_peer_get(sk); if (!other) goto out_err; } if (sk->shutdown&SEND_SHUTDOWN) goto pipe_err; while(sent < len) { /* * Optimisation for the fact that under 0.01% of X messages typically * need breaking up. */ size=len-sent; /* Keep two messages in the pipe so it schedules better */ if (size > sk->sndbuf/2 - 16) size = sk->sndbuf/2 - 16; /* * Keep to page sized kmalloc()'s as various people * have suggested. Big mallocs stress the vm too * much. */ if (size > PAGE_SIZE-16) limit = PAGE_SIZE-16; /* Fall back to a page if we can't grab a big buffer this instant */ else limit = 0; /* Otherwise just grab and wait */ /* * Grab a buffer */ skb=sock_alloc_send_skb(sk,size,limit,msg->msg_flags&MSG_DONTWAIT, &err); if (skb==NULL) goto out_err; /* * If you pass two values to the sock_alloc_send_skb * it tries to grab the large buffer with GFP_BUFFER * (which can fail easily), and if it fails grab the * fallback size buffer which is under a page and will * succeed. [Alan] */ size = min(size, skb_tailroom(skb)); memcpy(UNIXCREDS(skb), &scm->creds, sizeof(struct ucred)); if (scm->fp) unix_attach_fds(scm, skb); if ((err = memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size)) != 0) { kfree_skb(skb); goto out_err; } unix_state_rlock(other); if (other->dead || (other->shutdown & RCV_SHUTDOWN)) goto pipe_err_free; skb_queue_tail(&other->receive_queue, skb); unix_state_runlock(other); other->data_ready(other, size); sent+=size; } sock_put(other); return sent;pipe_err_free: unix_state_runlock(other); kfree_skb(skb);pipe_err: if (sent==0 && !(msg->msg_flags&MSG_NOSIGNAL)) send_sig(SIGPIPE,current,0); err = -EPIPE;out_err: if (other) sock_put(other); return sent ? : err;}static void unix_copy_addr(struct msghdr *msg, struct sock *sk){ msg->msg_namelen = sizeof(short); if (sk->protinfo.af_unix.addr) { msg->msg_namelen=sk->protinfo.af_unix.addr->len; memcpy(msg->msg_name, sk->protinfo.af_unix.addr->name, sk->protinfo.af_unix.addr->len); }}static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm){ struct sock *sk = sock->sk; int noblock = flags & MSG_DONTWAIT; struct sk_buff *skb; int err; err = -EOPNOTSUPP; if (flags&MSG_OOB) goto out; msg->msg_namelen = 0; skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) goto out; wake_up_interruptible(&sk->protinfo.af_unix.peer_wait); if (msg->msg_name) unix_copy_addr(msg, skb->sk); if (size > skb->len) size = skb->len; else if (size < skb->len) msg->msg_flags |= MSG_TRUNC; err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size); if (err) goto out_free; scm->creds = *UNIXCREDS(skb); if (!(flags & MSG_PEEK)) { if (UNIXCB(skb).fp) unix_detach_fds(scm, skb); } else { /* It is questionable: on PEEK we could: - do not return fds - good, but too simple 8) - return fds, and do not return them on read (old strategy, apparently wrong) - clone fds (I choosed it for now, it is the most universal solution) POSIX 1003.1g does not actually define this clearly at all. POSIX 1003.1g doesn't define a lot of things clearly however! */ if (UNIXCB(skb).fp) scm->fp = scm_fp_dup(UNIXCB(skb).fp); } err = size;out_free: skb_free_datagram(sk,skb);out: return err;}/* * Sleep until data has arrive. But check for races.. */ static long unix_stream_data_wait(unix_socket * sk, long timeo){ DECLARE_WAITQUEUE(wait, current); unix_state_rlock(sk); add_wait_queue(sk->sleep, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (skb_queue_len(&sk->receive_queue) || sk->err || (sk->shutdown & RCV_SHUTDOWN) || signal_pending(current) || !timeo) break; set_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags); unix_state_runlock(sk); timeo = schedule_timeout(timeo); unix_state_rlock(sk); clear_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags); } __set_current_state(TASK_RUNNING); remove_wait_queue(sk->sleep, &wait); unix_state_runlock(sk); return timeo;}static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm){ struct sock *sk = sock->sk; struct sockaddr_un *sunaddr=msg->msg_name; int copied = 0; int check_creds = 0; int target; int err = 0; long timeo; err = -EINVAL; if (sk->state != TCP_ESTABLISHED) goto out; err = -EOPNOTSUPP; if (flags&MSG_OOB) goto out; target = sock_rcvlowat(sk, flags&MSG_WAITALL, size); timeo = sock_rcvtimeo(sk, flags&MSG_DONTWAIT); msg->msg_namelen = 0; /* Lock the socket to prevent queue disordering * while sleeps in memcpy_tomsg */ down(&sk->protinfo.af_unix.readsem); do { int chunk; struct sk_buff *skb; skb=skb_dequeue(&sk->receive_queue); if (skb==NULL) { if (copied >= target) break; /* * POSIX 1003.1g mandates this order. */ if ((err = sock_error(sk)) != 0) break; if (sk->shutdown & RCV_SHUTDOWN) break; err = -EAGAIN; if (!timeo) break; up(&sk->protinfo.af_unix.readsem); timeo = unix_stream_data_wait(sk, timeo); if (signal_pending(current)) { err = sock_intr_errno(timeo); goto out; } down(&sk->protinfo.af_unix.readsem); continue; } if (check_creds) { /* Never glue messages from different writers */ if (memcmp(UNIXCREDS(skb), &scm->creds, sizeof(scm->creds)) != 0) { skb_queue_head(&sk->receive_queue, skb); break; } } else { /* Copy credentials */ scm->creds = *UNIXCREDS(skb); check_creds = 1; } /* Copy address just once */ if (sunaddr) { unix_copy_addr(msg, skb->sk); sunaddr = NULL; } chunk = min(skb->len, size); if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { skb_queue_head(&sk->receive_queue, skb); if (copied == 0) copied = -EFAULT; break; } copied += chunk; size -= chunk; /* Mark read part of skb as used */ if (!(flags & MSG_PEEK)) { skb_pull(skb, chunk); if (UNIXCB(skb).fp) unix_detach_fds(scm, skb); /* put the skb back if we didn't use it up.. */ if (skb->len) { skb_queue_head(&sk->receive_queue, skb); break; } kfree_skb(skb); if (scm->fp) break; } else { /* It is questionable, see note in unix_dgram_recvmsg. */ if (UNIXCB(skb).fp) scm->fp = scm_fp_dup(UNIXCB(skb).fp); /* put message back and return */ skb_queue_head(&sk->receive_queue, skb); break; } } while (size); up(&sk->protinfo.af_unix.readsem);out: return copied ? : err;}static int unix_shutdown(struct socket *sock, int mode){ struct sock *sk = sock->sk; unix_socket *other; mode = (mode+1)&(RCV_SHUTDOWN|SEND_SHUTDOWN); if (mode) { unix_state_wlock(sk); sk->shutdown |= mode; other=unix_peer(sk); if (other) sock_hold(other); unix_state_wunlock(sk); sk->state_change(sk); if (other && sk->type == SOCK_STREAM) { int peer_mode = 0; if (mode&RCV_SHUTDOWN) peer_mode |= SEND_SHUTDOWN; if (mode&SEND_SHUTDOWN) peer_mode |= RCV_SHUTDOWN; unix_state_wlock(other); other->shutdown |= peer_mode; unix_state_wunlock(other); other->state_change(other); read_lock(&other->callback_lock); if (peer_mode == SHUTDOWN_MASK) sk_wake_async(other,1,POLL_HUP); else if (peer_mode & RCV_SHUTDOWN) sk_wake_async(other,1,POLL_IN); read_unlock(&other->callback_lock); } if (other) sock_put(other); } return 0;}static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg){ struct sock *sk = sock->sk; long amount=0; int err; switch(cmd) { case SIOCOUTQ: amount = atomic_read(&sk->wmem_alloc); err = put_user(amount, (int *)arg); break; case SIOCINQ: { struct sk_buff *skb; if (sk->state==TCP_LISTEN) { err = -EINVAL; break; } spin_lock(&sk->receive_queue.lock); if((skb=skb_peek(&sk->receive_queue))!=NULL) amount=skb->len; spin_unlock(&sk->receive_queue.lock); err = put_user(amount, (int *)arg); break; } default: err = dev_ioctl(cmd, (void *)arg); break; } return err;}static unsigned int unix_poll(struct file * file, struct socket *sock, poll_table *wait){ struct sock *sk = sock->sk; unsigned int mask; poll_wait(file, sk->sleep, wait); mask = 0; /* exceptional events? */ if (sk->err) mask |= POLLERR; if (sk->shutdown == SHUTDOWN_MASK) mask |= POLLHUP; /* readable? */ if (!skb_queue_empty(&sk->receive_queue) || (sk->shutdown&RCV_SHUTDOWN)) mask |= POLLIN | POLLRDNORM; /* Connection-based need to check for termination and startup */ if (sk->type == SOCK_STREAM && sk->state==TCP_CLOSE) mask |= POLLHUP; /* * we set writable also when the other side has shut down the * connection. This prevents stuck sockets. */ if (unix_writable(sk)) mask |= POLLOUT | POLLWRNORM | POLLWRBAND; return mask;}#ifdef CONFIG_PROC_FSstatic int unix_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data){ off_t pos=0; off_t begin=0; int len=0; int i; unix_socket *s; len+= sprintf(buffer,"Num RefCount Protocol Flags Type St " "Inode Path\n"); read_lock(&unix_table_lock); forall_unix_sockets (i,s) { unix_state_rlock(s); len+=sprintf(buffer+len,"%p: %08X %08X %08X %04X %02X %5ld", s, atomic_read(&s->refcnt), 0, s->state == TCP_LISTEN ? __SO_ACCEPTCON : 0, s->type, s->socket ? (s->state == TCP_ESTABLISHED ? SS_CONNECTED : SS_UNCONNECTED) : (s->state == TCP_ESTABLISHED ? SS_CONNECTING : SS_DISCONNECTING), sock_i_ino(s)); if (s->protinfo.af_unix.addr) { buffer[len++] = ' '; memcpy(buffer+len, s->protinfo.af_unix.addr->name->sun_path, s->protinfo.af_unix.addr->len-sizeof(short)); if (!UNIX_ABSTRACT(s)) len--; else buffer[len] = '@'; len += s->protinfo.af_unix.addr->len - sizeof(short); } unix_state_runlock(s); buffer[len++]='\n'; pos = begin + len; if(pos<offset) { len=0; begin=pos; } if(pos>offset+length) goto done; } *eof = 1;done: read_unlock(&unix_table_lock); *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) len=length; if (len < 0) len = 0; return len;}#endifstruct proto_ops unix_stream_ops = { family: PF_UNIX, release: unix_release, bind: unix_bind, connect: unix_stream_connect, socketpair: unix_socketpair, accept: unix_accept, getname: unix_getname, poll: unix_poll, ioctl: unix_ioctl, listen: unix_listen, shutdown: unix_shutdown, setsockopt: sock_no_setsockopt, getsockopt: sock_no_getsockopt, sendmsg: unix_stream_sendmsg, recvmsg: unix_stream_recvmsg, mmap: sock_no_mmap,};struct proto_ops unix_dgram_ops = { family: PF_UNIX, release: unix_release, bind: unix_bind, connect: unix_dgram_connect, socketpair: unix_socketpair, accept: sock_no_accept, getname: unix_getname, poll: datagram_poll, ioctl: unix_ioctl, listen: sock_no_listen, shutdown: unix_shutdown, setsockopt: sock_no_setsockopt, getsockopt: sock_no_getsockopt, sendmsg: unix_dgram_sendmsg, recvmsg: unix_dgram_recvmsg, mmap: sock_no_mmap,};struct net_proto_family unix_family_ops = { PF_UNIX, unix_create};#ifdef CONFIG_SYSCTLextern void unix_sysctl_register(void);extern void unix_sysctl_unregister(void);#endifstatic int __init af_unix_init(void){ struct sk_buff *dummy_skb; printk(KERN_INFO "NET4: Unix domain sockets 1.0/SMP for Linux NET4.0.\n"); if (sizeof(struct unix_skb_parms) > sizeof(dummy_skb->cb)) { printk(KERN_CRIT "unix_proto_init: panic\n"); return -1; } sock_register(&unix_family_ops);#ifdef CONFIG_PROC_FS create_proc_read_entry("net/unix", 0, 0, unix_read_proc, NULL);#endif#ifdef CONFIG_SYSCTL unix_sysctl_register();#endif return 0;}static void __exit af_unix_exit(void){ sock_unregister(PF_UNIX);#ifdef CONFIG_SYSCTL unix_sysctl_unregister();#endif#ifdef CONFIG_PROC_FS remove_proc_entry("net/unix", 0);#endif}module_init(af_unix_init);module_exit(af_unix_exit);/* * Local variables: * compile-command: "gcc -g -D__KERNEL__ -Wall -O6 -I/usr/src/linux/include -c af_unix.c" * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -