Beispiel #1
0
void
IghtConnectionState::handle_read(bufferevent *bev, void *opaque)
{
	auto self = (IghtConnectionState *) opaque;

	(void) bev;  // Suppress warning about unused variable

	self->reading = 1;
	self->on_data(bufferevent_get_input(self->bev));
	self->reading = 0;

	if (self->closing)
		delete (self);
}
Beispiel #2
0
void Parser::parse( const char *str, int len, bool end )
{
    const char *p = str;
    const char *pos = p;
    const char *s = 0;//数据开始位置
    const char *e = 0;//数据结束位置
    while( len > 0 )
    {
        if( *p == '<' )
        {
            pos = p;
            if( s )
               e = p;//记下数据结束位置
        }
        else if( *p=='>' )
        {
            std::string tag( pos + 1, p - pos - 1 );
			reArrange(tag);

            if( is_version( tag ) )
                ;//std::cout << tag << "\n";
			else if(is_help(tag))
				;
            else if( is_end( tag ) )
            {
                if( s && e )
                {
                    std::string data( s + 1, e -s - 1 );
                    on_data( data );
                    s = e = 0;
                }
                on_end( tag );
            }
            else if( is_element( tag ) )
            {
                parse_property( tag );
                on_end( tag );
            }
            else
            {
				
                parse_property( tag );
                s = p;//记下数据开位置
            }
        }
        p++;
        len--;
    }
}
Beispiel #3
0
void websocket_hybi_17::handle_data(bool is_binary)
{
	if ( state() != OPEN )
	{
		return;
	}

	std::istreambuf_iterator<char> beg(&read_buf_), end;
	buffer data(beg, end);

	if ( !is_client_side() )
	{
		bool const is_masked = (frame_start_[1] & 0x80) != 0;
		_aspect_assert(is_masked);
		for (size_t i = 0, count = data.size(); i != count; ++i)
		{
			data[i] ^= frame_mask_[i % 4];
		}
	}
	on_data(data, is_binary);
}
void EmitterBase::close(Callback<> cb) {
    if (close_pending) {
        /*
         * Rationale for throwing rather than ignoring: (1) it was the
         * behavior before we started refactoring; (2) the user expects
         * a callback to be called after close is successful. If we do
         * ignore subsequent close attempts, the promise that the callback
         * will be called is silently broken, and it seems instead better
         * to inform the caller that there is a problem with the code
         * because `close()` has been called more than once.
         */
        throw std::runtime_error("close already pending");
    }
    close_pending = true;
    shutdown();
    on_connect(nullptr);
    on_data(nullptr);
    on_flush(nullptr);
    on_error(nullptr);
    close_cb = cb;
}
Beispiel #5
0
  void Parser::checkOutputBuffer() {

    int to_read = 0; 
    int nread = 0;

    /* is there data we need to send? */
    int pending = BIO_ctrl_pending(out_bio);
    if (pending <= 0) {
      return;
    }

    while(pending) {

      /* how many bytes to read. */
      if (pending >= DTLS_BUFFER_SIZE) {
        to_read = DTLS_BUFFER_SIZE;
      }
      else {
        to_read = pending;
      }

      nread = BIO_read(out_bio, buffer, to_read);
      if (nread != to_read) {
        printf("dtls::Parser - error: failed readig the necessary amount of bytes from the out bio.\n");
        exit(1);
      }
      
      if (on_data) {
        on_data(buffer, nread, user);
      }
      
      pending -= to_read;
    }
    
    printf("dtls::Parser - verbose: pending in out_bio: %d\n", pending);
  }
