Add socketpair syscall
1. Support creating socket pairs of the UNIX domain 2. Add test for socketpair in test/unix_socket 3. Refactor unix_socket test
This commit is contained in:
		
							parent
							
								
									9c4391b32d
								
							
						
					
					
						commit
						dc14f27a29
					
				| @ -2,7 +2,7 @@ use super::*; | |||||||
| use alloc::prelude::ToString; | use alloc::prelude::ToString; | ||||||
| use std::collections::btree_map::BTreeMap; | use std::collections::btree_map::BTreeMap; | ||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::sync::atomic::spin_loop_hint; | use std::sync::atomic::{spin_loop_hint, AtomicUsize, Ordering}; | ||||||
| use std::sync::SgxMutex as Mutex; | use std::sync::SgxMutex as Mutex; | ||||||
| use util::ring_buf::{RingBuf, RingBufReader, RingBufWriter}; | use util::ring_buf::{RingBuf, RingBufReader, RingBufWriter}; | ||||||
| 
 | 
 | ||||||
| @ -88,6 +88,9 @@ impl File for UnixSocketFile { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static SOCKETPAIR_NUM: AtomicUsize = AtomicUsize::new(0); | ||||||
|  | const SOCK_PATH_PREFIX: &str = "socketpair_"; | ||||||
|  | 
 | ||||||
| impl UnixSocketFile { | impl UnixSocketFile { | ||||||
|     pub fn new(socket_type: c_int, protocol: c_int) -> Result<Self> { |     pub fn new(socket_type: c_int, protocol: c_int) -> Result<Self> { | ||||||
|         let inner = UnixSocket::new(socket_type, protocol)?; |         let inner = UnixSocket::new(socket_type, protocol)?; | ||||||
| @ -123,6 +126,34 @@ impl UnixSocketFile { | |||||||
|         let mut inner = self.inner.lock().unwrap(); |         let mut inner = self.inner.lock().unwrap(); | ||||||
|         inner.poll() |         inner.poll() | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     pub fn socketpair(socket_type: i32, protocol: i32) -> Result<(Self, Self)> { | ||||||
|  |         let listen_socket = Self::new(socket_type, protocol)?; | ||||||
|  |         let bound_path = listen_socket.bind_until_success(); | ||||||
|  |         listen_socket.listen()?; | ||||||
|  | 
 | ||||||
|  |         let client_socket = Self::new(socket_type, protocol)?; | ||||||
|  |         client_socket.connect(&bound_path)?; | ||||||
|  | 
 | ||||||
|  |         let accepted_socket = listen_socket.accept()?; | ||||||
|  |         Ok((client_socket, accepted_socket)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn bind_until_success(&self) -> String { | ||||||
|  |         let mut path = SOCK_PATH_PREFIX.to_string(); | ||||||
|  |         let mut index = SOCKETPAIR_NUM.load(Ordering::SeqCst); | ||||||
|  |         path.push_str(&index.to_string()); | ||||||
|  |         while self.bind(&path).is_err() { | ||||||
|  |             if index == std::usize::MAX { | ||||||
|  |                 SOCKETPAIR_NUM.store(0, Ordering::SeqCst); //flip SOCKETPAIR_NUM
 | ||||||
|  |             } | ||||||
|  |             index += 1; | ||||||
|  |             path = SOCK_PATH_PREFIX.to_string(); | ||||||
|  |             path.push_str(&index.to_string()); | ||||||
|  |         } | ||||||
|  |         SOCKETPAIR_NUM.fetch_max(index + 1, Ordering::SeqCst); | ||||||
|  |         path | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Debug for UnixSocketFile { | impl Debug for UnixSocketFile { | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ | |||||||
| #![feature(range_contains)] | #![feature(range_contains)] | ||||||
| #![feature(core_intrinsics)] | #![feature(core_intrinsics)] | ||||||
| #![feature(stmt_expr_attributes)] | #![feature(stmt_expr_attributes)] | ||||||
|  | #![feature(atomic_min_max)] | ||||||
| 
 | 
 | ||||||
| #[macro_use] | #[macro_use] | ||||||
| extern crate alloc; | extern crate alloc; | ||||||
|  | |||||||
| @ -295,6 +295,12 @@ pub extern "C" fn dispatch_syscall( | |||||||
|             arg4 as *mut libc::sockaddr, |             arg4 as *mut libc::sockaddr, | ||||||
|             arg5 as *mut libc::socklen_t, |             arg5 as *mut libc::socklen_t, | ||||||
|         ), |         ), | ||||||
|  |         SYS_SOCKETPAIR => do_socketpair( | ||||||
|  |             arg0 as c_int, | ||||||
|  |             arg1 as c_int, | ||||||
|  |             arg2 as c_int, | ||||||
|  |             arg3 as *mut c_int, | ||||||
|  |         ), | ||||||
| 
 | 
 | ||||||
|         _ => do_unknown(num, arg0, arg1, arg2, arg3, arg4, arg5), |         _ => do_unknown(num, arg0, arg1, arg2, arg3, arg4, arg5), | ||||||
|     }; |     }; | ||||||
| @ -1026,7 +1032,7 @@ fn do_sched_setaffinity(pid: pid_t, cpusize: size_t, buf: *const c_uchar) -> Res | |||||||
| 
 | 
 | ||||||
| fn do_socket(domain: c_int, socket_type: c_int, protocol: c_int) -> Result<isize> { | fn do_socket(domain: c_int, socket_type: c_int, protocol: c_int) -> Result<isize> { | ||||||
|     info!( |     info!( | ||||||
|         "socket: domain: {}, socket_type: {}, protocol: {}", |         "socket: domain: {}, socket_type: 0x{:x}, protocol: {}", | ||||||
|         domain, socket_type, protocol |         domain, socket_type, protocol | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
| @ -1320,6 +1326,46 @@ fn do_recvfrom( | |||||||
|     Ok(ret as isize) |     Ok(ret as isize) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn do_socketpair( | ||||||
|  |     domain: c_int, | ||||||
|  |     socket_type: c_int, | ||||||
|  |     protocol: c_int, | ||||||
|  |     sv: *mut c_int, | ||||||
|  | ) -> Result<isize> { | ||||||
|  |     info!( | ||||||
|  |         "socketpair: domain: {}, type:0x{:x}, protocol: {}", | ||||||
|  |         domain, socket_type, protocol | ||||||
|  |     ); | ||||||
|  |     let mut sock_pair = unsafe { | ||||||
|  |         check_mut_array(sv, 2)?; | ||||||
|  |         std::slice::from_raw_parts_mut(sv as *mut u32, 2) | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     if (domain == libc::AF_UNIX) { | ||||||
|  |         let (client_socket, server_socket) = | ||||||
|  |             UnixSocketFile::socketpair(socket_type as i32, protocol as i32)?; | ||||||
|  |         let current_ref = process::get_current(); | ||||||
|  |         let mut proc = current_ref.lock().unwrap(); | ||||||
|  |         sock_pair[0] = proc | ||||||
|  |             .get_files() | ||||||
|  |             .lock() | ||||||
|  |             .unwrap() | ||||||
|  |             .put(Arc::new(Box::new(client_socket)), false); | ||||||
|  |         sock_pair[1] = proc | ||||||
|  |             .get_files() | ||||||
|  |             .lock() | ||||||
|  |             .unwrap() | ||||||
|  |             .put(Arc::new(Box::new(server_socket)), false); | ||||||
|  | 
 | ||||||
|  |         info!("socketpair: ({}, {})", sock_pair[0], sock_pair[1]); | ||||||
|  |         Ok(0) | ||||||
|  |     } else if (domain == libc::AF_TIPC) { | ||||||
|  |         return_errno!(EAFNOSUPPORT, "cluster domain sockets not supported") | ||||||
|  |     } else { | ||||||
|  |         return_errno!(EAFNOSUPPORT, "domain not supported") | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| fn do_select( | fn do_select( | ||||||
|     nfds: c_int, |     nfds: c_int, | ||||||
|     readfds: *mut libc::fd_set, |     readfds: *mut libc::fd_set, | ||||||
|  | |||||||
| @ -9,106 +9,182 @@ | |||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <spawn.h> | #include <spawn.h> | ||||||
| 
 | 
 | ||||||
| const char SOCK_PATH[] = "echo_socket"; | #include "test.h" | ||||||
| 
 | 
 | ||||||
| int create_server_socket() { | #define ECHO_MSG "echo msg for unix_socket test" | ||||||
| 	int fd = socket(AF_UNIX, SOCK_STREAM, 0); | #define THROW_ERROR(msg)    do {                      \ | ||||||
| 	if (fd == -1) { |     printf("\t\tERROR: %s in func %s at line %d of file %s\n",  \ | ||||||
| 		printf("ERROR: failed to create a unix socket\n"); |            (msg), __func__, __LINE__, __FILE__);            \ | ||||||
| 		return -1; |     return -1;                                              \ | ||||||
| 	} | } while(0) | ||||||
| 
 | 
 | ||||||
| 	struct sockaddr_un local; | int create_connected_sockets(int *sockets, char *sock_path) { | ||||||
| 	local.sun_family = AF_UNIX; |     int listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); | ||||||
| 	strcpy(local.sun_path, SOCK_PATH); |     if (listen_fd == -1) { | ||||||
| 	socklen_t len = strlen(local.sun_path) + sizeof(local.sun_family); |         THROW_ERROR("failed to create a unix socket"); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| 	if (bind(fd, (struct sockaddr *)&local, len) == -1) { |     struct sockaddr_un addr; | ||||||
| 		printf("ERROR: failed to bind\n"); |     memset(&addr, 0, sizeof(struct sockaddr_un)); //Clear structure
 | ||||||
| 		return -1; |     addr.sun_family = AF_UNIX; | ||||||
| 	} |     strcpy(addr.sun_path, sock_path); | ||||||
|  |     socklen_t addr_len = strlen(addr.sun_path) + sizeof(addr.sun_family); | ||||||
|  |     if (bind(listen_fd, (struct sockaddr *)&addr, addr_len) == -1) { | ||||||
|  |         close(listen_fd); | ||||||
|  |         THROW_ERROR("failed to bind"); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| 	if (listen(fd, 5) == -1) { |     if (listen(listen_fd, 5) == -1) { | ||||||
| 		printf("ERROR: failed to listen\n"); |         close(listen_fd); | ||||||
| 		return -1; |         THROW_ERROR("failed to listen"); | ||||||
| 	} |     } | ||||||
| 	return fd; | 
 | ||||||
|  |     int client_fd = socket(AF_UNIX, SOCK_STREAM, 0); | ||||||
|  |     if (client_fd == -1) { | ||||||
|  |         close(listen_fd); | ||||||
|  |         THROW_ERROR("failed to create a unix socket"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (connect(client_fd, (struct sockaddr *)&addr, addr_len) == -1) { | ||||||
|  |         close(listen_fd); | ||||||
|  |         close(client_fd); | ||||||
|  |         THROW_ERROR("failed to connect"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int accepted_fd = accept(listen_fd, (struct sockaddr *)&addr, &addr_len); | ||||||
|  |     if (accepted_fd == -1) { | ||||||
|  |         close(listen_fd); | ||||||
|  |         close(client_fd); | ||||||
|  |         THROW_ERROR("failed to accept socket"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sockets[0] = client_fd; | ||||||
|  |     sockets[1] = accepted_fd; | ||||||
|  |     close(listen_fd); | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int create_client_socket() { | int create_connceted_sockets_default(int *sockets) { | ||||||
| 	int fd = socket(AF_UNIX, SOCK_STREAM, 0); |     return create_connected_sockets(sockets, "unix_socket_default_path"); | ||||||
| 	if (fd == -1) { |  | ||||||
| 		printf("ERROR: failed to create a unix socket\n"); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	struct sockaddr_un remote; |  | ||||||
| 	remote.sun_family = AF_UNIX; |  | ||||||
| 	strcpy(remote.sun_path, SOCK_PATH); |  | ||||||
| 	socklen_t len = strlen(remote.sun_path) + sizeof(remote.sun_family); |  | ||||||
| 
 |  | ||||||
| 	if (connect(fd, (struct sockaddr *)&remote, len) == -1) { |  | ||||||
| 		printf("ERROR: failed to connect\n"); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
| 	return fd; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int main(int argc, const char* argv[]) { | int verify_child_echo(int *connected_sockets) { | ||||||
| 	int listen_fd = create_server_socket(); |  | ||||||
| 	if (listen_fd == -1) { |  | ||||||
| 		printf("ERROR: failed to create server socket\n"); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	int socket_rd_fd = create_client_socket(); |  | ||||||
| 	if (socket_rd_fd == -1) { |  | ||||||
| 		printf("ERROR: failed to create client socket\n"); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	struct sockaddr_un remote; |  | ||||||
| 	socklen_t len = sizeof(remote); |  | ||||||
| 	int socket_wr_fd = accept(listen_fd, (struct sockaddr *)&remote, &len); |  | ||||||
| 	if (socket_wr_fd == -1) { |  | ||||||
| 		printf("ERROR: failed to accept socket\n"); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// The following is same as 'pipe'
 |  | ||||||
| 
 |  | ||||||
|     posix_spawn_file_actions_t file_actions; |  | ||||||
|     posix_spawn_file_actions_init(&file_actions); |  | ||||||
|     posix_spawn_file_actions_adddup2(&file_actions, socket_wr_fd, STDOUT_FILENO); |  | ||||||
|     posix_spawn_file_actions_addclose(&file_actions, socket_rd_fd); |  | ||||||
| 
 |  | ||||||
|     const char* msg = "Echo!\n"; |  | ||||||
|     const char* child_prog = "/bin/hello_world"; |     const char* child_prog = "/bin/hello_world"; | ||||||
|     const char* child_argv[3] = { child_prog, msg, NULL }; |     const char* child_argv[3] = { child_prog, ECHO_MSG, NULL }; | ||||||
|     int child_pid; |     int child_pid; | ||||||
|  |     posix_spawn_file_actions_t file_actions; | ||||||
|  | 
 | ||||||
|  |     posix_spawn_file_actions_init(&file_actions); | ||||||
|  |     posix_spawn_file_actions_adddup2(&file_actions, connected_sockets[0], STDOUT_FILENO); | ||||||
|  |     posix_spawn_file_actions_addclose(&file_actions, connected_sockets[1]); | ||||||
|  | 
 | ||||||
|     if (posix_spawn(&child_pid, child_prog, &file_actions, |     if (posix_spawn(&child_pid, child_prog, &file_actions, | ||||||
|             NULL, (char*const*)child_argv, NULL) < 0) { |             NULL, (char*const*)child_argv, NULL) < 0) { | ||||||
|         printf("ERROR: failed to spawn a child process\n"); |         THROW_ERROR("failed to spawn a child process"); | ||||||
|         return -1; |  | ||||||
|     } |     } | ||||||
|     close(socket_wr_fd); |  | ||||||
| 
 | 
 | ||||||
|     const char* expected_str = msg; |  | ||||||
|     size_t expected_len = strlen(expected_str); |  | ||||||
|     char actual_str[32] = {0}; |     char actual_str[32] = {0}; | ||||||
|     ssize_t actual_len; |     ssize_t actual_len; | ||||||
|  |     //TODO: implement blocking read
 | ||||||
|     do { |     do { | ||||||
|         actual_len = read(socket_rd_fd, actual_str, sizeof(actual_str) - 1); |         actual_len = read(connected_sockets[1], actual_str, 32); | ||||||
|     } while (actual_len == 0); |     } while (actual_len == 0); | ||||||
|     if (strncmp(expected_str, actual_str, expected_len) != 0) { |     if (strncmp(actual_str, ECHO_MSG, sizeof(ECHO_MSG) - 1) != 0) { | ||||||
|         printf("ERROR: received string is not as expected\n"); |         printf("data read is :%s\n", actual_str); | ||||||
|         return -1; |         THROW_ERROR("received string is not as expected"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     int status = 0; |     int status = 0; | ||||||
|     if (wait4(child_pid, &status, 0, NULL) < 0) { |     if (wait4(child_pid, &status, 0, NULL) < 0) { | ||||||
|         printf("ERROR: failed to wait4 the child process\n"); |         THROW_ERROR("failed to wait4 the child process"); | ||||||
|         return -1; |     } | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int verify_connection(int src_sock, int dest_sock) { | ||||||
|  |     char buf[1024]; | ||||||
|  |     int i; | ||||||
|  |     for (i = 0; i < 100; i++) { | ||||||
|  |         if (write(src_sock, ECHO_MSG, sizeof(ECHO_MSG)) < 0) { | ||||||
|  |             THROW_ERROR("writing server message"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (read(dest_sock, buf, 1024) < 0) { | ||||||
|  |             THROW_ERROR("reading server message"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (strncmp(buf, ECHO_MSG, sizeof(ECHO_MSG)) != 0) { | ||||||
|  |             THROW_ERROR("msg received mismatch"); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | //this value should not be too large as one pair consumes 2MB memory
 | ||||||
|  | #define PAIR_NUM 15 | ||||||
|  | 
 | ||||||
|  | int test_multiple_socketpairs() { | ||||||
|  |     int sockets[PAIR_NUM][2]; | ||||||
|  |     int i; | ||||||
|  |     int ret = 0; | ||||||
|  | 
 | ||||||
|  |     for(i = 0; i < PAIR_NUM; i++) { | ||||||
|  |         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets[i]) < 0) { | ||||||
|  |             THROW_ERROR("opening stream socket pair"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(verify_connection(sockets[i][0], sockets[i][1]) < 0) { | ||||||
|  |             ret = -1; | ||||||
|  |             goto cleanup; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if(verify_connection(sockets[i][1], sockets[i][0]) < 0) { | ||||||
|  |             ret = -1; | ||||||
|  |             goto cleanup; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     i--; | ||||||
|  | cleanup: | ||||||
|  |     for(; i >= 0; i--){ | ||||||
|  |         close(sockets[i][0]); | ||||||
|  |         close(sockets[i][1]); | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int socketpair_default(int *sockets) { | ||||||
|  |     return socketpair(AF_UNIX, SOCK_STREAM, 0, sockets); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | typedef int(*create_connection_func_t)(int *); | ||||||
|  | int test_connected_sockets_inter_process(create_connection_func_t fn) { | ||||||
|  |     int ret = 0; | ||||||
|  |     int sockets[2]; | ||||||
|  |     if (fn(sockets) < 0) | ||||||
|  |         return -1; | ||||||
|  | 
 | ||||||
|  |     ret = verify_child_echo(sockets); | ||||||
|  | 
 | ||||||
|  |     close(sockets[0]); | ||||||
|  |     close(sockets[1]); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int test_unix_socket_inter_process() { | ||||||
|  |     return test_connected_sockets_inter_process(socketpair_default); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int test_socketpair_inter_process() { | ||||||
|  |     return test_connected_sockets_inter_process(create_connceted_sockets_default); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static test_case_t test_cases[] = { | ||||||
|  |     TEST_CASE(test_unix_socket_inter_process), | ||||||
|  |     TEST_CASE(test_socketpair_inter_process), | ||||||
|  |     TEST_CASE(test_multiple_socketpairs), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | int main(int argc, const char* argv[]) { | ||||||
|  |     return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user