예제 #1
0
/** Make a fast asynchronous call over IPC.
 *
 * This function can only handle four arguments of payload, but is faster than
 * the generic function sys_ipc_call_async_slow().
 *
 * @param phoneid Phone handle for the call.
 * @param imethod Interface and method of the call.
 * @param arg1    Service-defined payload argument.
 * @param arg2    Service-defined payload argument.
 * @param arg3    Service-defined payload argument.
 * @param arg4    Service-defined payload argument.
 *
 * @return Call hash on success.
 * @return IPC_CALLRET_FATAL in case of a fatal error.
 * @return IPC_CALLRET_TEMPORARY if there are too many pending
 *         asynchronous requests; answers should be handled first.
 *
 */
sysarg_t sys_ipc_call_async_fast(sysarg_t phoneid, sysarg_t imethod,
    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
{
	phone_t *phone;
	if (phone_get(phoneid, &phone) != EOK)
		return IPC_CALLRET_FATAL;
	
	if (check_call_limit(phone))
		return IPC_CALLRET_TEMPORARY;
	
	call_t *call = ipc_call_alloc(0);
	IPC_SET_IMETHOD(call->data, imethod);
	IPC_SET_ARG1(call->data, arg1);
	IPC_SET_ARG2(call->data, arg2);
	IPC_SET_ARG3(call->data, arg3);
	IPC_SET_ARG4(call->data, arg4);
	
	/*
	 * To achieve deterministic behavior, zero out arguments that are beyond
	 * the limits of the fast version.
	 */
	IPC_SET_ARG5(call->data, 0);
	
	int res = request_preprocess(call, phone);
	
	if (!res)
		ipc_call(phone, call);
	else
		ipc_backsend_err(phone, call, res);
	
	return (sysarg_t) call;
}
예제 #2
0
/** Answer an IPC call - fast version.
 *
 * This function can handle only two return arguments of payload, but is faster
 * than the generic sys_ipc_answer().
 *
 * @param callid Hash of the call to be answered.
 * @param retval Return value of the answer.
 * @param arg1   Service-defined return value.
 * @param arg2   Service-defined return value.
 * @param arg3   Service-defined return value.
 * @param arg4   Service-defined return value.
 *
 * @return 0 on success, otherwise an error code.
 *
 */
sysarg_t sys_ipc_answer_fast(sysarg_t callid, sysarg_t retval,
    sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
{
	/* Do not answer notification callids */
	if (callid & IPC_CALLID_NOTIFICATION)
		return 0;
	
	call_t *call = get_call(callid);
	if (!call)
		return ENOENT;
	
	ipc_data_t saved_data;
	bool saved;
	
	if (answer_need_old(call)) {
		memcpy(&saved_data, &call->data, sizeof(call->data));
		saved = true;
	} else
		saved = false;
	
	IPC_SET_RETVAL(call->data, retval);
	IPC_SET_ARG1(call->data, arg1);
	IPC_SET_ARG2(call->data, arg2);
	IPC_SET_ARG3(call->data, arg3);
	IPC_SET_ARG4(call->data, arg4);
	
	/*
	 * To achieve deterministic behavior, zero out arguments that are beyond
	 * the limits of the fast version.
	 */
	IPC_SET_ARG5(call->data, 0);
	int rc = answer_preprocess(call, saved ? &saved_data : NULL);
	
	ipc_answer(&TASK->answerbox, call);
	return rc;
}
예제 #3
0
static void tcp_sock_send(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
{
	int socket_id;
	int fragments;
	int index;
	socket_core_t *sock_core;
	tcp_sockdata_t *socket;
	ipc_call_t answer;
	ipc_callid_t wcallid;
	size_t length;
	uint8_t buffer[TCP_SOCK_FRAGMENT_SIZE];
	tcp_error_t trc;
	int rc;

	log_msg(LVL_DEBUG, "tcp_sock_send()");
	socket_id = SOCKET_GET_SOCKET_ID(call);
	fragments = SOCKET_GET_DATA_FRAGMENTS(call);
	SOCKET_GET_FLAGS(call);

	sock_core = socket_cores_find(&client->sockets, socket_id);
	if (sock_core == NULL) {
		async_answer_0(callid, ENOTSOCK);
		return;
	}

	socket = (tcp_sockdata_t *)sock_core->specific_data;
	fibril_mutex_lock(&socket->lock);

	if (socket->conn == NULL) {
		fibril_mutex_unlock(&socket->lock);
		async_answer_0(callid, ENOTCONN);
		return;
	}

	for (index = 0; index < fragments; index++) {
		if (!async_data_write_receive(&wcallid, &length)) {
			fibril_mutex_unlock(&socket->lock);
			async_answer_0(callid, EINVAL);
			return;
		}

		if (length > TCP_SOCK_FRAGMENT_SIZE)
			length = TCP_SOCK_FRAGMENT_SIZE;

		rc = async_data_write_finalize(wcallid, buffer, length);
		if (rc != EOK) {
			fibril_mutex_unlock(&socket->lock);
			async_answer_0(callid, rc);
			return;
		}

		trc = tcp_uc_send(socket->conn, buffer, length, 0);

		switch (trc) {
		case TCP_EOK:
			rc = EOK;
			break;
		case TCP_ENOTEXIST:
			rc = ENOTCONN;
			break;
		case TCP_ECLOSING:
			rc = ENOTCONN;
			break;
		case TCP_ERESET:
			rc = ECONNABORTED;
			break;
		default:
			assert(false);
		}

		if (rc != EOK) {
			fibril_mutex_unlock(&socket->lock);
			async_answer_0(callid, rc);
			return;
		}
	}

	IPC_SET_ARG1(answer, 0);
	SOCKET_SET_DATA_FRAGMENT_SIZE(answer, TCP_SOCK_FRAGMENT_SIZE);
	async_answer_2(callid, EOK, IPC_GET_ARG1(answer),
	    IPC_GET_ARG2(answer));
	fibril_mutex_unlock(&socket->lock);
}
예제 #4
0
static void udp_sock_sendto(udp_client_t *client, ipc_callid_t callid, ipc_call_t call)
{
	int socket_id;
	int fragments;
	int index;
	struct sockaddr_in *addr;
	size_t addr_size;
	socket_core_t *sock_core;
	udp_sockdata_t *socket;
	udp_sock_t fsock, *fsockp;
	ipc_call_t answer;
	ipc_callid_t wcallid;
	size_t length;
	uint8_t buffer[UDP_FRAGMENT_SIZE];
	udp_error_t urc;
	int rc;

	log_msg(LVL_DEBUG, "udp_sock_send()");

	addr = NULL;

	if (IPC_GET_IMETHOD(call) == NET_SOCKET_SENDTO) {
		rc = async_data_write_accept((void **) &addr, false,
		    0, 0, 0, &addr_size);
		if (rc != EOK) {
			async_answer_0(callid, rc);
			goto out;
		}

		if (addr_size != sizeof(struct sockaddr_in)) {
			async_answer_0(callid, EINVAL);
			goto out;
		}

		fsock.addr.ipv4 = uint32_t_be2host(addr->sin_addr.s_addr);
		fsock.port = uint16_t_be2host(addr->sin_port);
		fsockp = &fsock;
	} else {
		fsockp = NULL;
	}

	socket_id = SOCKET_GET_SOCKET_ID(call);
	fragments = SOCKET_GET_DATA_FRAGMENTS(call);
	SOCKET_GET_FLAGS(call);

	sock_core = socket_cores_find(&client->sockets, socket_id);
	if (sock_core == NULL) {
		async_answer_0(callid, ENOTSOCK);
		goto out;
	}

	if (sock_core->port == 0) {
		/* Implicitly bind socket to port */
		rc = socket_bind(&client->sockets, &gsock, SOCKET_GET_SOCKET_ID(call),
		    addr, addr_size, UDP_FREE_PORTS_START, UDP_FREE_PORTS_END,
		    last_used_port);
		if (rc != EOK) {
			async_answer_0(callid, rc);
			goto out;
		}
	}

	socket = (udp_sockdata_t *)sock_core->specific_data;
	fibril_mutex_lock(&socket->lock);

	if (socket->assoc->ident.local.addr.ipv4 == UDP_IPV4_ANY) {
		/* Determine local IP address */
		inet_addr_t loc_addr, rem_addr;

		rem_addr.ipv4 = fsockp ? fsock.addr.ipv4 :
		    socket->assoc->ident.foreign.addr.ipv4;

		rc = inet_get_srcaddr(&rem_addr, 0, &loc_addr);
		if (rc != EOK) {
			fibril_mutex_unlock(&socket->lock);
			async_answer_0(callid, rc);
			log_msg(LVL_DEBUG, "udp_sock_sendto: Failed to "
			    "determine local address.");
			return;
		}

		socket->assoc->ident.local.addr.ipv4 = loc_addr.ipv4;
		log_msg(LVL_DEBUG, "Local IP address is %x",
		    socket->assoc->ident.local.addr.ipv4);
	}


	assert(socket->assoc != NULL);

	for (index = 0; index < fragments; index++) {
		if (!async_data_write_receive(&wcallid, &length)) {
			fibril_mutex_unlock(&socket->lock);
			async_answer_0(callid, EINVAL);
			goto out;
		}

		if (length > UDP_FRAGMENT_SIZE)
			length = UDP_FRAGMENT_SIZE;

		rc = async_data_write_finalize(wcallid, buffer, length);
		if (rc != EOK) {
			fibril_mutex_unlock(&socket->lock);
			async_answer_0(callid, rc);
			goto out;
		}

		urc = udp_uc_send(socket->assoc, fsockp, buffer, length, 0);

		switch (urc) {
		case UDP_EOK:
			rc = EOK;
			break;
/*		case TCP_ENOTEXIST:
			rc = ENOTCONN;
			break;
		case TCP_ECLOSING:
			rc = ENOTCONN;
			break;
		case TCP_ERESET:
			rc = ECONNABORTED;
			break;*/
		default:
			assert(false);
		}

		if (rc != EOK) {
			fibril_mutex_unlock(&socket->lock);
			async_answer_0(callid, rc);
			goto out;
		}
	}
	
	IPC_SET_ARG1(answer, 0);
	SOCKET_SET_DATA_FRAGMENT_SIZE(answer, UDP_FRAGMENT_SIZE);
	async_answer_2(callid, EOK, IPC_GET_ARG1(answer),
	    IPC_GET_ARG2(answer));
	fibril_mutex_unlock(&socket->lock);
	
out:
	if (addr != NULL)
		free(addr);
}
예제 #5
0
/** Forward a received call to another destination
 *
 * Common code for both the fast and the slow version.
 *
 * @param callid  Hash of the call to forward.
 * @param phoneid Phone handle to use for forwarding.
 * @param imethod New interface and method to use for the forwarded call.
 * @param arg1    New value of the first argument for the forwarded call.
 * @param arg2    New value of the second argument for the forwarded call.
 * @param arg3    New value of the third argument for the forwarded call.
 * @param arg4    New value of the fourth argument for the forwarded call.
 * @param arg5    New value of the fifth argument for the forwarded call.
 * @param mode    Flags that specify mode of the forward operation.
 * @param slow    If true, arg3, arg4 and arg5 are considered. Otherwise
 *                the function considers only the fast version arguments:
 *                i.e. arg1 and arg2.
 *
 * @return 0 on succes, otherwise an error code.
 *
 * Warning: Make sure that ARG5 is not rewritten for certain system IPC
 *
 */
static sysarg_t sys_ipc_forward_common(sysarg_t callid, sysarg_t phoneid,
    sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
    sysarg_t arg4, sysarg_t arg5, unsigned int mode, bool slow)
{
	call_t *call = get_call(callid);
	phone_t *phone;
	bool need_old = answer_need_old(call);
	bool after_forward = false;
	ipc_data_t old;
	int rc;

	if (!call)
		return ENOENT;

	if (need_old)
		old = call->data;
	
	if (phone_get(phoneid, &phone) != EOK) {
		rc = ENOENT;
		goto error;
	}
	
	if (!method_is_forwardable(IPC_GET_IMETHOD(call->data))) {
		rc = EPERM;
		goto error;
	}

	call->flags |= IPC_CALL_FORWARDED;
	
	/*
	 * User space is not allowed to change interface and method of system
	 * methods on forward, allow changing ARG1, ARG2, ARG3 and ARG4 by
	 * means of imethod, arg1, arg2 and arg3.
	 * If the interface and method is immutable, don't change anything.
	 */
	if (!method_is_immutable(IPC_GET_IMETHOD(call->data))) {
		if (method_is_system(IPC_GET_IMETHOD(call->data))) {
			if (IPC_GET_IMETHOD(call->data) == IPC_M_CONNECT_TO_ME)
				phone_dealloc(IPC_GET_ARG5(call->data));
			
			IPC_SET_ARG1(call->data, imethod);
			IPC_SET_ARG2(call->data, arg1);
			IPC_SET_ARG3(call->data, arg2);
			
			if (slow)
				IPC_SET_ARG4(call->data, arg3);
			
			/*
			 * For system methods we deliberately don't
			 * overwrite ARG5.
			 */
		} else {
			IPC_SET_IMETHOD(call->data, imethod);
			IPC_SET_ARG1(call->data, arg1);
			IPC_SET_ARG2(call->data, arg2);
			if (slow) {
				IPC_SET_ARG3(call->data, arg3);
				IPC_SET_ARG4(call->data, arg4);
				IPC_SET_ARG5(call->data, arg5);
			}
		}
	}
	
	rc = ipc_forward(call, phone, &TASK->answerbox, mode);
	if (rc != EOK) {
		after_forward = true;
		goto error;
	}

	return EOK;

error:
	IPC_SET_RETVAL(call->data, EFORWARD);
	(void) answer_preprocess(call, need_old ? &old : NULL);
	if (after_forward)
		_ipc_answer_free_call(call, false);
	else
		ipc_answer(&TASK->answerbox, call);

	return rc;
}