Beispiel #6
0
void HttpServerConnection::read() {
	while(!is_closed()) {
		switch(read_state) {
		case LINE:
			// get the request line
			try {
				if(!async_read_in(line,sizeof(uri)-1))
					return;
			} catch(EndOfStreamError*) {
				if(!count)
					throw;
				// so we get end-of-stream when keep-alive?  no problem
				return;
			}
			if(!line.ends_with("\r\n",2))
				HttpError::Throw(HttpError::ERequestURITooLong,*this);
			if(!memcmp(line.cstr(),"\r\n",3)) { // empty lines are ok before request line
				line.clear();
				continue;
			}
			count++;
			read_state = HEADER;
			{
				const char* method = strtok(line.cstr()," ");
				strncpy(uri,strtok(NULL," \r"),sizeof(this->uri));
				const char* v = strtok(NULL,"\r");
				if(!memcmp(v,"HTTP/1.1",9))
					version = HTTP_1_1;
				else if(!memcmp(v,"HTTP/1.0",9))
					version = HTTP_1_0;
				else
					version = HTTP_0_9;
				in_encoding_chunked = false;
				in_content_length = -1; // not known
				keep_alive = out_encoding_chunked = (HTTP_1_1 == version);
				on_request(method,uri);
			}
			line.clear();
			break;
		case HEADER:
			if(!async_read_in(line))
				return;
			if(!memcmp("\r\n",line.cstr(),3)) {
				read_state = BODY;
				if(keep_alive && !in_encoding_chunked && (-1 == in_content_length))
					in_content_length = 0; // length isn't specified, yet its keep-alive, so there is no content
				line.clear();
				on_body();
				break;
			}
			if(!line.ends_with("\r\n",2))
				HttpError::Throw(HttpError::ERequestEntityTooLarge,*this);
			{
				const char* header = strtok(line.cstr()," "), *value = strtok(NULL,"\r");
				if(!ends_with(header,":",1))
					HttpError::Throw(HttpError::EBadRequest,*this);
				if((write_state == LINE) && !strcasecmp(header,"connection:") && !strcasecmp(value,"keep-alive"))
					keep_alive = true;
				else if(!strcasecmp(header,"content-length:")) {
					in_content_length = atoi(value);
					if(in_content_length < 0)
						HttpError::Throw(HttpError::EBadRequest,*this);
				} else if(!strcasecmp(header,"transfer-encoding:"))
					in_encoding_chunked = !strcasecmp(value,"chunked");
				on_header(header,value);
			}
			line.clear();
			break;
		case BODY:
			if(in_encoding_chunked) { //RFC2616-s4.4 says this overrides any explicit content-length header
				ThrowInternalError("in encoding chunked not implemented yet");
			} else if(!keep_alive && (-1 == in_content_length)) {
				// read all available
				uint8_t* chunk;
				while(uint16_t len = async_read_buffered(chunk))
					on_data(chunk,len);
			} else if(-1 != in_content_length) {
				// read all available
				while(in_content_length) {
					uint8_t* chunk;
					if(const uint16_t len = async_read_buffered(chunk,in_content_length)) {
						in_content_length -= len;
						on_data(chunk,len);
					} else
						return;
				}
				if(!keep_alive) {
					read_state = FINISHED;
					shutdown(fd,SHUT_RD);
					return;
				}
				read_state = LINE;
			} else
				ThrowInternalError("cannot cope with combination of keep_alive %d, content_length %d and encoding_chunked %d",
					keep_alive,in_content_length,in_encoding_chunked);
			break;
		default:
			ThrowInternalError("unexpected read_state");
		}
	}
}
int
main (
	int		argc,
	char*		argv[]
	)
{
	int e;
	pgm_error_t* pgm_err = NULL;

	setlocale (LC_ALL, "");

	log_init ();
	g_message ("syncrecv");

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

	g_quit = FALSE;
#ifdef G_OS_UNIX
	e = pipe (g_quit_pipe);
#else
	e = _pipe (g_quit_pipe, 4096, _O_BINARY | _O_NOINHERIT);
#endif
	g_assert (0 == e);

/* 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");
		exit(1);
	}

/* dispatch loop */
	g_message ("entering PGM message loop ... ");
	do {
		struct timeval tv;
		int timeout;
		int n_fds = 2;
		struct pollfd fds[ 1 + n_fds ];
		char buffer[4096];
		gsize len;
		pgm_tsi_t from;
		const int status = pgm_recvfrom (g_transport,
					         buffer,
					         sizeof(buffer),
						 0,
					         &len,
					         &from,
					         &pgm_err);
		switch (status) {
		case PGM_IO_STATUS_NORMAL:
			on_data (buffer, len, &from);
			break;
		case PGM_IO_STATUS_TIMER_PENDING:
			pgm_transport_get_timer_pending (g_transport, &tv);
			goto block;
		case PGM_IO_STATUS_RATE_LIMITED:
			pgm_transport_get_rate_remaining (g_transport, &tv);
		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));
			memset (fds, 0, sizeof(fds));
			fds[0].fd = g_quit_pipe[0];
			fds[0].events = POLLIN;
			pgm_transport_poll_info (g_transport, &fds[1], &n_fds, POLLIN);
			poll (fds, 1 + n_fds, 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 (g_quit_pipe[0]);
	close (g_quit_pipe[1]);

	if (g_transport) {
		g_message ("destroying transport.");

		pgm_transport_destroy (g_transport, TRUE);
		g_transport = NULL;
	}

	g_message ("PGM engine shutdown.");
	pgm_shutdown ();
	g_message ("finished.");
	return EXIT_SUCCESS;
}
Beispiel #8
0
int
main (
	int		argc,
	char*		argv[]
	)
{
	pgm_error_t* pgm_err = NULL;

	setlocale (LC_ALL, "");

#ifndef _WIN32
	puts ("プリン プリン");
#else
	_putws (L"プリン プリン");
#endif

	if (!pgm_init (&pgm_err)) {
		fprintf (stderr, "Unable to start PGM engine: %s\n", 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:f:K:N:lih")) != -1)
	{
		switch (c) {
		case 'n':	network = optarg; break;
		case 's':	port = atoi (optarg); break;
		case 'p':	udp_encap_port = atoi (optarg); break;
		case 'f':	use_fec = TRUE; break;
		case 'K':	rs_k = atoi (optarg); break;
		case 'N':	rs_n = atoi (optarg); break;
		case 'l':	use_multicast_loop = TRUE; break;

		case 'i':
			pgm_if_print_all();
			return EXIT_SUCCESS;

		case 'h':
		case '?': usage (binary_name);
		}
	}

	if (use_fec && ( !rs_n || !rs_k )) {
		fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k);
		usage (binary_name);
	}

/* setup signal handlers */
#ifdef SIGHUP
	signal (SIGHUP,  SIG_IGN);
#endif
#ifndef _WIN32
	int e = pipe (terminate_pipe);
	assert (0 == e);
	signal (SIGINT,  on_signal);
	signal (SIGTERM, on_signal);
#else
	terminate_event = CreateEvent (NULL, TRUE, FALSE, TEXT("TerminateEvent"));
	SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE);
#endif /* !_WIN32 */

	if (!on_startup()) {
		fprintf (stderr, "Startup failed\n");
		return EXIT_FAILURE;
	}

/* dispatch loop */
#ifndef _WIN32
	int fds;
	fd_set readfds;
#else
	int n_handles = 3, recv_sock, pending_sock;
	HANDLE waitHandles[ 3 ];
	DWORD dwTimeout, dwEvents;
	WSAEVENT recvEvent, pendingEvent;

	recvEvent = WSACreateEvent ();
	pgm_getsockopt (sock, PGM_RECV_SOCK, &recv_sock, sizeof(recv_sock));
	WSAEventSelect (recv_sock, recvEvent, FD_READ);
	pendingEvent = WSACreateEvent ();
	pgm_getsockopt (sock, PGM_PENDING_SOCK, &pending_sock, sizeof(pending_sock));
	WSAEventSelect (pending_sock, pendingEvent, FD_READ);

	waitHandles[0] = terminate_event;
	waitHandles[1] = recvEvent;
	waitHandles[2] = pendingEvent;
#endif /* !_WIN32 */
	puts ("Entering PGM message loop ... ");
	do {
		struct timeval tv;
		char buffer[4096];
		size_t len;
		pgm_tsi_t from;
		const int status = pgm_recvfrom (sock,
					         buffer,
					         sizeof(buffer),
					         0,
					         &len,
					         &from,
					         &pgm_err);
		switch (status) {
		case PGM_IO_STATUS_NORMAL:
			on_data (buffer, len, &from);
			break;
		case PGM_IO_STATUS_TIMER_PENDING:
			pgm_getsockopt (sock, PGM_TIME_REMAIN, &tv, sizeof(tv));
			goto block;
		case PGM_IO_STATUS_RATE_LIMITED:
			pgm_getsockopt (sock, PGM_RATE_REMAIN, &tv, sizeof(tv));
		case PGM_IO_STATUS_WOULD_BLOCK:
/* select for next event */
block:
#ifndef _WIN32
			fds = terminate_pipe[0] + 1;
			FD_ZERO(&readfds);
			FD_SET(terminate_pipe[0], &readfds);
			pgm_select_info (sock, &readfds, NULL, &fds);
			fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv);
#else
			dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv.tv_usec / 1000));
			dwEvents = WaitForMultipleObjects (n_handles, waitHandles, FALSE, dwTimeout);
			switch (dwEvents) {
			case WAIT_OBJECT_0+1: WSAResetEvent (recvEvent); break;
			case WAIT_OBJECT_0+2: WSAResetEvent (pendingEvent); break;
			default: break;
			}
#endif /* !_WIN32 */
			break;

		default:
			if (pgm_err) {
				fprintf (stderr, "%s\n", pgm_err->message);
				pgm_error_free (pgm_err);
				pgm_err = NULL;
			}
			if (PGM_IO_STATUS_ERROR == status)
				break;
		}
	} while (!is_terminated);

	puts ("Message loop terminated, cleaning up.");

