/** Wait for an incoming IPC call or an answer. * * @param calldata Pointer to buffer where the call/answer data is stored. * @param usec Timeout. See waitq_sleep_timeout() for explanation. * @param flags Select mode of sleep operation. See waitq_sleep_timeout() * for explanation. * * @return Hash of the call. * If IPC_CALLID_NOTIFICATION bit is set in the hash, the * call is a notification. IPC_CALLID_ANSWERED denotes an * answer. * */ sysarg_t sys_ipc_wait_for_call(ipc_data_t *calldata, uint32_t usec, unsigned int flags) { call_t *call; restart: #ifdef CONFIG_UDEBUG udebug_stoppable_begin(); #endif call = ipc_wait_for_call(&TASK->answerbox, usec, flags | SYNCH_FLAGS_INTERRUPTIBLE); #ifdef CONFIG_UDEBUG udebug_stoppable_end(); #endif if (!call) return 0; if (call->flags & IPC_CALL_NOTIF) { /* Set in_phone_hash to the interrupt counter */ call->data.phone = (void *) call->priv; STRUCT_TO_USPACE(calldata, &call->data); ipc_call_free(call); return ((sysarg_t) call) | IPC_CALLID_NOTIFICATION; } if (call->flags & IPC_CALL_ANSWERED) { process_answer(call); if (call->flags & IPC_CALL_DISCARD_ANSWER) { ipc_call_free(call); goto restart; } STRUCT_TO_USPACE(&calldata->args, &call->data.args); ipc_call_free(call); return ((sysarg_t) call) | IPC_CALLID_ANSWERED; } if (process_request(&TASK->answerbox, call)) goto restart; /* Include phone address('id') of the caller in the request, * copy whole call->data, not only call->data.args */ if (STRUCT_TO_USPACE(calldata, &call->data)) { /* * The callee will not receive this call and no one else has * a chance to answer it. Reply with the EPARTY error code. */ 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, EPARTY); (void) answer_preprocess(call, saved ? &saved_data : NULL); ipc_answer(&TASK->answerbox, call); return 0; } return (sysarg_t) call; }
/** Wait for all answers to asynchronous calls to arrive. */ static void ipc_wait_for_all_answered_calls(void) { call_t *call; size_t i; restart: /* * Go through all phones, until they are all free. * Locking is needed as there may be connection handshakes in progress. */ for (i = 0; i < IPC_MAX_PHONES; i++) { phone_t *phone = &TASK->phones[i]; mutex_lock(&phone->lock); if ((phone->state == IPC_PHONE_HUNGUP) && (atomic_get(&phone->active_calls) == 0)) { phone->state = IPC_PHONE_FREE; phone->callee = NULL; } /* * We might have had some IPC_PHONE_CONNECTING phones at the * beginning of ipc_cleanup(). Depending on whether these were * forgotten or answered, they will eventually enter the * IPC_PHONE_FREE or IPC_PHONE_CONNECTED states, respectively. * In the latter case, the other side may slam the open phones * at any time, in which case we will get an IPC_PHONE_SLAMMED * phone. */ if ((phone->state == IPC_PHONE_CONNECTED) || (phone->state == IPC_PHONE_SLAMMED)) { mutex_unlock(&phone->lock); ipc_phone_hangup(phone); /* * Now there may be one extra active call, which needs * to be forgotten. */ ipc_forget_all_active_calls(); goto restart; } /* * If the hangup succeeded, it has sent a HANGUP message, the * IPC is now in HUNGUP state, we wait for the reply to come */ if (phone->state != IPC_PHONE_FREE) { mutex_unlock(&phone->lock); break; } mutex_unlock(&phone->lock); } /* Got into cleanup */ if (i == IPC_MAX_PHONES) return; call = ipc_wait_for_call(&TASK->answerbox, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE); ASSERT(call->flags & (IPC_CALL_ANSWERED | IPC_CALL_NOTIF)); SYSIPC_OP(answer_process, call); ipc_call_free(call); goto restart; }
int main(int argc, char **argv) { printf("%s: HelenOS IPC Naming Service\n", NAME); int rc = service_init(); if (rc != EOK) return rc; rc = clonable_init(); if (rc != EOK) return rc; rc = task_init(); if (rc != EOK) return rc; printf("%s: Accepting connections\n", NAME); while (true) { process_pending_conn(); process_pending_wait(); ipc_call_t call; ipc_callid_t callid = ipc_wait_for_call(&call); task_id_t id; sysarg_t retval; switch (IPC_GET_IMETHOD(call)) { case IPC_M_PHONE_HUNGUP: retval = ns_task_disconnect(&call); break; case IPC_M_CONNECT_TO_ME: /* * Server requests service registration. */ if (service_clonable(IPC_GET_ARG1(call))) { register_clonable(IPC_GET_ARG1(call), IPC_GET_ARG5(call), &call, callid); continue; } else { retval = register_service(IPC_GET_ARG1(call), IPC_GET_ARG5(call), &call); } break; case IPC_M_CONNECT_ME_TO: /* * Client requests to be connected to a service. */ if (service_clonable(IPC_GET_ARG1(call))) { connect_to_clonable(IPC_GET_ARG1(call), &call, callid); continue; } else { connect_to_service(IPC_GET_ARG1(call), &call, callid); continue; } break; case NS_PING: retval = EOK; break; case NS_TASK_WAIT: id = (task_id_t) MERGE_LOUP32(IPC_GET_ARG1(call), IPC_GET_ARG2(call)); wait_for_task(id, &call, callid); continue; case NS_ID_INTRO: retval = ns_task_id_intro(&call); break; case NS_RETVAL: retval = ns_task_retval(&call); break; default: retval = ENOENT; break; } if (!(callid & IPC_CALLID_NOTIFICATION)) ipc_answer_0(callid, retval); } /* Not reached */ return 0; }