/*
 * util/tube.h - pipe service
 *
 * Copyright (c) 2008, NLnet Labs. All rights reserved.
 *
 * This software is open source.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * 
 * Neither the name of the NLNET LABS nor the names of its contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * \file
 *
 * This file contains pipe service functions.
 */

#ifndef UTIL_TUBE_H
#define UTIL_TUBE_H
struct comm_reply;
struct comm_point;
struct comm_base;
struct tube;
struct tube_res_list;
#ifdef USE_WINSOCK
#include "util/locks.h"
#include "util/winsock_event.h"
#endif

/**
 * Callback from pipe listen function
 * void mycallback(tube, msg, len, error, user_argument);
 * if error is true (NETEVENT_*), msg is probably NULL.
 */
typedef void tube_callback_t(struct tube*, uint8_t*, size_t, int, void*);

/**
 * A pipe
 */
struct tube {
#ifndef USE_WINSOCK
	/** pipe end to read from */
	int sr;
	/** pipe end to write on */
	int sw;

	/** listen commpoint */
	struct comm_point* listen_com;
	/** listen callback */
	tube_callback_t* listen_cb;
	/** listen callback user arg */
	void* listen_arg;
	/** are we currently reading a command, 0 if not, else bytecount */
	size_t cmd_read;
	/** size of current read command, may be partially read */
	uint32_t cmd_len;
	/** the current read command content, malloced, can be partially read*/
	uint8_t* cmd_msg;

	/** background write queue, commpoint to write results back */
	struct comm_point* res_com;
	/** are we currently writing a result, 0 if not, else bytecount into
	 * the res_list first entry. */
	size_t res_write;
	/** list of outstanding results to be written back */
	struct tube_res_list* res_list;
	/** last in list */
	struct tube_res_list* res_last;

#else /* USE_WINSOCK */
	/** listen callback */
	tube_callback_t* listen_cb;
	/** listen callback user arg */
	void* listen_arg;
	/** the windows sockets event (signaled if items in pipe) */
	WSAEVENT event;
	/** winsock event storage when registered with event base */
	struct event ev_listen;

	/** lock on the list of outstanding items */
	lock_basic_t res_lock;
	/** list of outstanding results on pipe */
	struct tube_res_list* res_list;
	/** last in list */
	struct tube_res_list* res_last;
#endif /* USE_WINSOCK */
};

/**
 * List of results (arbitrary command serializations) to write back
 */
struct tube_res_list {
	/** next in list */
	struct tube_res_list* next;
	/** serialized buffer to write */
	uint8_t* buf;
	/** length to write */
	uint32_t len;
};

/**
 * Create a pipe
 * @return: new tube struct or NULL on error.
 */
struct tube* tube_create(void);

/**
 * Delete and destroy a pipe
 * @param tube: to delete
 */
void tube_delete(struct tube* tube);

/**
 * Write length bytes followed by message.
 * @param tube: the tube to write on.
 *     If that tube is a pipe, its write fd is used as
 *     the socket to write on. Is nonblocking.
 *      Set to blocking by the function,
 *      and back to non-blocking at exit of function.
 * @param buf: the message.
 * @param len: length of message.
 * @param nonblock: if set to true, the first write is nonblocking.
 *      If the first write fails the function returns -1.
 *      If set false, the first write is blocking.
 * @return: all remainder writes are nonblocking.
 *      return 0 on error, in that case blocking/nonblocking of socket is
 *              unknown.
 *      return 1 if all OK.
 */
int tube_write_msg(struct tube* tube, uint8_t* buf, uint32_t len, 
	int nonblock);

/**
 * Read length bytes followed by message.
 * @param tube: The tube to read on.
 *     If that tube is a pipe, its read fd is used as
 *     the socket to read on. Is nonblocking.
 *      Set to blocking by the function,
 *      and back to non-blocking at exit of function.
 * @param buf: the message, malloced.
 * @param len: length of message, returned.
 * @param nonblock: if set to true, the first read is nonblocking.
 *      If the first read fails the function returns -1.
 *      If set false, the first read is blocking.
 * @return: all remainder reads are nonblocking.
 *      return 0 on error, in that case blocking/nonblocking of socket is 
 *              unknown. On EOF 0 is returned.
 *      return 1 if all OK.
 */
int tube_read_msg(struct tube* tube, uint8_t** buf, uint32_t* len, 
	int nonblock);

/**
 * Close read part of the pipe.
 * The tube can no longer be read from.
 * @param tube: tube to operate on.
 */
void tube_close_read(struct tube* tube);

/**
 * Close write part of the pipe.
 * The tube can no longer be written to.
 * @param tube: tube to operate on.
 */
void tube_close_write(struct tube* tube);

/**
 * See if data is ready for reading on the tube without blocking.
 * @param tube: tube to check for readable items
 * @return true if readable items are present. False if not (or error).
 *     true on pipe_closed.
 */
int tube_poll(struct tube* tube);

/**
 * Wait for data to be ready for reading on the tube. is blocking.
 * No timeout.
 * @param tube: the tube to wait on.
 * @return: if there was something to read (false on error).
 *     true on pipe_closed.
 */
int tube_wait(struct tube* tube);

/**
 * Get FD that is readable when new information arrives.
 * @param tube
 * @return file descriptor.
 */
int tube_read_fd(struct tube* tube);

/**
 * Start listening for information over the pipe.
 * Background registration of a read listener, callback when read completed.
 * Do not mix with tube_read_msg style direct reads from the pipe.
 * @param tube: tube to listen on
 * @param base: what base to register event callback.
 * @param cb: callback routine.
 * @param arg: user argument for callback routine.
 * @return true if successful, false on error.
 */
int tube_setup_bg_listen(struct tube* tube, struct comm_base* base,
	tube_callback_t* cb, void* arg);

/**
 * Remove bg listen setup from event base.
 * @param tube: what tube to cleanup
 */
void tube_remove_bg_listen(struct tube* tube);

/**
 * Start background write handler for the pipe.
 * Do not mix with tube_write_msg style direct writes to the pipe.
 * @param tube: tube to write on
 * @param base: what base to register event handler on.
 * @return true if successful, false on error.
 */
int tube_setup_bg_write(struct tube* tube, struct comm_base* base);

/**
 * Remove bg write setup from event base.
 * @param tube: what tube to cleanup
 */
void tube_remove_bg_write(struct tube* tube);


/**
 * Append data item to background list of writes.
 * Mallocs a list entry behind the scenes.
 * Not locked behind the scenes, call from one thread or lock on outside.
 * @param tube: what tube to queue on.
 * @param msg: memory message to send. Is free()d after use.
 * 	Put at the end of the to-send queue.
 * @param len: length of item.
 * @return 0 on failure (msg freed).
 */
int tube_queue_item(struct tube* tube, uint8_t* msg, size_t len);

/** for fptr wlist, callback function */
int tube_handle_listen(struct comm_point* c, void* arg, int error, 
	struct comm_reply* reply_info);

/** for fptr wlist, callback function */
int tube_handle_write(struct comm_point* c, void* arg, int error, 
	struct comm_reply* reply_info);

/** for fptr wlist, winsock signal event callback function */
void tube_handle_signal(int fd, short events, void* arg);

#endif /* UTIL_TUBE_H */