/* cleanup */
#ifndef _WIN32
	close (terminate_pipe[0]);
	close (terminate_pipe[1]);
#else
	WSACloseEvent (recvEvent);
	WSACloseEvent (pendingEvent);
	CloseHandle (terminate_event);
#endif /* !_WIN32 */

	if (sock) {
		puts ("Destroying PGM socket.");
		pgm_close (sock, TRUE);
		sock = NULL;
	}

	puts ("PGM engine shutdown.");
	pgm_shutdown ();
	puts ("finished.");
	return EXIT_SUCCESS;
}
Beispiel #9
0
int
main (
	int		argc,
	char*		argv[]
	)
{
	pgm_error_t* pgm_err = NULL;

	setlocale (LC_ALL, "");

#ifndef _WIN32
	puts ("いちごのショートケーキ");
#else
	puts ("ichigo no shōtokēki");
#endif

	if (!pgm_init (&pgm_err)) {
		fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message);
		pgm_error_free (pgm_err);
		return EXIT_FAILURE;
	}

/* parse program arguments */
#ifdef _WIN32
	const char* binary_name = strrchr (argv[0], '\\');
#else
	const char* binary_name = strrchr (argv[0], '/');
#endif
	if (NULL == binary_name)	binary_name = argv[0];
	else				binary_name++;

	int c;
	while ((c = getopt (argc, argv, "s:n:p:f:K:N:lih")) != -1)
	{
		switch (c) {
		case 'n':	network = optarg; break;
		case 's':	port = atoi (optarg); break;
		case 'p':	udp_encap_port = atoi (optarg); break;
		case 'f':	use_fec = TRUE; break;
		case 'K':	rs_k = atoi (optarg); break;
		case 'N':	rs_n = atoi (optarg); break;
		case 'l':	use_multicast_loop = TRUE; break;

		case 'i':
			pgm_if_print_all();
			return EXIT_SUCCESS;

		case 'h':
		case '?': usage (binary_name);
		}
	}

	if (use_fec && ( !rs_n || !rs_k )) {
		fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k);
		usage (binary_name);
	}

