/** 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; }
/** 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; }
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); }
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); }
/** 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; }