1178 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1178 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Unix Domain Sockets Implementation (PF_UNIX, PF_LOCAL)
 | |
|  * This code handles requests generated by operations on /dev/uds
 | |
|  *
 | |
|  * The entry points into this file are...
 | |
|  *
 | |
|  *   uds_open:   handles the   open(2) syscall on /dev/uds
 | |
|  *   uds_close:  handles the  close(2) syscall on /dev/uds
 | |
|  *   uds_select: handles the select(2) syscall on /dev/uds
 | |
|  *   uds_read:   handles the   read(2) syscall on /dev/uds
 | |
|  *   uds_write:  handles the  write(2) syscall on /dev/uds
 | |
|  *   uds_ioctl:  handles the  ioctl(2) syscall on /dev/uds
 | |
|  *   uds_status: handles status requests.
 | |
|  *   uds_cancel: handles cancelled syscalls.
 | |
|  *
 | |
|  * Also See...
 | |
|  *
 | |
|  *   table.c, uds.c, uds.h
 | |
|  *
 | |
|  * Overview
 | |
|  *
 | |
|  * The interface to unix domain sockets is similar to the
 | |
|  * the interface to network sockets. There is a character
 | |
|  * device (/dev/uds) that uses STYLE_CLONE and this server
 | |
|  * is a 'driver' for that device.
 | |
|  */
 | |
| 
 | |
| #define DEBUG 0
 | |
| 
 | |
| #include "inc.h"
 | |
| #include "const.h"
 | |
| #include "glo.h"
 | |
| #include "uds.h"
 | |
| 
 | |
| FORWARD _PROTOTYPE( int uds_perform_read, (int minor, endpoint_t m_source,
 | |
| 	size_t size, int pretend));
 | |
| FORWARD _PROTOTYPE( int uds_perform_write, (int minor, endpoint_t m_source,
 | |
| 	size_t size, int pretend));
 | |
| 
 | |
| PUBLIC int uds_open(message *dev_m_in, message *dev_m_out)
 | |