/* setup signal handlers */
#ifdef SIGHUP
	signal (SIGHUP,  SIG_IGN);
#endif
#ifndef _WIN32
	int e = pipe (terminate_pipe);
	assert (0 == e);
	signal (SIGINT,  on_signal);
	signal (SIGTERM, on_signal);
#else
	terminateEvent = WSACreateEvent ();
	SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE);
	setvbuf (stdout, (char *) NULL, _IONBF, 0);
#endif /* !_WIN32 */

	if (!on_startup()) {
		fprintf (stderr, "Startup failed\n");
		return EXIT_FAILURE;
	}

/* dispatch loop */
#ifndef _WIN32
	int fds, read_fd = async_get_socket (async);
	fd_set readfds;
#else
	DWORD cEvents = 2;
	WSAEVENT waitEvents[ 2 ];
	DWORD dwEvents;

	waitEvents[0] = terminateEvent;
	waitEvents[1] = async_get_event (async);
#endif /* !_WIN32 */
	puts ("Entering PGM message loop ... ");
	do {
		char buffer[4096];
		struct pgm_sockaddr_t from;
		socklen_t fromlen = sizeof (from);
		const ssize_t len = async_recvfrom (async,
					            buffer,
					            sizeof(buffer),
					            &from,
						    &fromlen);
		if (len >= 0) {
			on_data (buffer, len, &from);
		} else {
#ifndef _WIN32
			fds = MAX(terminate_pipe[0], read_fd) + 1;
			FD_ZERO(&readfds);
			FD_SET(terminate_pipe[0], &readfds);
			FD_SET(read_fd, &readfds);
			fds = select (fds, &readfds, NULL, NULL, NULL);
#else
			dwEvents = WSAWaitForMultipleEvents (cEvents, waitEvents, FALSE, WSA_INFINITE, FALSE);
			switch (dwEvents) {
			case WSA_WAIT_EVENT_0+1: WSAResetEvent (waitEvents[1]); break;
			default: break;
			}
#endif /* _WIN32 */
		}
	} while (!is_terminated);

	puts ("Message loop terminated, cleaning up.");

