void zmq::pgm_socket_t::process_upstream () { pgm_msgv_t dummy_msg; size_t dummy_bytes = 0; pgm_error_t *pgm_error = NULL; const int status = pgm_recvmsgv (sock, &dummy_msg, 1, MSG_ERRQUEUE, &dummy_bytes, &pgm_error); // Invalid parameters. zmq_assert (status != PGM_IO_STATUS_ERROR); // No data should be returned. zmq_assert (dummy_bytes == 0 && (status == PGM_IO_STATUS_TIMER_PENDING || status == PGM_IO_STATUS_RATE_LIMITED || status == PGM_IO_STATUS_WOULD_BLOCK)); last_rx_status = status; if (status == PGM_IO_STATUS_TIMER_PENDING) errno = EBUSY; else if (status == PGM_IO_STATUS_RATE_LIMITED) errno = ENOMEM; else errno = EAGAIN; }
void zmq::pgm_socket_t::process_upstream () { pgm_msgv_t dummy_msg; size_t dummy_bytes = 0; GError *pgm_error = NULL; PGMIOStatus status = pgm_recvmsgv (transport, &dummy_msg, 1, MSG_DONTWAIT, &dummy_bytes, &pgm_error); // No data should be returned. zmq_assert (dummy_bytes == 0 && (status == PGM_IO_STATUS_TIMER_PENDING || status == PGM_IO_STATUS_RATE_LIMITED)); }
// pgm_transport_recvmsgv is called to fill the pgm_msgv array up to // pgm_msgv_len. In subsequent calls data from pgm_msgv structure are // returned. ssize_t zmq::pgm_socket_t::receive (void **raw_data_, const pgm_tsi_t **tsi_) { size_t raw_data_len = 0; // We just sent all data from pgm_transport_recvmsgv up // and have to return 0 that another engine in this thread is scheduled. if (nbytes_rec == nbytes_processed && nbytes_rec > 0) { // Reset all the counters. nbytes_rec = 0; nbytes_processed = 0; pgm_msgv_processed = 0; return 0; } // If we have are going first time or if we have processed all pgm_msgv_t // structure previously read from the pgm socket. if (nbytes_rec == nbytes_processed) { // Check program flow. zmq_assert (pgm_msgv_processed == 0); zmq_assert (nbytes_processed == 0); zmq_assert (nbytes_rec == 0); // Receive a vector of Application Protocol Domain Unit's (APDUs) // from the transport. GError *pgm_error = NULL; const PGMIOStatus status = pgm_recvmsgv (transport, pgm_msgv, pgm_msgv_len, MSG_DONTWAIT, &nbytes_rec, &pgm_error); zmq_assert (status != PGM_IO_STATUS_ERROR); // In a case when no ODATA/RDATA fired POLLIN event (SPM...) // pgm_recvmsg returns ?. if (status == PGM_IO_STATUS_TIMER_PENDING) { zmq_assert (nbytes_rec == 0); // In case if no RDATA/ODATA caused POLLIN 0 is // returned. nbytes_rec = 0; return 0; } // Data loss. if (status == PGM_IO_STATUS_RESET) { pgm_peer_t* peer = (pgm_peer_t*) transport->peers_pending->data; // Save lost data TSI. *tsi_ = &peer->tsi; nbytes_rec = 0; // In case of dala loss -1 is returned. errno = EINVAL; g_error_free (pgm_error); return -1; } zmq_assert (status == PGM_IO_STATUS_NORMAL); } zmq_assert (nbytes_rec > 0); // Only one APDU per pgm_msgv_t structure is allowed. zmq_assert (pgm_msgv [pgm_msgv_processed].msgv_len == 1); struct pgm_sk_buff_t* skb = pgm_msgv [pgm_msgv_processed].msgv_skb [0]; // Take pointers from pgm_msgv_t structure. *raw_data_ = skb->data; raw_data_len = skb->len; // Save current TSI. *tsi_ = &skb->tsi; // Move the the next pgm_msgv_t structure. pgm_msgv_processed++; nbytes_processed +=raw_data_len; return raw_data_len; }
static gpointer receiver_thread ( gpointer data ) { pgm_sock_t* rx_sock = (pgm_sock_t*)data; const long iov_len = 20; const long ev_len = 1; struct pgm_msgv_t msgv[iov_len]; #ifdef CONFIG_HAVE_EPOLL struct epoll_event events[ev_len]; /* wait for maximum 1 event */ const int efd = epoll_create (IP_MAX_MEMBERSHIPS); if (efd < 0) { g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); g_main_loop_quit (g_loop); return NULL; } if (pgm_epoll_ctl (rx_sock, efd, EPOLL_CTL_ADD, EPOLLIN) < 0) { g_error ("pgm_epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); g_main_loop_quit (g_loop); return NULL; } struct epoll_event event; event.events = EPOLLIN; event.data.fd = g_quit_pipe[0]; if (epoll_ctl (efd, EPOLL_CTL_ADD, g_quit_pipe[0], &event) < 0) { g_error ("epoll_ctl failed errno %i: \"%s\"", errno, strerror(errno)); g_main_loop_quit (g_loop); return NULL; } #elif defined(CONFIG_HAVE_POLL) int n_fds = 2; struct pollfd fds[ 1 + n_fds ]; #elif defined(G_OS_UNIX) /* HAVE_SELECT */ int n_fds; fd_set readfds; #else /* G_OS_WIN32 */ SOCKET recv_sock, pending_sock; DWORD cEvents = PGM_RECV_SOCKET_READ_COUNT + 1; WSAEVENT waitEvents[ PGM_RECV_SOCKET_READ_COUNT + 1 ]; socklen_t socklen = sizeof (SOCKET); waitEvents[0] = g_quit_event; waitEvents[1] = WSACreateEvent (); pgm_getsockopt (rx_sock, IPPROTO_PGM, PGM_RECV_SOCK, &recv_sock, &socklen); WSAEventSelect (recv_sock, waitEvents[1], FD_READ); waitEvents[2] = WSACreateEvent (); pgm_getsockopt (rx_sock, IPPROTO_PGM, PGM_PENDING_SOCK, &pending_sock, &socklen); WSAEventSelect (pending_sock, waitEvents[2], FD_READ); #endif /* !CONFIG_HAVE_EPOLL */ do { struct timeval tv; #ifndef _WIN32 int timeout; #else DWORD dwTimeout, dwEvents; #endif size_t len; pgm_error_t* pgm_err = NULL; const int status = pgm_recvmsgv (rx_sock, msgv, G_N_ELEMENTS(msgv), 0, &len, &pgm_err); switch (status) { case PGM_IO_STATUS_NORMAL: on_msgv (msgv, len); break; case PGM_IO_STATUS_TIMER_PENDING: { socklen_t optlen = sizeof (tv); pgm_getsockopt (rx_sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); } goto block; case PGM_IO_STATUS_RATE_LIMITED: { socklen_t optlen = sizeof (tv); pgm_getsockopt (rx_sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); } /* fall through */ case PGM_IO_STATUS_WOULD_BLOCK: block: #ifdef CONFIG_HAVE_EPOLL timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); #elif defined(CONFIG_HAVE_POLL) timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); memset (fds, 0, sizeof(fds)); fds[0].fd = g_quit_pipe[0]; fds[0].events = POLLIN; pgm_poll_info (rx_sock, &fds[1], &n_fds, POLLIN); poll (fds, 1 + n_fds, timeout /* ms */); #elif defined(G_OS_UNIX) /* HAVE_SELECT */ FD_ZERO(&readfds); FD_SET(g_quit_pipe[0], &readfds); n_fds = g_quit_pipe[0] + 1; pgm_select_info (rx_sock, &readfds, NULL, &n_fds); select (n_fds, &readfds, NULL, NULL, PGM_IO_STATUS_RATE_LIMITED == status ? &tv : NULL); #else /* G_OS_WIN32 */ dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? WSA_INFINITE : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); dwEvents = WSAWaitForMultipleEvents (cEvents, waitEvents, FALSE, dwTimeout, FALSE); switch (dwEvents) { case WSA_WAIT_EVENT_0+1: WSAResetEvent (waitEvents[1]); break; case WSA_WAIT_EVENT_0+2: WSAResetEvent (waitEvents[2]); break; default: break; } #endif /* !CONFIG_HAVE_EPOLL */ break; default: if (pgm_err) { g_warning ("%s", pgm_err->message); pgm_error_free (pgm_err); pgm_err = NULL; } if (PGM_IO_STATUS_ERROR == status) break; } } while (!g_quit); #ifdef CONFIG_HAVE_EPOLL close (efd); #elif defined(G_OS_WIN32) WSACloseEvent (waitEvents[1]); WSACloseEvent (waitEvents[2]); # if (__STDC_VERSION__ < 199901L) g_free (waitHandles); # endif #endif return NULL; }
// pgm_recvmsgv is called to fill the pgm_msgv array up to pgm_msgv_len. // In subsequent calls data from pgm_msgv structure are returned. ssize_t zmq::pgm_socket_t::receive (void **raw_data_, const pgm_tsi_t **tsi_) { size_t raw_data_len = 0; // We just sent all data from pgm_transport_recvmsgv up // and have to return 0 that another engine in this thread is scheduled. if (nbytes_rec == nbytes_processed && nbytes_rec > 0) { // Reset all the counters. nbytes_rec = 0; nbytes_processed = 0; pgm_msgv_processed = 0; errno = EAGAIN; return 0; } // If we have are going first time or if we have processed all pgm_msgv_t // structure previously read from the pgm socket. if (nbytes_rec == nbytes_processed) { // Check program flow. zmq_assert (pgm_msgv_processed == 0); zmq_assert (nbytes_processed == 0); zmq_assert (nbytes_rec == 0); // Receive a vector of Application Protocol Domain Unit's (APDUs) // from the transport. pgm_error_t *pgm_error = NULL; const int status = pgm_recvmsgv (sock, pgm_msgv, pgm_msgv_len, MSG_ERRQUEUE, &nbytes_rec, &pgm_error); // Invalid parameters. zmq_assert (status != PGM_IO_STATUS_ERROR); last_rx_status = status; // In a case when no ODATA/RDATA fired POLLIN event (SPM...) // pgm_recvmsg returns PGM_IO_STATUS_TIMER_PENDING. if (status == PGM_IO_STATUS_TIMER_PENDING) { zmq_assert (nbytes_rec == 0); // In case if no RDATA/ODATA caused POLLIN 0 is // returned. nbytes_rec = 0; errno = EBUSY; return 0; } // Send SPMR, NAK, ACK is rate limited. if (status == PGM_IO_STATUS_RATE_LIMITED) { zmq_assert (nbytes_rec == 0); // In case if no RDATA/ODATA caused POLLIN 0 is returned. nbytes_rec = 0; errno = ENOMEM; return 0; } // No peers and hence no incoming packets. if (status == PGM_IO_STATUS_WOULD_BLOCK) { zmq_assert (nbytes_rec == 0); // In case if no RDATA/ODATA caused POLLIN 0 is returned. nbytes_rec = 0; errno = EAGAIN; return 0; } // Data loss. if (status == PGM_IO_STATUS_RESET) { struct pgm_sk_buff_t* skb = pgm_msgv [0].msgv_skb [0]; // Save lost data TSI. *tsi_ = &skb->tsi; nbytes_rec = 0; // In case of dala loss -1 is returned. errno = EINVAL; pgm_free_skb (skb); return -1; } zmq_assert (status == PGM_IO_STATUS_NORMAL); } else { zmq_assert (pgm_msgv_processed <= pgm_msgv_len); } // Zero byte payloads are valid in PGM, but not 0MQ protocol. zmq_assert (nbytes_rec > 0); // Only one APDU per pgm_msgv_t structure is allowed. zmq_assert (pgm_msgv [pgm_msgv_processed].msgv_len == 1); struct pgm_sk_buff_t* skb = pgm_msgv [pgm_msgv_processed].msgv_skb [0]; // Take pointers from pgm_msgv_t structure. *raw_data_ = skb->data; raw_data_len = skb->len; // Save current TSI. *tsi_ = &skb->tsi; // Move the the next pgm_msgv_t structure. pgm_msgv_processed++; zmq_assert (pgm_msgv_processed <= pgm_msgv_len); nbytes_processed +=raw_data_len; return raw_data_len; }
int main ( int argc, char* argv[] ) { pgm_error_t* pgm_err = NULL; setlocale (LC_ALL, ""); log_init (); g_message ("enonblocksyncrecvmsgv"); if (!pgm_init (&pgm_err)) { g_error ("Unable to start PGM engine: %s", pgm_err->message); pgm_error_free (pgm_err); return EXIT_FAILURE; } /* parse program arguments */ const char* binary_name = strrchr (argv[0], '/'); int c; while ((c = getopt (argc, argv, "s:n:p:lh")) != -1) { switch (c) { case 'n': g_network = optarg; break; case 's': g_port = atoi (optarg); break; case 'p': g_udp_encap_port = atoi (optarg); break; case 'l': g_multicast_loop = TRUE; break; case 'h': case '?': usage (binary_name); } } /* setup signal handlers */ signal (SIGSEGV, on_sigsegv); signal (SIGINT, on_signal); signal (SIGTERM, on_signal); #ifdef SIGHUP signal (SIGHUP, SIG_IGN); #endif if (!on_startup ()) { g_error ("startup failed"); return EXIT_FAILURE; } /* incoming message buffer, iov_len must be less than SC_IOV_MAX */ const long iov_len = 8; const long ev_len = 1; g_message ("Using iov_len %li ev_len %li", iov_len, ev_len); struct pgm_msgv_t msgv[iov_len]; struct epoll_event events[ev_len]; /* wait for maximum 1 event */ /* epoll file descriptor */ const int efd = epoll_create (IP_MAX_MEMBERSHIPS); if (efd < 0) { g_error ("epoll_create failed errno %i: \"%s\"", errno, strerror(errno)); return EXIT_FAILURE; } const int retval = pgm_epoll_ctl (g_sock, efd, EPOLL_CTL_ADD, EPOLLIN); if (retval < 0) { g_error ("pgm_epoll_ctl failed."); return EXIT_FAILURE; } /* dispatch loop */ g_message ("entering PGM message loop ... "); do { struct timeval tv; int timeout; size_t len; const int status = pgm_recvmsgv (g_sock, msgv, iov_len, 0, &len, &pgm_err); switch (status) { case PGM_IO_STATUS_NORMAL: on_msgv (msgv, len); break; case PGM_IO_STATUS_TIMER_PENDING: { socklen_t optlen = sizeof (tv); pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen); } goto block; case PGM_IO_STATUS_RATE_LIMITED: { socklen_t optlen = sizeof (tv); pgm_getsockopt (g_sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen); } /* fall through */ case PGM_IO_STATUS_WOULD_BLOCK: /* poll for next event */ block: timeout = PGM_IO_STATUS_WOULD_BLOCK == status ? -1 : ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); epoll_wait (efd, events, G_N_ELEMENTS(events), timeout /* ms */); break; default: if (pgm_err) { g_warning ("%s", pgm_err->message); pgm_error_free (pgm_err); pgm_err = NULL; } if (PGM_IO_STATUS_ERROR == status) break; } } while (!g_quit); g_message ("message loop terminated, cleaning up."); /* cleanup */ close (efd); if (g_sock) { g_message ("closing PGM socket."); pgm_close (g_sock, TRUE); g_sock = NULL; } g_message ("PGM engine shutdown."); pgm_shutdown (); g_message ("finished."); return EXIT_SUCCESS; }