| {
 | |
| 	message fs_m_in, fs_m_out;
 | |
| 	struct ucred ucred;
 | |
| 	int rc, i;
 | |
| 	int minor;
 | |
| 
 | |
| #if DEBUG == 1
 | |
| 	static int call_count = 0;
 | |
| 	printf("(uds) [%d] uds_open() call_count=%d\n", uds_minor(dev_m_in),
 | |
| 							++call_count);
 | |
| 	printf("Endpoint: 0x%x\n", dev_m_in->USER_ENDPT);
 | |
| #endif
 | |
| 
 | |
| 	/*
 | |
| 	 * Find a slot in the descriptor table for the new descriptor.
 | |
| 	 * The index of the descriptor in the table will be returned.
 | |
| 	 * Subsequent calls to read/write/close/ioctl/etc will use this
 | |
| 	 * minor number. The minor number must be different from the
 | |
| 	 * the /dev/uds device's minor number (currently 0).
 | |
| 	 */
 | |
| 
 | |
| 	minor = -1; /* to trap error */
 | |
| 
 | |
| 	for (i = 1; i < NR_FDS; i++) {
 | |
| 		if (uds_fd_table[i].state == UDS_FREE) {
 | |
| 			minor = i;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (minor == -1) {
 | |
| 
 | |
| 		/* descriptor table full */
 | |
| 		uds_set_reply(dev_m_out, DEV_OPEN_REPL, dev_m_in->USER_ENDPT,
 | |
| 			      (cp_grant_id_t) dev_m_in->IO_GRANT, ENFILE);
 | |
| 		return ENFILE;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * We found a slot in uds_fd_table, now initialize the descriptor
 | |
| 	 */
 | |
| 
 | |
| 	/* mark this one as 'in use' so that it doesn't get assigned to
 | |
| 	 * another socket
 | |
| 	 */
 | |
| 	uds_fd_table[minor].state = UDS_INUSE;
 | |
| 
 | |
| 	/* track the system call we are performing in case it gets cancelled */
 | |
| 	uds_fd_table[minor].call_nr = dev_m_in->m_type;
 | |
| 	uds_fd_table[minor].ioctl = 0;
 | |
| 	uds_fd_table[minor].syscall_done = 0;
 | |
| 
 | |
| 	/* set the socket owner */
 | |
| 	uds_fd_table[minor].owner = dev_m_in->USER_ENDPT;
 | |
| 	uds_fd_table[minor].endpoint = dev_m_in->USER_ENDPT;
 | |
| 
 | |
| 	/* setup select(2) framework */
 | |
| 	uds_fd_table[minor].selecting = 0;
 | |
| 	uds_fd_table[minor].select_proc = 0;
 | |
| 	uds_fd_table[minor].sel_ops_in = 0;
 | |
| 	uds_fd_table[minor].sel_ops_out = 0;
 | |
| 	uds_fd_table[minor].status_updated = 0;
 | |
| 
 | |
| 	/* initialize the data pointer (pos) to the start of the PIPE */
 | |
| 	uds_fd_table[minor].pos = 0;
 | |
| 
 | |
| 	/* the PIPE is initially empty */
 | |
| 	uds_fd_table[minor].size = 0;
 | |
| 
 | |
| 	/* the default for a new socket is to allow reading and writing.
 | |
| 	 * shutdown(2) will remove one or both flags.
 | |
| 	 */
 | |
| 	uds_fd_table[minor].mode = S_IRUSR|S_IWUSR;
 | |
| 
 | |
| 	/* In libc socket(2) sets this to the actual value later with the
 | |
| 	 * NWIOSUDSTYPE ioctl().
 | |
| 	 */
 | |
| 	uds_fd_table[minor].type = -1;
 | |
| 
 | |
| 	/* Clear the backlog by setting each entry to -1 */
 | |
| 	for (i = 0; i < UDS_SOMAXCONN; i++) {
 | |
| 		/* initially no connections are pending */
 | |
| 		uds_fd_table[minor].backlog[i] = -1;
 | |
| 	}
 | |
| 
 | |
| 	memset(&uds_fd_table[minor].ancillary_data, '\0', sizeof(struct
 | |
| 								ancillary));
 | |
| 	for (i = 0; i < OPEN_MAX; i++) {
 | |
| 		uds_fd_table[minor].ancillary_data.fds[i] = -1;
 | |
| 	}
 | |
| 
 | |
| 	/* default the size to UDS_SOMAXCONN */
 | |
| 	uds_fd_table[minor].backlog_size = UDS_SOMAXCONN;
 | |
| 
 | |
| 	/* the socket isn't listening for incoming connections until
 | |
| 	 * listen(2) is called
 | |
| 	 */
 | |
| 	uds_fd_table[minor].listening = 0;
 | |
| 
 | |
| 	/* initially the socket is not connected to a peer */
 | |
| 	uds_fd_table[minor].peer = -1;
 | |
| 
 | |
| 	/* there isn't a child waiting to be accept(2)'d */
 | |
| 	uds_fd_table[minor].child = -1;
 | |
| 
 | |
| 	/* initially the socket is not bound or listening on an address */
 | |
| 	memset(&(uds_fd_table[minor].addr), '\0', sizeof(struct sockaddr_un));
 | |
| 	memset(&(uds_fd_table[minor].source), '\0', sizeof(struct sockaddr_un));
 | |
| 	memset(&(uds_fd_table[minor].target), '\0', sizeof(struct sockaddr_un));
 | |
| 
 | |
| 	/* Initially the socket isn't suspended. */
 | |
| 	uds_fd_table[minor].suspended = UDS_NOT_SUSPENDED;
 | |
| 
 | |
| 	/* and the socket doesn't have an I/O grant initially */
 | |
| 	uds_fd_table[minor].io_gr = (cp_grant_id_t) 0;
 | |
| 
 | |
| 	/* since there is no I/O grant it effectively has no size either */
 | |
| 	uds_fd_table[minor].io_gr_size = 0;
 | |
| 
 | |
| 	/* The process isn't suspended so we don't flag it as revivable */
 | |
| 	uds_fd_table[minor].ready_to_revive = 0;
 | |
| 
 | |
| 	/* get the effective user id and effective group id from the endpoint */
 | |
| 	/* this is needed in the REQ_NEWNODE request to PFS. */
 | |
| 	rc = getnucred(uds_fd_table[minor].endpoint, &ucred);
 | |
| 	if (rc == -1) {
 | |
| 		/* roll back the changes we made to the descriptor */
 | |
| 		memset(&(uds_fd_table[minor]), '\0', sizeof(uds_fd_t));
 | |
| 
 | |
| 		/* likely error: invalid endpoint / proc doesn't exist */
 | |
| 		uds_set_reply(dev_m_out, DEV_OPEN_REPL, dev_m_in->USER_ENDPT,
 | |
| 			      (cp_grant_id_t) dev_m_in->IO_GRANT, errno);
 | |
| 		return errno;
 | |
| 	}
 | |
| 
 | |
| 	/* Prepare Request to the FS side of PFS */
 | |
| 
 | |
| 	fs_m_in.m_type = REQ_NEWNODE;
 | |
| 	fs_m_in.REQ_MODE = I_NAMED_PIPE;
 | |
| 	fs_m_in.REQ_DEV = NO_DEV;
 | |
| 	fs_m_in.REQ_UID = ucred.uid;
 | |
| 	fs_m_in.REQ_GID = ucred.gid;
 | |
| 
 | |
| 	/* Request a new inode on the pipe file system */
 | |
| 
 | |
| 	rc = fs_newnode(&fs_m_in, &fs_m_out);
 | |
| 	if (rc != OK) {
 | |
| 		/* roll back the changes we made to the descriptor */
 | |
| 		memset(&(uds_fd_table[minor]), '\0', sizeof(uds_fd_t));
 | |
| 
 | |
| 		/* likely error: get_block() failed */
 | |
| 		uds_set_reply(dev_m_out, DEV_OPEN_REPL, dev_m_in->USER_ENDPT,
 | |
| 				(cp_grant_id_t) dev_m_in->IO_GRANT, rc);
 | |
| 		return rc;
 | |
| 	}
 | |
| 
 | |
| 	/* Process the response */
 | |
| 
 | |
| 	uds_fd_table[minor].inode_nr = fs_m_out.RES_INODE_NR;
 | |
| 
 | |
| 	/* prepare the reply */
 | |
| 
 | |
| 	uds_fd_table[minor].syscall_done = 1;
 | |
| 	uds_set_reply(dev_m_out, DEV_OPEN_REPL, dev_m_in->USER_ENDPT,
 | |
| 		      (cp_grant_id_t) dev_m_in->IO_GRANT, minor);
 | |
| 	return minor;
 | |
| }
 | |
| 
 | |
| PUBLIC int uds_close(message *dev_m_in, message *dev_m_out)
 | |
| {
 | |
| 	int minor;
 | |
| 	message fs_m_in, fs_m_out;
 | |
| 	int rc;
 | |
| 
 | |
| #if DEBUG == 1
 | |
| 	static int call_count = 0;
 | |
| 	printf("(uds) [%d] uds_close() call_count=%d\n", uds_minor(dev_m_in),
 | |
| 							++call_count);
 | |
| 	printf("Endpoint: 0x%x\n", dev_m_in->USER_ENDPT);
 | |
| #endif
 | |
| 
 | |
| 	minor = uds_minor(dev_m_in);
 | |
| 
 | |
| 	if (uds_fd_table[minor].state != UDS_INUSE) {
 | |
| 		/* attempted to close a socket that hasn't been opened --
 | |
| 		 * something is very wrong :(
 | |
| 		 */
 | |
| 		uds_set_reply(dev_m_out, DEV_CLOSE_REPL, dev_m_in->USER_ENDPT,
 | |
| 			      (cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL);
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	/* no need to track the syscall in case of cancellation. close() is
 | |
| 	 * atomic and can't be cancelled. no need to update the endpoint here,
 | |
| 	 * we won't be needing it to kill the socket
 | |
| 	 */
 | |
| 
 | |
| 	/* if the socket is connected, disconnect it */
 | |
| 	if (uds_fd_table[minor].peer != -1) {
 | |
| 
 | |
| 		/* set peer of this peer to -1 */
 | |
| 		uds_fd_table[uds_fd_table[minor].peer].peer = -1;
 | |
| 
 | |
| 		/* error to pass to peer */
 | |
| 		uds_fd_table[uds_fd_table[minor].peer].err = ECONNRESET;
 | |
| 
 | |
| 		/* if peer was blocked on I/O revive peer */
 | |
| 		if (uds_fd_table[uds_fd_table[minor].peer].suspended) {
 | |
| 			int peer = uds_fd_table[minor].peer;
 | |
| 
 | |
| 			uds_fd_table[peer].ready_to_revive = 1;
 | |
| 			uds_unsuspend(dev_m_in->m_source, peer);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (uds_fd_table[minor].ancillary_data.nfiledes > 0) {
 | |
| 		clear_fds(minor, &(uds_fd_table[minor].ancillary_data));
 | |
| 	}
 | |
| 
 | |
| 	/* Prepare Request to the FS side of PFS */
 | |
| 
 | |
| 	fs_m_in.m_type = REQ_PUTNODE;
 | |
| 	fs_m_in.REQ_INODE_NR = uds_fd_table[minor].inode_nr;
 | |
| 	fs_m_in.REQ_COUNT = 1;
 | |
| 
 | |
| 	/* set the socket back to its original UDS_FREE state */
 | |
| 	memset(&(uds_fd_table[minor]), '\0', sizeof(uds_fd_t));
 | |
| 
 | |
| 	/* Request the removal of the inode from the pipe file system */
 | |
| 
 | |
| 	rc = fs_putnode(&fs_m_in, &fs_m_out);
 | |
| 	if (rc != OK) {
 | |
| 		perror("fs_putnode");
 | |
| 		/* likely error: get_block() failed */
 | |
| 		return rc;
 | |
| 	}
 | |
| 
 | |
| 	uds_set_reply(dev_m_out, DEV_CLOSE_REPL, dev_m_in->USER_ENDPT,
 | |
| 		      (cp_grant_id_t) dev_m_in->IO_GRANT, OK);
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| PUBLIC int uds_select(message *dev_m_in, message *dev_m_out)
 | |
| {
 | |
| 	int i, bytes;
 | |
| 	int minor;
 | |
| 
 | |
| #if DEBUG == 1
 | |
| 	static int call_count = 0;
 | |
| 	printf("(uds) [%d] uds_select() call_count=%d\n", uds_minor(dev_m_in),
 | |
| 							++call_count);
 | |
| 	printf("Endpoint: 0x%x\n", dev_m_in->USER_ENDPT);
 | |
| #endif
 | |
| 
 | |
| 	minor = uds_minor(dev_m_in);
 | |
| 
 | |
| 	if (uds_fd_table[minor].state != UDS_INUSE) {
 | |
| 
 | |
| 		/* attempted to close a socket that hasn't been opened --
 | |
| 		 * something is very wrong :(
 | |
| 		 */
 | |
| 
 | |
| 		uds_sel_reply(dev_m_out, DEV_SEL_REPL1, minor, EINVAL);
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	/* setup select(2) framework */
 | |
| 	uds_fd_table[minor].selecting = 1;
 | |
| 	uds_fd_table[minor].select_proc = dev_m_in->m_source;
 | |
| 
 | |
| 	/* track the system call we are performing in case it gets cancelled */
 | |
| 	uds_fd_table[minor].call_nr = dev_m_in->m_type;
 | |
| 	uds_fd_table[minor].ioctl = 0;
 | |
| 	uds_fd_table[minor].syscall_done = 0;
 | |
| 
 | |
| 	/* Can't update the process endpoint here, no info.  */
 | |
| 
 | |
| 	uds_fd_table[minor].sel_ops_in = dev_m_in->USER_ENDPT;
 | |
| 	uds_fd_table[minor].sel_ops_out = 0;
 | |
| 
 | |
| 	/* check if there is data available to read */
 | |
| 	bytes = uds_perform_read(minor, dev_m_in->m_source, 1, 1);
 | |
| 	if (bytes > 0) {
 | |
| 
 | |
| 		/* there is data in the pipe for us to read */
 | |
| 		uds_fd_table[minor].sel_ops_out |= SEL_RD;
 | |
| 
 | |
| 	} else if (uds_fd_table[minor].listening == 1) {
 | |
| 
 | |
| 		/* check for pending connections */
 | |
| 		for (i = 0; i < uds_fd_table[minor].backlog_size; i++) {
 | |
| 			if (uds_fd_table[minor].backlog[i] != -1) {
 | |
| 				uds_fd_table[minor].sel_ops_out |= SEL_RD;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* check if we can write without blocking */
 | |
| 	bytes = uds_perform_write(minor, dev_m_in->m_source, PIPE_BUF, 1);
 | |
| 	if (bytes > 0) {
 | |
| 		uds_fd_table[minor].sel_ops_out |= SEL_WR;
 | |
| 	}
 | |
| 
 | |
| 	uds_fd_table[minor].syscall_done = 1;
 | |
| 	uds_sel_reply(dev_m_out, DEV_SEL_REPL1, minor,
 | |
| 		      uds_fd_table[minor].sel_ops_out);
 | |
| 
 | |
| 	return uds_fd_table[minor].sel_ops_out;
 | |
| }
 | |
| 
 | |
| PRIVATE int uds_perform_read(int minor, endpoint_t m_source,
 | |
| 	size_t size, int pretend)
 | |
| {
 | |
| 	int rc;
 | |
| 	message fs_m_in;
 | |
| 	message fs_m_out;
 | |
| 
 | |
| #if DEBUG == 1
 | |
| 	static int call_count = 0;
 | |
| 	printf("(uds) [%d] uds_perform_read() call_count=%d\n", minor,
 | |
| 							++call_count);
 | |
| #endif
 | |
| 
 | |
| 	/* skip reads and writes of 0 (or less!) bytes */
 | |
| 	if (size <= 0) {
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	/* check if we are allowed to read */
 | |
| 	if (!(uds_fd_table[minor].mode & S_IRUSR)) {
 | |
| 
 | |
| 		/* socket is shutdown for reading */
 | |
| 		return EPIPE;
 | |
| 	}
 | |
| 
 | |
| 	if (uds_fd_table[minor].size == 0) {
 | |
| 
 | |
| 		if (pretend) {
 | |
| 			return SUSPEND;
 | |
| 		}
 | |
| 
 | |
| 		/* maybe a process is blocked waiting to write? if
 | |
| 		 * needed revive the writer
 | |
| 		 */
 | |
| 		if (uds_fd_table[minor].peer != -1 &&
 | |
| 			uds_fd_table[uds_fd_table[minor].peer].suspended) {
 | |
| 			int peer = uds_fd_table[minor].peer;
 | |
| 
 | |
| 			uds_fd_table[peer].ready_to_revive = 1;
 | |
| 			uds_unsuspend(m_source, peer);
 | |
| 		}
 | |
| 
 | |
| #if DEBUG == 1
 | |
| 		printf("(uds) [%d] suspending read request\n", minor);
 | |
| #endif
 | |
| 
 | |
| 		/* Process is reading from an empty pipe,
 | |
| 		 * suspend it so some bytes can be written
 | |
| 		 */
 | |
| 		uds_fd_table[minor].suspended = UDS_SUSPENDED_READ;
 | |
| 		return SUSPEND;
 | |
| 	}
 | |
| 
 | |
| 	if (pretend) {
 | |
| 
 | |
| 		return (size > uds_fd_table[minor].size) ?
 | |
| 				uds_fd_table[minor].size : size;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	/* Prepare Request to the FS side of PFS */
 | |
| 	fs_m_in.m_type = REQ_READ;
 | |
| 	fs_m_in.REQ_INODE_NR = uds_fd_table[minor].inode_nr;
 | |
| 	fs_m_in.REQ_GRANT = uds_fd_table[minor].io_gr;
 | |
| 	fs_m_in.REQ_SEEK_POS_HI = 0;
 | |
| 	fs_m_in.REQ_SEEK_POS_LO = uds_fd_table[minor].pos;
 | |
| 	fs_m_in.REQ_NBYTES = (size > uds_fd_table[minor].size) ?
 | |
| 				uds_fd_table[minor].size : size;
 | |
| 
 | |
| 	/* perform the read */
 | |
| 	rc = fs_readwrite(&fs_m_in, &fs_m_out);
 | |
| 	if (rc != OK) {
 | |
| 		perror("fs_readwrite");
 | |
| 		return rc;
 | |
| 	}
 | |
| 
 | |
| 	/* Process the response */
 | |
| #if DEBUG == 1
 | |
| 	printf("(uds) [%d] read complete\n", minor);
 | |
| #endif
 | |
| 
 | |
| 	/* move the position of the data pointer up to data we haven't
 | |
| 	 * read yet
 | |
| 	 */
 | |
| 	uds_fd_table[minor].pos += fs_m_out.RES_NBYTES;
 | |
| 
 | |
| 	/* decrease the number of unread bytes */
 | |
| 	uds_fd_table[minor].size -= fs_m_out.RES_NBYTES;
 | |
| 
 | |
| 	/* if we have 0 unread bytes, move the data pointer back to the
 | |
| 	 * start of the buffer
 | |
| 	 */
 | |
| 	if (uds_fd_table[minor].size == 0) {
 | |
| 		uds_fd_table[minor].pos = 0;
 | |
| 	}
 | |
| 
 | |
| 	/* maybe a big write was waiting for us to read some data, if
 | |
| 	 * needed revive the writer
 | |
| 	 */
 | |
| 	if (uds_fd_table[minor].peer != -1 &&
 | |
| 			uds_fd_table[uds_fd_table[minor].peer].suspended) {
 | |
| 		int peer = uds_fd_table[minor].peer;
 | |
| 
 | |
| 		uds_fd_table[peer].ready_to_revive = 1;
 | |
| 		uds_unsuspend(m_source, peer);
 | |
| 	}
 | |
| 
 | |
| 	/* see if peer is blocked on select() and a write is possible
 | |
| 	 * (from peer to minor)
 | |
| 	 */
 | |
| 	if (uds_fd_table[minor].peer != -1 &&
 | |
| 		uds_fd_table[uds_fd_table[minor].peer].selecting == 1 &&
 | |
| 		(uds_fd_table[minor].size + uds_fd_table[minor].pos + 1
 | |
| 		< PIPE_BUF)) {
 | |
| 
 | |
| 		int peer = uds_fd_table[minor].peer;
 | |
| 
 | |
| 		/* if the peer wants to know about write being possible
 | |
| 		 * and it doesn't know about it already, then let the peer know.
 | |
| 		 */
 | |
| 		if ((uds_fd_table[peer].sel_ops_in & SEL_WR) &&
 | |
| 				!(uds_fd_table[peer].sel_ops_out & SEL_WR)) {
 | |
| 
 | |
| 			/* a write on peer is possible now */
 | |
| 			uds_fd_table[peer].sel_ops_out |= SEL_WR;
 | |
| 			uds_fd_table[peer].status_updated = 1;
 | |
| 			uds_unsuspend(m_source, peer);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return fs_m_out.RES_NBYTES; /* return number of bytes read */
 | |
| }
 | |
| 
 | |
| PRIVATE int uds_perform_write(int minor, endpoint_t m_source,
 | |
| 						size_t size, int pretend)
 | |
| {
 | |
| 	int rc, peer, i;
 | |
| 	message fs_m_in;
 | |
| 	message fs_m_out;
 | |
| 
 | |
| #if DEBUG == 1
 | |
| 	static int call_count = 0;
 | |
| 	printf("(uds) [%d] uds_perform_write() call_count=%d\n", minor,
 | |
| 							++call_count);
 | |
| #endif
 | |
| 
 | |
| 	/* skip reads and writes of 0 (or less!) bytes */
 | |
| 	if (size <= 0) {
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	/* check if we are allowed to write */
 | |
| 	if (!(uds_fd_table[minor].mode & S_IWUSR)) {
 | |
| 
 | |
| 		/* socket is shutdown for writing */
 | |
| 		return EPIPE;
 | |
| 	}
 | |
| 
 | |
| 	if (size > PIPE_BUF) {
 | |
| 
 | |
| 		/* message is too big to ever write to the PIPE */
 | |
| 		return EMSGSIZE;
 | |
| 	}
 | |
| 
 | |
| 	if (uds_fd_table[minor].type == SOCK_STREAM ||
 | |
| 			uds_fd_table[minor].type == SOCK_SEQPACKET) {
 | |
| 
 | |
| 		/* if we're writing with a connection oriented socket,
 | |
| 		 * then it needs a peer to write to
 | |
| 		 */
 | |
| 		if (uds_fd_table[minor].peer == -1) {
 | |
| 			if (uds_fd_table[minor].err == ECONNRESET) {
 | |
| 
 | |
| 				uds_fd_table[minor].err = 0;
 | |
| 				return ECONNRESET;
 | |
| 			} else {
 | |
| 				return ENOTCONN;
 | |
| 			}
 | |
| 		} else {
 | |
| 
 | |
| 			peer = uds_fd_table[minor].peer;
 | |
| 		}
 | |
| 
 | |
| 	} else /* uds_fd_table[minor].type == SOCK_DGRAM */ {
 | |
| 
 | |
| 		peer = -1;
 | |
| 
 | |
| 		/* locate the "peer" we want to write to */
 | |
| 		for (i = 0; i < NR_FDS; i++) {
 | |
| 
 | |
| 			/* look for a SOCK_DGRAM socket that is bound on
 | |
| 			 * the target address
 | |
| 			 */
 | |
| 			if (uds_fd_table[i].type == SOCK_DGRAM &&
 | |
| 				uds_fd_table[i].addr.sun_family == AF_UNIX &&
 | |
| 				!strncmp(uds_fd_table[minor].target.sun_path,
 | |
| 				uds_fd_table[i].addr.sun_path, UNIX_PATH_MAX)) {
 | |
| 
 | |
| 				peer = i;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (peer == -1) {
 | |
| 			return ENOENT;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* check if write would overrun buffer. check if message
 | |
| 	 * boundry preserving types (SEQPACKET and DGRAM) wouldn't write
 | |
| 	 * to an empty buffer. check if connectionless sockets have a
 | |
| 	 * target to write to.
 | |
| 	 */
 | |
| 	if ((uds_fd_table[peer].pos+uds_fd_table[peer].size+size > PIPE_BUF) ||
 | |
| 		((uds_fd_table[minor].type == SOCK_SEQPACKET ||
 | |
| 		uds_fd_table[minor].type == SOCK_DGRAM) &&
 | |
| 		uds_fd_table[peer].size > 0) || (peer == -1)) {
 | |
| 
 | |
| 		if (pretend) {
 | |
| 			return SUSPEND;
 | |
| 		}
 | |
| 
 | |
| 		/* if needed revive the reader */
 | |
| 		if (uds_fd_table[peer].suspended) {
 | |
| 			uds_fd_table[peer].ready_to_revive = 1;
 | |
| 			uds_unsuspend(m_source, peer);
 | |
| 		}
 | |
| 
 | |
| #if DEBUG == 1
 | |
| 	printf("(uds) [%d] suspending write request\n", minor);
 | |
| #endif
 | |
| 
 | |
| 		/* Process is reading from an empty pipe,
 | |
| 		 * suspend it so some bytes can be written
 | |
| 		 */
 | |
| 		uds_fd_table[minor].suspended = UDS_SUSPENDED_WRITE;
 | |
| 		return SUSPEND;
 | |
| 	}
 | |
| 
 | |
| 	if (pretend) {
 | |
| 		return size;
 | |
| 	}
 | |
| 
 | |
| 	/* Prepare Request to the FS side of PFS */
 | |
| 	fs_m_in.m_type = REQ_WRITE;
 | |
| 	fs_m_in.REQ_INODE_NR = uds_fd_table[peer].inode_nr;
 | |
| 	fs_m_in.REQ_GRANT = uds_fd_table[minor].io_gr;
 | |
| 	fs_m_in.REQ_SEEK_POS_HI = 0;
 | |
| 	fs_m_in.REQ_SEEK_POS_LO = uds_fd_table[peer].pos +
 | |
| 					uds_fd_table[peer].size;
 | |
| 	fs_m_in.REQ_NBYTES = size;
 | |
| 
 | |
| 	/* Request the write */
 | |
| 	rc = fs_readwrite(&fs_m_in, &fs_m_out);
 | |
| 	if (rc != OK) {
 | |
| 		perror("fs_readwrite");
 | |
| 		return rc;
 | |
| 	}
 | |
| 
 | |
| 	/* Process the response */
 | |
| #if DEBUG == 1
 | |
| 	printf("(uds) [%d] write complete\n", minor);
 | |
| #endif
 | |
| 	/* increase the count of unread bytes */
 | |
| 	uds_fd_table[peer].size += fs_m_out.RES_NBYTES;
 | |
| 
 | |
| 
 | |
| 	/* fill in the source address to be returned by recvfrom & recvmsg */
 | |
| 	if (uds_fd_table[minor].type == SOCK_DGRAM) {
 | |
| 		memcpy(&uds_fd_table[peer].source, &uds_fd_table[minor].addr,
 | |
| 						sizeof(struct sockaddr_un));
 | |
| 	}
 | |
| 
 | |
| 	/* revive peer that was waiting for us to write */
 | |
| 	if (uds_fd_table[peer].suspended) {
 | |
| 		uds_fd_table[peer].ready_to_revive = 1;
 | |
| 		uds_unsuspend(m_source, peer);
 | |
| 	}
 | |
| 
 | |
| 	/* see if peer is blocked on select()*/
 | |
| 	if (uds_fd_table[peer].selecting == 1 && fs_m_out.RES_NBYTES > 0) {
 | |
| 
 | |
| 		/* if the peer wants to know about data ready to read
 | |
| 		 * and it doesn't know about it already, then let the peer
 | |
| 		 * know we have data for it.
 | |
| 		 */
 | |
| 		if ((uds_fd_table[peer].sel_ops_in & SEL_RD) &&
 | |
| 				!(uds_fd_table[peer].sel_ops_out & SEL_RD)) {
 | |
| 
 | |
| 			/* a read on peer is possible now */
 | |
| 			uds_fd_table[peer].sel_ops_out |= SEL_RD;
 | |
| 			uds_fd_table[peer].status_updated = 1;
 | |
| 			uds_unsuspend(m_source, peer);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return fs_m_out.RES_NBYTES; /* return number of bytes written */
 | |
| }
 | |
| 
 | |
| PUBLIC int uds_read(message *dev_m_in, message *dev_m_out)
 | |
| {
 | |
| 	int bytes;
 | |
| 	int minor;
 | |
| 
 | |
| #if DEBUG == 1
 | |
| 	static int call_count = 0;
 | |
| 	printf("(uds) [%d] uds_read() call_count=%d\n", uds_minor(dev_m_in),
 | |
| 							++call_count);
 | |
| 	printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->USER_ENDPT,
 | |
| 							dev_m_in->POSITION);
 | |
| #endif
 | |
| 
 | |
| 	minor = uds_minor(dev_m_in);
 | |
| 
 | |
| 	if (uds_fd_table[minor].state != UDS_INUSE) {
 | |
| 
 | |
| 		/* attempted to close a socket that hasn't been opened --
 | |
| 		 * something is very wrong :(
 | |
| 		 */
 | |
| 		uds_set_reply(dev_m_out, DEV_REVIVE, dev_m_in->USER_ENDPT,
 | |
| 			      (cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL);
 | |
| 
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	/* track the system call we are performing in case it gets cancelled */
 | |
| 	uds_fd_table[minor].call_nr = dev_m_in->m_type;
 | |
| 	uds_fd_table[minor].ioctl = 0;
 | |
| 	uds_fd_table[minor].syscall_done = 0;
 | |
| 
 | |
| 	/* Update the process endpoint. */
 | |
| 	uds_fd_table[minor].endpoint = dev_m_in->USER_ENDPT;
 | |
| 
 | |
| 	/* setup select(2) framework */
 | |
| 	uds_fd_table[minor].selecting = 0;
 | |
| 
 | |
| 	/* save I/O Grant info */
 | |
| 	uds_fd_table[minor].io_gr = (cp_grant_id_t) dev_m_in->IO_GRANT;
 | |
| 	uds_fd_table[minor].io_gr_size = dev_m_in->COUNT;
 | |
| 
 | |
| 	bytes = uds_perform_read(minor, dev_m_in->m_source,
 | |
| 					uds_fd_table[minor].io_gr_size, 0);
 | |
| 
 | |
| 	uds_set_reply(dev_m_out, DEV_REVIVE, uds_fd_table[minor].endpoint,
 | |
| 		      uds_fd_table[minor].io_gr, bytes);
 | |
| 
 | |
| 	return bytes;
 | |
| }
 | |
| 
 | |
| PUBLIC int uds_write(message *dev_m_in, message *dev_m_out)
 | |
| {
 | |
| 	int bytes;
 | |
| 	int minor;
 | |
| 
 | |
| #if DEBUG == 1
 | |
| 	static int call_count = 0;
 | |
| 	printf("(uds) [%d] uds_write() call_count=%d\n", uds_minor(dev_m_in),
 | |
| 							++call_count);
 | |
| 	printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->USER_ENDPT,
 | |
| 							dev_m_in->POSITION);
 | |
| #endif
 | |
| 
 | |
| 	minor = uds_minor(dev_m_in);
 | |
| 
 | |
| 	if (uds_fd_table[minor].state != UDS_INUSE) {
 | |
| 
 | |
| 		/* attempted to close a socket that hasn't been opened --
 | |
| 		 * something is very wrong :(
 | |
| 		 */
 | |
| 		uds_set_reply(dev_m_out, DEV_REVIVE, dev_m_in->USER_ENDPT,
 | |
| 			      (cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL);
 | |
| 
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	/* track the system call we are performing in case it gets cancelled */
 | |
| 	uds_fd_table[minor].call_nr = dev_m_in->m_type;
 | |
| 	uds_fd_table[minor].ioctl = 0;
 | |
| 	uds_fd_table[minor].syscall_done = 0;
 | |
| 
 | |
| 	/* Update the process endpoint. */
 | |
| 	uds_fd_table[minor].endpoint = dev_m_in->USER_ENDPT;
 | |
| 
 | |
| 	/* setup select(2) framework */
 | |
| 	uds_fd_table[minor].selecting = 0;
 | |
| 
 | |
| 	/* save I/O Grant info */
 | |
| 	uds_fd_table[minor].io_gr = (cp_grant_id_t) dev_m_in->IO_GRANT;
 | |
| 	uds_fd_table[minor].io_gr_size = dev_m_in->COUNT;
 | |
| 
 | |
| 	bytes = uds_perform_write(minor, dev_m_in->m_source,
 | |
| 					uds_fd_table[minor].io_gr_size, 0);
 | |
| 
 | |
| 	uds_set_reply(dev_m_out, DEV_REVIVE, uds_fd_table[minor].endpoint,
 | |
| 		      uds_fd_table[minor].io_gr, bytes);
 | |
| 
 | |
| 	return bytes;
 | |
| }
 | |
| 
 | |
| PUBLIC int uds_ioctl(message *dev_m_in, message *dev_m_out)
 | |
| {
 | |
| 	int rc, minor;
 | |
| 
 | |
| #if DEBUG == 1
 | |
| 	static int call_count = 0;
 | |
| 	printf("(uds) [%d] uds_ioctl() call_count=%d\n", uds_minor(dev_m_in),
 | |
| 							++call_count);
 | |
| 	printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->USER_ENDPT,
 | |
| 							dev_m_in->POSITION);
 | |
| #endif
 | |
| 
 | |
| 	minor = uds_minor(dev_m_in);
 | |
| 
 | |
| 	if (uds_fd_table[minor].state != UDS_INUSE) {
 | |
| 
 | |
| 		/* attempted to close a socket that hasn't been opened --
 | |
| 		 * something is very wrong :(
 | |
| 		 */
 | |
| 		uds_set_reply(dev_m_out, DEV_REVIVE, dev_m_in->USER_ENDPT,
 | |
| 			      (cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL);
 | |
| 
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	/* track the system call we are performing in case it gets cancelled */
 | |
| 	uds_fd_table[minor].call_nr = dev_m_in->m_type;
 | |
| 	uds_fd_table[minor].ioctl = dev_m_in->COUNT;
 | |
| 	uds_fd_table[minor].syscall_done = 0;
 | |
| 
 | |
| 	/* setup select(2) framework */
 | |
| 	uds_fd_table[minor].selecting = 0;
 | |
| 
 | |
| 	/* update the owner endpoint - yes it's really stored in POSITION */
 | |
| 	uds_fd_table[minor].owner = dev_m_in->POSITION;
 | |
| 
 | |
| 	switch (dev_m_in->COUNT) {	/* Handle the ioctl(2) command */
 | |
| 
 | |
| 		case NWIOSUDSCONN:
 | |
| 
 | |
| 			/* connect to a listening socket -- connect() */
 | |
| 			rc = do_connect(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOSUDSACCEPT:
 | |
| 
 | |
| 			/* accept an incoming connection -- accept() */
 | |
| 			rc = do_accept(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOSUDSBLOG:
 | |
| 
 | |
| 			/* set the backlog_size and put the socket into the
 | |
| 			 * listening state -- listen()
 | |
| 			 */
 | |
| 			rc = do_listen(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOSUDSTYPE:
 | |
| 
 | |
| 			/* set the type for this socket (i.e.
 | |
| 			 * SOCK_STREAM, SOCK_DGRAM, etc) -- socket()
 | |
| 			 */
 | |
| 			rc = do_socket(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOSUDSADDR:
 | |
| 
 | |
| 			/* set the address for this socket -- bind() */
 | |
| 			rc = do_bind(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOGUDSADDR:
 | |
| 
 | |
| 			/* get the address for this socket -- getsockname() */
 | |
| 			rc = do_getsockname(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOGUDSPADDR:
 | |
| 
 | |
| 			/* get the address for the peer -- getpeername() */
 | |
| 			rc = do_getpeername(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOSUDSSHUT:
 | |
| 
 | |
| 			/* shutdown a socket for reading, writing, or
 | |
| 			 * both -- shutdown()
 | |
| 			 */
 | |
| 			rc = do_shutdown(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOSUDSPAIR:
 | |
| 
 | |
| 			/* connect two sockets -- socketpair() */
 | |
| 			rc = do_socketpair(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOSUDSPAIROLD:
 | |
| 
 | |
| 			/* connect two sockets -- socketpair() */
 | |
| 			rc = do_socketpair_old(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOGUDSSOTYPE:
 | |
| 
 | |
| 			/* get socket type -- getsockopt(SO_TYPE) */
 | |
| 			rc = do_getsockopt_sotype(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOGUDSPEERCRED:
 | |
| 
 | |
| 			/* get peer endpoint -- getsockopt(SO_PEERCRED) */
 | |
| 			rc = do_getsockopt_peercred(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOGUDSPEERCREDOLD:
 | |
| 
 | |
| 			/* get peer endpoint -- getsockopt(SO_PEERCRED) */
 | |
| 			rc = do_getsockopt_peercred_old(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOSUDSTADDR:
 | |
| 
 | |
| 			/* set target address -- sendto() */
 | |
| 			rc = do_sendto(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOGUDSFADDR:
 | |
| 
 | |
| 			/* get from address -- recvfrom() */
 | |
| 			rc = do_recvfrom(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOGUDSSNDBUF:
 | |
| 
 | |
| 			/* get the send buffer size -- getsockopt(SO_SNDBUF) */
 | |
| 			rc = do_getsockopt_sndbuf(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOSUDSSNDBUF:
 | |
| 
 | |
| 			/* set the send buffer size -- setsockopt(SO_SNDBUF) */
 | |
| 			rc = do_setsockopt_sndbuf(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOGUDSRCVBUF:
 | |
| 
 | |
| 			/* get the send buffer size -- getsockopt(SO_SNDBUF) */
 | |
| 			rc = do_getsockopt_rcvbuf(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOSUDSRCVBUF:
 | |
| 
 | |
| 			/* set the send buffer size -- setsockopt(SO_SNDBUF) */
 | |
| 			rc = do_setsockopt_rcvbuf(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOSUDSCTRL:
 | |
| 
 | |
| 			/* set the control data -- sendmsg() */
 | |
| 			rc = do_sendmsg(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		case NWIOGUDSCTRL:
 | |
| 
 | |
| 			/* set the control data -- recvmsg() */
 | |
| 			rc = do_recvmsg(dev_m_in, dev_m_out);
 | |
| 
 | |
| 			break;
 | |
| 
 | |
| 		default:
 | |
| 
 | |
| 			/* the IOCTL command is not valid for /dev/uds --
 | |
| 			 * this happens a lot and is normal. a lot of
 | |
| 			 * libc functions determine the socket type with
 | |
| 			 * IOCTLs. Any not for us simply get a EBADIOCTL
 | |
| 			 * response.
 | |
| 			 */
 | |
| 
 | |
| 			rc = EBADIOCTL;
 | |
| 	}
 | |
| 
 | |
| 	if (rc != SUSPEND)
 | |
| 		uds_fd_table[minor].syscall_done = 1;
 | |
| 
 | |
| 	uds_set_reply(dev_m_out, DEV_REVIVE, dev_m_in->USER_ENDPT,
 | |
| 		      (cp_grant_id_t) dev_m_in->IO_GRANT, rc);
 | |
| 
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| PUBLIC int uds_unsuspend(endpoint_t m_source, int minor)
 | |
| {
 | |
| 	int r = OK, bytes;
 | |
| 	message m_out;
 | |
| 	uds_fd_t *fdp;
 | |
| 
 | |
| 	fdp = &uds_fd_table[minor];
 | |
| 
 | |
| 	if (fdp->status_updated == 1) {
 | |
| 
 | |
| 		/* clear the status_updated flag */
 | |
| 		fdp->status_updated = 0;
 | |
| 		fdp->selecting = 0;
 | |
| 
 | |
| 		/* prepare the response */
 | |
| 		uds_sel_reply(&m_out, DEV_SEL_REPL2, minor, fdp->sel_ops_out);
 | |
| 	} else if (fdp->ready_to_revive == 1) {
 | |
| 
 | |
| 		/* clear the ready to revive flag */
 | |
| 		fdp->ready_to_revive = 0;
 | |
| 
 | |
| 		switch (fdp->suspended) {
 | |
| 
 | |
| 			case UDS_SUSPENDED_READ:
 | |
| 
 | |
| 				bytes = uds_perform_read(minor, m_source,
 | |
| 							 fdp->io_gr_size, 0);
 | |
| 
 | |
| 				if (bytes == SUSPEND) {
 | |
| 					r = SUSPEND;
 | |
| 					break;
 | |
| 				}
 | |
| 
 | |
| 				fdp->suspended = UDS_NOT_SUSPENDED;
 | |
| 
 | |
| 				uds_set_reply(&m_out, DEV_REVIVE, fdp->endpoint,
 | |
| 					      fdp->io_gr, bytes);
 | |
| 
 | |
| 				break;
 | |
| 
 | |
| 			case UDS_SUSPENDED_WRITE:
 | |
| 
 | |
| 				bytes = uds_perform_write(minor, m_source,
 | |
| 							  fdp->io_gr_size, 0);
 | |
| 
 | |
| 				if (bytes == SUSPEND) {
 | |
| 					r = SUSPEND;
 | |
| 					break;
 | |
| 				}
 | |
| 
 | |
| 				fdp->suspended = UDS_NOT_SUSPENDED;
 | |
| 
 | |
| 				uds_set_reply(&m_out, DEV_REVIVE, fdp->endpoint,
 | |
| 					      fdp->io_gr, bytes);
 | |
| 
 | |
| 				break;
 | |
| 
 | |
| 			case UDS_SUSPENDED_CONNECT:
 | |
| 			case UDS_SUSPENDED_ACCEPT:
 | |
| 
 | |
| 				/* In both cases, the process
 | |
| 				 * that send the notify()
 | |
| 				 * already performed the connection.
 | |
| 				 * The only thing to do here is
 | |
| 				 * unblock.
 | |
| 				 */
 | |
| 
 | |
| 				fdp->suspended = UDS_NOT_SUSPENDED;
 | |
| 
 | |
| 				uds_set_reply(&m_out, DEV_REVIVE, fdp->endpoint,
 | |
| 					      fdp->io_gr, OK);
 | |
| 
 | |
| 				break;
 | |
| 
 | |
| 			default:
 | |
| 				return(OK);
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	if (r == OK) reply(m_source, &m_out);
 | |
| 	return(r);
 | |
| }
 | |
| 
 | |
| PUBLIC int uds_cancel(message *dev_m_in, message *dev_m_out)
 | |
| {
 | |
| 	int i, j;
 | |
| 	int minor;
 | |
| 	/* XXX: should become a noop? */
 | |
| #if DEBUG == 1
 | |
| 	static int call_count = 0;
 | |
| 	printf("(uds) [%d] uds_cancel() call_count=%d\n", uds_minor(dev_m_in),
 | |
| 							++call_count);
 | |
| 	printf("Endpoint: 0x%x\n", dev_m_in->USER_ENDPT);
 | |
| #endif
 | |
| 
 | |
| 	minor = uds_minor(dev_m_in);
 | |
| 
 | |
| 	if (uds_fd_table[minor].state != UDS_INUSE) {
 | |
| 
 | |
| 		/* attempted to close a socket that hasn't been opened --
 | |
| 		 * something is very wrong :(
 | |
| 		 */
 | |
| 		uds_set_reply(dev_m_out, DEV_NO_STATUS, dev_m_in->USER_ENDPT,
 | |
| 			      (cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL);
 | |
| 
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	/* Update the process endpoint. */
 | |
| 	uds_fd_table[minor].endpoint = dev_m_in->USER_ENDPT;
 | |
| 
 | |
| 	/* setup select(2) framework */
 | |
| 	uds_fd_table[minor].selecting = 0;
 | |
| 
 | |
| 	/* the system call was cancelled, so if the socket was suspended
 | |
| 	 * (which is likely the case), then it is not suspended anymore.
 | |
| 	 */
 | |
| 	uds_fd_table[minor].suspended = UDS_NOT_SUSPENDED;
 | |
| 
 | |
| 	/* If there is a system call and it isn't complete, roll back */
 | |
| 	if (uds_fd_table[minor].call_nr && !uds_fd_table[minor].syscall_done) {
 | |
| 
 | |
| 
 | |
| 		if  (uds_fd_table[minor].call_nr == DEV_IOCTL_S) {
 | |
| 
 | |
| 			switch (uds_fd_table[minor].ioctl) {
 | |
| 
 | |
| 				case NWIOSUDSACCEPT:	/* accept() */
 | |
| 
 | |
| 					/* partial accept() only changes
 | |
| 					 * uds_fd_table[minorparent].child
 | |
| 					 */
 | |
| 
 | |
| 					for (i = 0; i < NR_FDS; i++) {
 | |
| 						if (uds_fd_table[i].child ==
 | |
| 							minor) {
 | |
| 
 | |
| 						uds_fd_table[i].child = -1;
 | |
| 
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					break;
 | |
| 
 | |
| 				case NWIOSUDSCONN:	/* connect() */
 | |
| 
 | |
| 					/* partial connect() sets addr
 | |
| 					 * and adds minor to server backlog
 | |
| 					 */
 | |
| 
 | |
| 					for (i = 0; i < NR_FDS; i++) {
 | |
| 
 | |
| 						/* find a socket that is in
 | |
| 						 * use.
 | |
| 						 */
 | |
| 						if (uds_fd_table[i].state ==
 | |
| 							UDS_INUSE) {
 | |
| 
 | |
| 							/* see if minor is in
 | |
| 							 * the backlog
 | |
| 							 */
 | |
| 			for (j = 0; j < uds_fd_table[i].backlog_size; j++) {
 | |
| 
 | |
| 				if (uds_fd_table[i].backlog[j] == minor) {
 | |
| 
 | |
| 					/* remove from backlog */
 | |
| 					uds_fd_table[i].backlog[j] = -1;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					/* clear the address */
 | |
| 					memset(&(uds_fd_table[minor].addr),
 | |
| 						'\0',
 | |
| 						sizeof(struct sockaddr_un));
 | |
| 
 | |
| 					break;
 | |
| 
 | |
| 				case NWIOSUDSTADDR:	/* sendto() */
 | |
| 				case NWIOSUDSADDR:	/* bind() */
 | |
| 				case NWIOGUDSADDR:	/* getsockname() */
 | |
| 				case NWIOGUDSPADDR:	/* getpeername() */
 | |
| 				case NWIOSUDSTYPE:	/* socket() */
 | |
| 				case NWIOSUDSBLOG:	/* listen() */
 | |
| 				case NWIOSUDSSHUT:	/* shutdown() */
 | |
| 				case NWIOSUDSPAIR:	/* socketpair() */
 | |
| 				case NWIOGUDSSOTYPE:	/* SO_TYPE */
 | |
| 				case NWIOGUDSPEERCRED:	/* SO_PEERCRED */
 | |
| 				default:
 | |
| 					/* these are atomic, never suspend,
 | |
| 					 * and can't be cancelled once called
 | |
| 					 */
 | |
| 					break;
 | |
| 			}
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		/* DEV_READ_S or DEV_WRITE_S don't need to do anything
 | |
| 		 * when cancelled. DEV_OPEN, DEV_REOPEN, DEV_SELECT,
 | |
| 		 * DEV_CLOSE are atomic, never suspend, and can't
 | |
| 		 * be cancelled once called.
 | |
| 		 */
 | |
| 
 | |
| 		uds_fd_table[minor].syscall_done = 1;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	uds_set_reply(dev_m_out, DEV_NO_STATUS, dev_m_in->USER_ENDPT,
 | |
| 			(cp_grant_id_t) dev_m_in->IO_GRANT, EINTR);
 | |
| 
 | |
| 	return EINTR;
 | |
| }
 | 