/* cleanup */
#ifndef _WIN32
	close (terminate_pipe[0]);
	close (terminate_pipe[1]);
#else
	WSACloseEvent (terminateEvent);
#endif /* !_WIN32 */

	if (async) {
		puts ("Destroying asynchronous queue.");
		async_destroy (async);
		async = NULL;
	}

	if (sock) {
		puts ("Closing PGM socket.");
		pgm_close (sock, TRUE);
		sock = NULL;
	}

	puts ("PGM engine shutdown.");
	pgm_shutdown ();
	puts ("finished.");
	return EXIT_SUCCESS;
}
Beispiel #10
0
int
main (
	int		argc,
	char*		argv[]
	)
{
	pgm_error_t* pgm_err = NULL;

	setlocale (LC_ALL, "");

	log_init ();
	g_message ("blocksyncrecv");

	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);
#ifdef SIGHUP
	signal(SIGHUP,  SIG_IGN);
#endif
#ifdef G_OS_UNIX
	signal(SIGINT,  on_signal);
	signal(SIGTERM, on_signal);
#else
	SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE);
	setvbuf (stdout, (char *) NULL, _IONBF, 0);
#endif

	on_startup();

/* dispatch loop */
	g_message ("entering PGM message loop ... ");
	do {
		char buffer[4096];
		size_t len;
		struct pgm_sockaddr_t from;
		socklen_t fromlen = sizeof(from);
		const int status = pgm_recvfrom (g_sock,
					         buffer,
					         sizeof(buffer),
					         0,
					         &len,
					         &from,
						 &fromlen,
					         &pgm_err);
		if (PGM_IO_STATUS_NORMAL == status)
			on_data (buffer, len, &from);
		else {
			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 */
	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;
}
Beispiel #11
0
int
main (
	int		argc,
	char*		argv[]
	)
{
	pgm_error_t* pgm_err = NULL;

	setlocale (LC_ALL, "");

#if !defined(_WIN32)
	puts ("プリン プリン");
#else
/* Windows consoles have incredibly limited Unicode support */
	puts ("purin purin");
#endif
	if (!pgm_init (&pgm_err)) {
		fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message);
		pgm_error_free (pgm_err);
		return EXIT_FAILURE;
	}

/* parse program arguments */
#ifdef _WIN32
	const char* binary_name = strrchr (argv[0], '\\');
#else
	const char* binary_name = strrchr (argv[0], '/');
#endif
	if (NULL == binary_name)	binary_name = argv[0];
	else				binary_name++;

	int c;
	while ((c = getopt (argc, argv, "s:n:p:cf:K:N:lih")) != -1)
	{
		switch (c) {
		case 'n':	network = optarg; break;
		case 's':	port = atoi (optarg); break;
		case 'p':	udp_encap_port = atoi (optarg); break;
		case 'c':	use_pgmcc = TRUE; break;
		case 'f':	use_fec = TRUE; break;
		case 'K':	rs_k = atoi (optarg); break;
		case 'N':	rs_n = atoi (optarg); break;
		case 'l':	use_multicast_loop = TRUE; break;

		case 'i':
			pgm_if_print_all();
			return EXIT_SUCCESS;

		case 'h':
		case '?': usage (binary_name);
		}
	}

	if (use_fec && ( !rs_n || !rs_k )) {
		fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k);
		usage (binary_name);
	}

