/*
 * We end up here due to a osegw_receive, osegw_receive_w_tmo or
 * osegw_init_async_receive. While we are waiting for a signal,
 * we must be able to handle "pings" and osegw_cancel_async_receive
 * from the client.
 */
int OseGW_PLT_ReceiveRequest_cbk(int skt, int len, char *payload,
                                 struct ClientInfo *cinfo)
{
	struct OseGW_ReceiveRequest *req;
        struct OseGW_TransportHdr thdr;
        union LINX_SIGNAL *sig;
        LINX_SIGSELECT *sigsel;
        int nfds, status, size, linx_skt, sigsel_len;
        fd_set rfds;
        struct timeval tv0, tv, *tvp;
        unsigned int tmo;
        void *buf;

        linx_skt = linx_get_descriptor(cinfo->linx);
	req = (struct OseGW_ReceiveRequest *)payload;
        sigsel_len = ntohl(req->sigsel_len);
        buf = NULL;

        /* 0. This may be a osegw_cancel_async_receive... */
        if (sigsel_len == 0) {
                sig = NULL;
                size = 0;
                goto out;
        }

        /* 1. Setup signal filter that should be used while polling... */
        sigsel = copy_sigselect(req);
        if (sigsel == NULL)
                goto e_exit;
        if (set_sigselect(linx_skt, sigsel) == -1)
                goto e_exit;

        /* 2. Setup time-out... */
	tmo = ntohl(req->timeout);
        if (tmo != (unsigned int)~0) {
                tvp = msec_to_timeval(tmo, &tv);
                if (gettimeofday(&tv0, NULL) == -1)
                        goto e_exit;
        } else
                tvp = NULL; /* Infinite */

  again:

        /* 3. Setup descriptors... */
        FD_ZERO(&rfds);
        FD_SET(linx_skt, &rfds); /* LINX socket */
        FD_SET(skt, &rfds);      /* TCP socket */
        nfds = linx_skt > skt ? linx_skt : skt;

        /* 4. Wait for a signal, ping or osegw_cancel_async_receive */
        status = select(nfds + 1, &rfds, NULL, NULL, tvp);
        if (status == -1)
                goto e_exit;

        if (status == 0) {
                /* osegw_receive_w_tmo has timed out */
                sig = NULL;
                size = 0;
                goto out;
        }

        if (FD_ISSET(linx_skt, &rfds)) {
                /* A signal that matches the signal filter is available */
                status = linx_receive(cinfo->linx, &sig, sigsel);
                if (status == -1)
                        goto e_exit;
                size = linx_sigsize(cinfo->linx, &sig) - sizeof(SIGSELECT);
                goto out;
        }

        if (FD_ISSET(cinfo->sd, &rfds)) {
                /* Get command */
                buf = get_command(skt, &thdr);
                if (buf == NULL)
                        goto e_exit;

                switch (thdr.payload_type) {
                case OseGW_PLT_InterfaceRequest:
                        status = OseGW_PLT_InterfaceRequest_cbk(skt, thdr.payload_len, buf, cinfo);
                        if (status == -1)
                                goto e_exit;
                        /* Compensate for the time spent in select */
                        if (tvp != NULL)
                                recalc_tmo(tmo, &tv0, &tv);
                        goto again;
                        break;
                case OseGW_PLT_ReceiveRequest:
                        req = (struct OseGW_ReceiveRequest *)buf;
                        sigsel_len = ntohl(req->sigsel_len);
                        if (sigsel_len != 0)
                                goto e_exit; /* Only cancel async receive is allowed */
                        sig = NULL;
                        size = 0;
                        goto out;
                        break;
                default:
                        syslog(LOG_INFO, "Gateway protocol violation detected, "
                               "got type %d while in a receive", thdr.payload_type);
                        goto e_exit;
                        break;
                }
        }
  out:
        free(buf);
        free(sigsel);
        return OseGW_PLT_ReceiveReply_cbk(skt, size, (char *)sig, cinfo);

  e_exit:
        free(buf);
        free(sigsel);
        return -1;
}
static void test_without_linxlib(LINX_SPID client, LINX * linx)
{
	int len, sig_len;
	struct sockaddr_linx to;
	socklen_t socklen;
	int sd = linx_get_descriptor(linx);
	size_t msg_size = 65536;
	union LINX_SIGNAL *sig = malloc(msg_size);

	if (sig == NULL) {
		ERR("Failed to allocate memory");
		exit(1);
	}
	socklen = sizeof(struct sockaddr_linx);
	to.family = AF_LINX;
	to.spid = LINX_ILLEGAL_SPID;

	for (;;) {
		len = sig_len = recvfrom(sd,
					 sig,
					 msg_size,
					 0, (struct sockaddr *)(void *)&to,
					 &socklen);

		if (unlikely(len <= 0)) {
			ERR("Failed to receive a signal(%d, %d)\n", len, errno);
			continue;
		}

		if (unlikely(to.spid != client)) {
			WARN("Ignoring signal from spid %#x since it is"
			     " not %#x", to.spid, client);
			continue;
		}

		switch (sig->sigNo) {
		case ECHO_SIG:
			TRACE(1, "Sending echo");

			len = sendto(sd, sig, len,
				     0, (struct sockaddr *)(void *)&to,
				     socklen);
			if (unlikely(sig_len != len)) {
				ERR("sendto returned: %d when asked "
				    "for: %d", len, sig_len);
			}
			if (unlikely(len <= 0)) {
				ERR("Failed to send the echo signal "
				    "(%d, %s)\n", len, strerror(errno));
			}
			break;

		case TERMINATE_REQ:
			TRACE(1, "Terminating");
			free(sig);
			linx_close(linx);
			return;

		case ATTACH_TEST_REQ:
			len = sendto(sd, sig, len,
				     0, (struct sockaddr *)(void *)&to,
				     socklen);
			if (unlikely(sig_len != len)) {
				ERR("sendto returned: %d when asked "
				    "for: %d", len, sig_len);
			}
			if (unlikely(len <= 0)) {
				ERR("Failed to send the echo signal "
				    "(%d, %s)\n", len, strerror(errno));
			}
			linx_close(linx);
			return;

		case BURST_REQ:
			{
				uint32_t sigCnt = ntohl(sig->burstReq.n);
				uint32_t reply_size =
				    ntohl(sig->burstReq.reply_size);

				LINX_SIGSELECT reply_sigNo =
				    ntohl(sig->burstReq.reply_sigNo);

				/* Allocate all signals before sending then to
				 * make the send loop as tight as possible */
				sig->burstSig.sigNo = reply_sigNo;
				while (sigCnt--) {
					len = sendto(sd, sig, reply_size,
						     0,
						     (struct sockaddr *)(void *)&to,
						     socklen);
					if (unlikely
					    (reply_size != (uint32_t) len)) {
						ERR("sendto returned: %d when "
						    "asked for: %d", len,
						    reply_size);
					}
					if (unlikely(len <= 0)) {
						ERR("Failed to send the echo "
						    "signal (%d, %s)\n",
						    len, strerror(errno));
					}
				}
				break;
			}

		default:
			WARN("Ignoring unknown signal %ud", sig->sigNo);
		}
	}
	free(sig);
}