/** ======================================================================== * ========================================================================= */ PslError psl_inet_make_fd_non_blocking(int const fd, const char* const userLabel, const void* const cookie) { /// Make it non-blocking int const flags = fcntl(fd, F_GETFL, 0); if (-1 == flags) { int const savederrno = errno; PSL_LOG_ERROR("%s (%s=%p): ERROR: fcntl(%d, F_GETFL, 0) failed; " \ "errno=%d (%s)", __func__, userLabel, cookie, fd, savederrno, strerror(savederrno)); return psl_err_pslerror_from_errno(savederrno, PSL_ERR_SOCKET_CONFIG); } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { int const savederrno = errno; PSL_LOG_ERROR("%s (%s=%p): ERROR: fcntl(%d, F_SETFL, " \ "flags | O_NONBLOCK) failed; errno=%d (%s)", __func__, userLabel, cookie, fd, savederrno, strerror(savederrno)); return psl_err_pslerror_from_errno(savederrno, PSL_ERR_SOCKET_CONFIG); } return 0; }//psl_inet_make_fd_non_blocking
/** ======================================================================== * ========================================================================= */ static void chan_fsm_free(PslChanFsm* const pFsm) { PSL_LOG_INFO("%s (fsm=%p)", __func__, pFsm); PSL_ASSERT(pFsm); /** * If the FSM was successfully started, then we should have been * finalized by the channel via psl_chan_fsm_finalize() before * our reference count dropped to zero */ PSL_ASSERT(!pFsm->fsmStarted || pFsm->fsmFinalized); /// Our sock should be closed by the state handlers if (pFsm->fd >= 0) { PSL_LOG_ERROR("%s: ERROR: pFsm->fd not closed!", __func__); } if (!pFsm->fsmStarted) { chan_fsm_destroy_widgets(pFsm); } g_free(pFsm); PSL_LOG_DEBUGLOW("%s (fsm=%p): LEAVING", __func__, pFsm); }
/** ======================================================================== * ========================================================================= */ PslError psl_multi_fd_watch_remove_fd(PslMultiFdWatchSource* source, int const fd) { gboolean removed; PslMultiFdDescriptor* desc; PSL_LOG_DEBUGLOW("%s (watch=%p), fd=%d", __func__, source, (int)fd); desc = (PslMultiFdDescriptor*) g_hash_table_lookup(source->descTable, GINT_TO_POINTER(fd)); if (!desc) { PSL_LOG_ERROR("%s (watch=%p): ERROR: fd=%d not found", __func__, source, (int)fd); return PSL_ERR_INVAL; } if (desc->pollAdded) { g_source_remove_poll((GSource*)source, &desc->pollFd); } removed = g_hash_table_remove(source->descTable, GINT_TO_POINTER(fd)); PSL_ASSERT(removed); return 0; }
/** ======================================================================== * ========================================================================= */ PslError psl_multi_fd_watch_set_poll_timeout(PslMultiFdWatchSource* source, int const timeoutMillisec) { PSL_LOG_DEBUG("%s (watch=%p), timeout=%d millisec", __func__, source, (int)timeoutMillisec); if (timeoutMillisec < -1) { PSL_LOG_ERROR("%s (watch=%p): ERROR: passed negative timemeout, " \ "but not -1: %d", __func__, source, (int)timeoutMillisec); return PSL_ERR_INVAL; } source->restartTimer = true; if (timeoutMillisec == -1) { source->timeoutMillisec = MULTI_FD_WATCH_INFINITE_MSEC; } else { // timeoutMillisec >= 0 source->timeoutMillisec = timeoutMillisec; } return 0; }
/** ======================================================================== * ========================================================================= */ static PslSmeEventStatus chan_fsm_closed_state_handler(PslChanFsmClosedState* const pState, PslChanFsm* const pFsm, PslSmeEventId const evtId, const PslChanFsmEvtArg* const evtArg) { switch (evtId) { case kFsmEventEnterScope: if (pFsm->fsmClosed) { PSL_LOG_ERROR("%s (fsm=%p): ERROR: entering CLOSED state *again*", __func__, pFsm); } if (pFsm->fd >= 0 && !(pFsm->userSettings.fdOpts & kPmSockFileDescOpt_doNotClose)) { shutdown(pFsm->fd, SHUT_RDWR); if (0 != close(pFsm->fd)) { const int savederrno = errno; PSL_LOG_ERROR( "%s (fsm=%p): ERROR closing comms fd: fd=%d, errno=%d (%s)", __func__, pFsm, pFsm->fd, savederrno, strerror(savederrno)); } } pFsm->fd = PSL_CHAN_FSM_CLOSED_FD_VALUE; pFsm->fsmClosed = true; return kPslSmeEventStatus_success; break; case kFsmEventExitScope: return kPslSmeEventStatus_success; break; case kFsmEventBegin: (void)psl_multi_fd_watch_reset(pFsm->fdWatchInfo.fdWatch); return kPslSmeEventStatus_success; break; default: break; ///< allow default processing by parent } return kPslSmeEventStatus_passToParent; }//chan_fsm_closed_state_handler
/** ======================================================================== * ========================================================================= */ PslError psl_inet_connect_sock(int const s, int const addrFamily, const char* const addrStr, int const port, const char* const userLabel, const void* const cookie) { PslInetGenericSockAddr server; PSL_LOG_DEBUG("%s (%s=%p): fd=%d, addrfamily=%d, addrStr=%s, port=%d", __func__, userLabel, cookie, s, addrFamily, PSL_LOG_OBFUSCATE_STR(addrStr), port); bool success = !psl_inet_make_sock_addr(addrFamily, addrStr, port, &server, userLabel, cookie); if (!success) { return PSL_ERR_BAD_SERV_ADDR; } int rc = connect(s, (struct sockaddr*)&server.sa, server.addrLength); PSL_ASSERT(rc <= 0); if (0 == rc) { PSL_LOG_DEBUG("%s (%s=%p): connect() succeeded immediately", __func__, userLabel, cookie); return 0; } else if (rc < 0 && EINPROGRESS == errno) { PSL_LOG_DEBUG("%s (%s=%p): connect() returned EINPROGRESS", __func__, userLabel, cookie); return 0; } /* @todo The implication of EINTR when connecting on a non-blocking socket is vague, at best. Should we retry connect(), assume that all is well, or give up on this connection? else if (rc < 0 && EINTR == errno) { PSL_LOG_DEBUG("%s (fsm=%p): connect() returned EINTR", __func__, pFsm); return 0; } */ else { /// (rc < 0) int const saverrno = errno; PSL_LOG_ERROR("%s (%s=%p): ERROR: connect() failed; errno=%d (%s)", __func__, userLabel, cookie, saverrno, strerror(saverrno)); return psl_err_pslerror_from_connect_errno(saverrno); } }//psl_inet_connect_sock
/** ======================================================================== * ========================================================================= */ static PslSmeEventStatus chan_fsm_alive_state_handler(PslChanFsmAliveState* const pState, PslChanFsm* const pFsm, PslSmeEventId const evtId, const PslChanFsmEvtArg* const evtArg) { switch (evtId) { case kFsmEventEnterScope: case kFsmEventExitScope: case kFsmEventBegin: return kPslSmeEventStatus_success; break; case PSL_CHAN_FSM_EVT_CLOSE: psl_chan_fsm_goto_closed_state(pFsm); return kPslSmeEventStatus_success; break; case PSL_CHAN_FSM_EVT_FINALIZE: psl_chan_fsm_goto_final_state(pFsm); return kPslSmeEventStatus_success; break; case PSL_CHAN_FSM_EVT_CHECK_IO: return psl_chan_fsm_evt_make_simple_CHECK_IO_response( &evtArg->checkIO, kPslChanFsmEvtIOReadyHint_notReady, PSL_INVALID_FD); break; default: break; } if (evtId >= kFsmEventFirstUserEvent) { PSL_LOG_ERROR( "%s (%p): %s.%s: ERROR: Operation not allowed: evtId=%d", __func__, pFsm, FsmDbgPeekMachineName(&pFsm->base.base), FsmDbgPeekStateName(&pState->base.base), evtId); psl_chan_fsm_set_last_error(pFsm, kPslChanFsmErrorSource_psl, PSL_ERR_NOT_ALLOWED); return kPslSmeEventStatus_errorCatchAll; } else { return kPslSmeEventStatus_success; } }//chan_fsm_alive_state_handler
/** ======================================================================== * ========================================================================= */ const char* psl_chan_fsm_str_from_giostatus(GIOStatus const giostatus) { switch (giostatus) { case G_IO_STATUS_ERROR: return "G_IO_STATUS_ERROR"; break; case G_IO_STATUS_NORMAL: return "G_IO_STATUS_NORMAL"; break; case G_IO_STATUS_EOF: return "G_IO_STATUS_EOF"; break; case G_IO_STATUS_AGAIN: return "G_IO_STATUS_AGAIN"; break; } PSL_LOG_ERROR("ERROR: UNKNOWN GIOStatus value: %ld", (long)giostatus); return "ERROR: UNKNOWN GIOStatus value (see log)"; }
/** ======================================================================== * PslMultiFdWatchSourceCb callback from the multi-fd watch * source * * @see PslMultiFdWatchSourceCb for arg info * * @param userData * @param pollrecs * @param numrecs * * @return gboolean * * ========================================================================= */ static gboolean chan_fsm_fd_watch_cb(gpointer const userData, const PslMultiFdPollRec* pollrecs, int const numrecs) { PslChanFsm* const pFsm = (PslChanFsm*)userData; PSL_ASSERT(!pFsm->fdWatchInfo.inFdWatchCb); pFsm->fdWatchInfo.inFdWatchCb = true; PslSmeEventStatus const status = psl_chan_fsm_evt_dispatch_FD_WATCH(pFsm, pollrecs, numrecs); if (kPslSmeEventStatus_errorCatchAll == status || kPslSmeEventStatus_notHandled == status) { PSL_LOG_ERROR("%s (fsm=%p): ERROR: FD_WATCH event fell through", __func__, pFsm); } /// Process external callbacks struct PslChanFsmFdWatchCompletion* const cb = &pFsm->fdWatchInfo.completionCb; if (kPslChanFsmEvtCompletionCbId_none != cb->cb.which) { /// So we don't get destroyed while in callbacks (void)psl_chan_fsm_ref(pFsm); psl_chan_fsm_dispatch_completion_cb(pFsm, &cb->cb, cb->pslErr, cb->switchingToCrypto); cb->cb.which = kPslChanFsmEvtCompletionCbId_none; pFsm->fdWatchInfo.inFdWatchCb = false; /// okay if the FSM gets destroyed now psl_chan_fsm_unref(pFsm); return true; }//if user callback scheduled else { pFsm->fdWatchInfo.inFdWatchCb = false; } return true; ///< true, so the GSource won't get detached from our gmain ctx }//chan_fsm_fd_watch_cb
/** ======================================================================== * ========================================================================= */ bool psl_inet_ipaddr_from_string(const char* const hn, PslInetIPAddress* const pBuf, const char* const userLabel, const void* const cookie) { PSL_ASSERT(pBuf); pBuf->family = AF_UNSPEC; if (!hn || !*hn) { PSL_LOG_ERROR("%s (%s=%p): ERROR: NULL or empty hostname string", __func__, userLabel, cookie); return false; } int rc; rc = inet_pton(AF_INET, hn, &pBuf->addr); if (rc > 0) { PSL_LOG_DEBUGLOW("%s (%s=%p): hostname looks like IPv4 address", __func__, userLabel, cookie); pBuf->family = AF_INET; pBuf->len = sizeof(struct in_addr); return true; } else { rc = inet_pton(AF_INET6, hn, &pBuf->addr); if (rc > 0) { PSL_LOG_DEBUGLOW("%s (%s=%p): hostname looks like IPv6 address", __func__, userLabel, cookie); pBuf->family = AF_INET6; pBuf->len = sizeof(struct in6_addr); return true; } else { PSL_LOG_DEBUGLOW("%s (%s=%p): hostname string '%s' doesn't look " \ "like an IP address'.", __func__, userLabel, cookie, PSL_LOG_OBFUSCATE_STR(hn)); } } return false; }//psl_inet_ipaddr_from_string
/** ======================================================================== * ========================================================================= */ int psl_inet_make_sock_addr(int const reqFamily, const char* const addrStr, int const port, PslInetGenericSockAddr* const res, const char* const userLabel, const void* const cookie) { PSL_ASSERT(AF_INET == reqFamily || AF_INET6 == reqFamily || AF_UNSPEC == reqFamily); PSL_ASSERT(AF_UNSPEC != reqFamily || (addrStr && addrStr[0])); PSL_ASSERT(res); memset(res, 0, sizeof(*res)); res->family = AF_UNSPEC; PslInetIPAddress pslinaddr; int inaddrlen = 0; int actualFamily = reqFamily; if (AF_UNSPEC == reqFamily) { PSL_LOG_DEBUGLOW("%s (%s=%p): resoloving addrfamily (was AF_UNSPEC)", __func__, userLabel, cookie); PSL_ASSERT(addrStr); bool const gotAddr = psl_inet_ipaddr_from_string(addrStr, &pslinaddr, userLabel, cookie); if (gotAddr) { actualFamily = pslinaddr.family; inaddrlen = pslinaddr.len; } else { PSL_LOG_ERROR("%s (%s=%p): ERROR: could not determine " \ "address family from address string '%s'.", __func__, userLabel, cookie, PSL_LOG_OBFUSCATE_STR(addrStr)); return EINVAL; } } void* addrDst = NULL; if (AF_INET == actualFamily) { res->addrLength = sizeof(res->sa); res->sa.sin_family = actualFamily; res->sa.sin_port = htons(port); res->sa.sin_addr.s_addr = INADDR_ANY; addrDst = &res->sa.sin_addr; } else { PSL_ASSERT(AF_INET6 == actualFamily); res->addrLength = sizeof(res->sa6); res->sa6.sin6_family = actualFamily; //res->sa6.sin6_flowinfo = 0; ///< what should we do with these? //res->sa6.sin6_scope_id = 0; res->sa6.sin6_port = htons(port); res->sa6.sin6_addr = in6addr_any; addrDst = &res->sa6.sin6_addr; } if (addrStr && inaddrlen) { memcpy(addrDst, &pslinaddr.addr, inaddrlen); } else if (addrStr) { PSL_ASSERT(addrDst); int const ptonRes = inet_pton(actualFamily, addrStr, addrDst); if (ptonRes < 0) { /// unexpected address family - check errno int const saverrno = errno; PSL_LOG_ERROR("%s (%s=%p): ERROR: inet_pton() failed; " \ "family=%d, addrStr=%s, errno=%d (%s).", __func__, userLabel, cookie, actualFamily, PSL_LOG_OBFUSCATE_STR(addrStr), saverrno, strerror(saverrno)); PSL_ASSERT(saverrno); return saverrno; } else if (ptonRes == 0) { /// addr does not match family /// @note This case does not set errno PSL_LOG_ERROR("%s (%s=%p): ERROR: inet_pton() failed; Address " \ "does not match family; family=%d, addrStr=%s.", __func__, userLabel, cookie, actualFamily, PSL_LOG_OBFUSCATE_STR(addrStr)); return EINVAL; } } res->family = actualFamily; return 0; }// psl_inet_make_sock_addr
/** ======================================================================== * ========================================================================= */ static PslSmeEventStatus chan_fsm_init_state_handler(PslChanFsmInitState* const pState, PslChanFsm* const pFsm, PslSmeEventId const evtId, const PslChanFsmEvtArg* const evtArg) { switch (evtId) { case kFsmEventEnterScope: case kFsmEventExitScope: case kFsmEventBegin: return kPslSmeEventStatus_success; break; case PSL_CHAN_FSM_EVT_CONNECT: if (AF_UNSPEC == pFsm->userSettings.serverAddr.addrFamily && pFsm->fd < 0) { PSL_LOG_ERROR("%s (fsm=%p): ERROR: PSL_CHAN_FSM_EVT_CONNECT " \ "failed: connect address not set", __func__, pFsm); psl_chan_fsm_set_last_error(pFsm, kPslChanFsmErrorSource_psl, PSL_ERR_BAD_SERV_ADDR); return kPslSmeEventStatus_error; } if (pFsm->fd < 0) { psl_chan_fsm_goto_plain_lookup_state(pFsm, &evtArg->connect.data); } else { static const struct PslChanFsmEvtInetAddrText addrText = { .family = AF_UNSPEC }; psl_chan_fsm_goto_plain_conn_state(pFsm, &evtArg->connect.data, &addrText); } return kPslSmeEventStatus_success; break; case PSL_CHAN_FSM_EVT_SET_CONN_FD: { const struct PslChanFsmEvtArgSetConnFD* const arg = &evtArg->setConnFD; PSL_ASSERT(arg->fd >= 0); if (AF_UNSPEC != pFsm->userSettings.serverAddr.addrFamily) { PSL_LOG_ERROR("%s (fsm=%p): SET_CONN_FD ERROR: " \ "not allowed when server address is set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } if (AF_UNSPEC != pFsm->userSettings.bindAddr.addrFamily) { PSL_LOG_ERROR("%s (fsm=%p): SET_CONN_FD ERROR: " \ "not allowed when bind address is set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } if (pFsm->fd >= 0) { PSL_LOG_ERROR("%s (fsm=%p): SET_CONN_FD ERROR: connected " \ "file descriptor was already set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } pFsm->fd = arg->fd; pFsm->userSettings.fdIsUserProvided = true; pFsm->userSettings.fdOpts = arg->opts; return kPslSmeEventStatus_success; break; } case PSL_CHAN_FSM_EVT_SET_SERVER: { const struct PslChanFsmEvtArgSetServer* const arg = &evtArg->setServer; if (pFsm->fd >= 0) { PSL_LOG_ERROR("%s (fsm=%p): SET_SERVER ADDR ERROR: not " \ "allowed when connected file descriptor is set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } if (AF_UNSPEC != pFsm->userSettings.serverAddr.addrFamily) { PSL_LOG_ERROR("%s (fsm=%p): SET_SERVER ADDR ERROR: " \ "server address was already set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } PSL_LOG_DEBUG("%s (fsm=%p): SET_SERVER ADDR: server: '%s:%d'/af=%d", __func__, pFsm, PSL_LOG_OBFUSCATE_STR(arg->hostStr), (int)arg->port, (int)arg->addrFamily); pFsm->userSettings.serverAddr.addrFamily = arg->addrFamily; pFsm->userSettings.serverAddr.port = arg->port; pFsm->userSettings.serverAddr.hostStr = g_strdup(arg->hostStr); PSL_ASSERT(pFsm->userSettings.serverAddr.hostStr); } return kPslSmeEventStatus_success; break; case PSL_CHAN_FSM_EVT_SET_SOCK_BIND: { const struct PslChanFsmEvtArgSetSockBind* const arg = &evtArg->setSockBind; if (pFsm->fd >= 0) { PSL_LOG_ERROR("%s (fsm=%p): SET_SOCK_BIND ERROR: not " \ "allowed when connected file descriptor is set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } if (AF_UNSPEC != pFsm->userSettings.bindAddr.addrFamily) { PSL_LOG_ERROR("%s (fsm=%p): SET_SOCK_BIND ERROR: " \ "socket bind address was already set", __func__, pFsm); return kPslSmeEventStatus_passToParent; } PSL_LOG_DEBUG("%s (fsm=%p): SET_SOCK_BIND: local addr: '%s:%d'/af=%d", __func__, pFsm, PSL_LOG_MAKE_SAFE_STR(arg->ipAddrStr), (int)arg->port, (int)arg->addrFamily); pFsm->userSettings.bindAddr.addrFamily = arg->addrFamily; pFsm->userSettings.bindAddr.port = arg->port; pFsm->userSettings.bindAddr.hostStr = g_strdup(arg->ipAddrStr); PSL_ASSERT(!arg->ipAddrStr || pFsm->userSettings.bindAddr.hostStr); } return kPslSmeEventStatus_success; break; default: break; ///< allow default processing by parent } return kPslSmeEventStatus_passToParent; }//chan_fsm_init_state_handler
/** ======================================================================== * The 'dispatch' function of GSourceFuncs * * @param base * @param opaqueCb @see GSourceFuncs * @param userData @see GSourceFuncs * * @return gboolean @see GSourceFuncs * * ========================================================================= */ static gboolean multi_fd_watch_dispatch(GSource* const base, GSourceFunc opaqueCb, gpointer userData) { PslMultiFdWatchSource* const source = (PslMultiFdWatchSource*)base; PslMultiFdWatchSourceCb* const cb = (PslMultiFdWatchSourceCb*)opaqueCb; source->restartTimer = true; ///< so it will restart at next poll prepare if (!cb) { PSL_LOG_ERROR("%s (watch=%p): ERROR: multi-fd watch dispatch with " \ "NULL user callback ptr: did you forget to call " \ "g_source_set_callback()?", __func__, base); return false; } PSL_LOG_DEBUGLOW("%s (watch=%p): preparing to call user's callback", __func__, base); /// Construct an array of ready PollFD's if (source->cachedReadyPollFdArray->len > 0) { g_array_set_size(source->cachedReadyPollFdArray, 0); } gpointer key, value; GHashTableIter iter; g_hash_table_iter_init(&iter, source->descTable); while (g_hash_table_iter_next(&iter, &key, &value)) { const GPollFD* const pollFd = &((PslMultiFdDescriptor*)value)->pollFd; PSL_LOG_DEBUGLOW( "%s (watch=%p): iter: table=%p, desc=%p, fd=%d, " \ "GIOCondition=(mon:0x%lX, ind:0x%lX)", __func__, source, source->descTable, value, (int)pollFd->fd, (unsigned long)pollFd->events, (unsigned long)pollFd->revents); if ((pollFd->revents & MULTI_FD_WATCH_PERM_FAILURE_IO_CONDS) != 0) { PSL_LOG_ERROR("%s: (watch=%p): I/O FAILURE on fd=%d: indicated " \ "GIOCondition=0x%lX", __func__, source, (int)pollFd->fd, (unsigned long)pollFd->revents); } const GIOCondition currentCondition = pollFd->revents & (pollFd->events | MULTI_FD_WATCH_PERM_FAILURE_IO_CONDS); if (currentCondition) { const PslMultiFdPollRec pollRec = { .fd = pollFd->fd, .reqEvents = pollFd->events, .indEvents = currentCondition }; g_array_append_vals(source->cachedReadyPollFdArray, &pollRec, 1); } } /// Dispatch the callback const gint numrecs = source->cachedReadyPollFdArray->len; const PslMultiFdPollRec* const pollrecs = ((numrecs > 0) ? (PslMultiFdPollRec*)source->cachedReadyPollFdArray->data : NULL); PSL_LOG_DEBUGLOW("%s (watch=%p): Calling user's callback: " \ "pollrec array=%p, numelts=%d", __func__, base, pollrecs, (int)numrecs); const gboolean stayAttached = cb(userData, pollrecs, numrecs); if (!stayAttached) { PSL_LOG_DEBUG("%s (watch=%p): user cb requested removal of source", __func__, base); } return stayAttached; } //multi_fd_watch_dispatch /** ======================================================================== * The 'finalize' function of GSourceFuncs * * @param base * * ========================================================================= */ static void multi_fd_watch_finalize(GSource* base) { PSL_LOG_DEBUG("%s (watch=%p)", __func__, base); PslMultiFdWatchSource* const source = (PslMultiFdWatchSource*)base; /** * @note We may be called with our own data members in a * partially-constructed (but initialized) state if * psl_multi_fd_watch_new failed part way through and * called g_source_unref. */ if (source->descTable) { /** * @note our "superclass" GSource takes care of removing * our pollFds record pointers from the source's and the * gmain context's pollFd lists when the GSource instance * is being detached from the gmain context, so we don't * need to do it ourselves. */ g_hash_table_destroy(source->descTable); } if (source->cachedReadyPollFdArray) { (void)g_array_free(source->cachedReadyPollFdArray, true/*free_segment*/); } }