/* setup signal handlers */
#ifdef SIGHUP
	signal (SIGHUP,  SIG_IGN);
#endif
#ifndef _WIN32
	int e = pipe (terminate_pipe);
	assert (0 == e);
	signal (SIGINT,  on_signal);
	signal (SIGTERM, on_signal);
#else
	terminateEvent = WSACreateEvent();
	SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE);
	setvbuf (stdout, (char *) NULL, _IONBF, 0);
#endif /* !_WIN32 */

	if (!on_startup()) {
		fprintf (stderr, "Startup failed\n");
		return EXIT_FAILURE;
	}

/* dispatch loop */
#ifndef _WIN32
	int fds;
	fd_set readfds;
#else
	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] = terminateEvent;
	waitEvents[1] = WSACreateEvent();
	waitEvents[2] = WSACreateEvent();
	assert (2 == PGM_RECV_SOCKET_READ_COUNT);
	pgm_getsockopt (sock, IPPROTO_PGM, PGM_RECV_SOCK, &recv_sock, &socklen);
	WSAEventSelect (recv_sock, waitEvents[1], FD_READ);
	pgm_getsockopt (sock, IPPROTO_PGM, PGM_PENDING_SOCK, &pending_sock, &socklen);
	WSAEventSelect (pending_sock, waitEvents[2], FD_READ);
#endif /* !_WIN32 */
	puts ("Entering PGM message loop ... ");
	do {
		struct timeval tv;
#ifdef _WIN32
		DWORD dwTimeout, dwEvents;
#endif
		char buffer[4096];
		size_t len;
		struct pgm_sockaddr_t from;
		socklen_t fromlen = sizeof (from);
		const int status = pgm_recvfrom (sock,
					         buffer,
					         sizeof(buffer),
					         0,
					         &len,
					         &from,
						 &fromlen,
					         &pgm_err);
		switch (status) {
		case PGM_IO_STATUS_NORMAL:
			on_data (buffer, len, &from);
			break;
		case PGM_IO_STATUS_TIMER_PENDING:
			{
				socklen_t optlen = sizeof (tv);
				pgm_getsockopt (sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen);
			}
			goto block;
		case PGM_IO_STATUS_RATE_LIMITED:
			{
				socklen_t optlen = sizeof (tv);
				pgm_getsockopt (sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen);
			}
		case PGM_IO_STATUS_WOULD_BLOCK:
/* select for next event */
block:
#ifndef _WIN32
			fds = terminate_pipe[0] + 1;
			FD_ZERO(&readfds);
			FD_SET(terminate_pipe[0], &readfds);
			pgm_select_info (sock, &readfds, NULL, &fds);
			fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv);
#else
			dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? WSA_INFINITE : (DWORD)((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 /* !_WIN32 */
			break;

		default:
			if (pgm_err) {
				fprintf (stderr, "%s\n", pgm_err->message);
				pgm_error_free (pgm_err);
				pgm_err = NULL;
			}
			if (PGM_IO_STATUS_ERROR == status)
				break;
		}
	} while (!is_terminated);

	puts ("Message loop terminated, cleaning up.");

/* cleanup */
#ifndef _WIN32
	close (terminate_pipe[0]);
	close (terminate_pipe[1]);
#else
	WSACloseEvent (waitEvents[0]);
	WSACloseEvent (waitEvents[1]);
	WSACloseEvent (waitEvents[2]);
#endif /* !_WIN32 */

	if (sock) {
		puts ("Destroying PGM socket.");
		pgm_close (sock, TRUE);
		sock = NULL;
	}

	puts ("PGM engine shutdown.");
	pgm_shutdown ();
	puts ("finished.");
	return EXIT_SUCCESS;
}