|
|
(5 intermediate revisions by the same user not shown) |
Line 1: |
Line 1: |
| // $Id: lion_library.txt,v 1.21 2005/12/28 03:16:01 lundman Exp $
| | As I wrote about the first experience I thought I might as complete it and write one for the birth of our second child, Kajerik. Last time a fair bit of the focus was on child birth as well as a large chunk around the topic of being in Japan. But the Japan part did not differ much, so it is not a very interesting story in that aspect. |
|
| |
|
| Lund's Input Output Library (lion) by Jorgen Lundman <lundman@lundman.net>
| | I was at work on Thursday the 8th of Feb, having returned from lunch a little earlier. I get a call from Ushka at 15:00, which in itself is unusual enough to tell me something was up. Since ordinarily we just chat on IRC, or send phone messages, and since I was at work, nobody from work would call me. Ushka said her water was broken, but nothing else was going on. She gave a definite "you should come home" this time so there was no decision there. I handed off my current/only important task to Erin (sorry), popped downstairs to take out some cash (see? No panic this time around!) and got in a Taxi. |
| (Document Version v1.14)
| |
| The source location of this documentation is at
| |
| http://www.lundman.net/wiki/index.php/LiON_Documentation
| |
|
| |
|
| lion library was written by Jorgen Lundman <lundman@lundman.net>
| | Unfortunately the driver picked to go through Shibuya, a route I dislike. It may be technically a shorter distance, but it takes longer (and cost more!) than going the Yamate-dori way. I sent an email off to the grand parents, and coordinated things with Ushka. She had already called Emily and the Hoikuen so they were prepared. Ironically, by the time I got home, I could have cycled faster, but oh well. |
| chiefly as a development tool for himself, to aid in making quick
| |
| programs that are networked, non-blocking and portable.
| |
|
| |
|
| I've tried to make sure it is as complete as possible, so that all | | I went up to the apartment and helped with the bags. Ushka was quite calm too, and we caught the first Taxi to the hospital. Quite a relaxing time this time, no contractions though. We naively discussed the possibility that I might be home again by 22:00 or so. (Since Lokien's labour took about 5 hours). Hah! |
| types are fully buffered, in and out. All features I could think of
| |
| that belong in the library are there and complete. It was
| |
| particularly hard to implement nonblocking files under all version of
| |
| Windows, as well as the lion_fork() function.
| |
|
| |
|
| | After all that, we arrived at Seibo Hospital at 16:00 or so, and checked in. When we got there it was quite empty, most rooms available, and only one other person in labour. Still no real contractions, they came and went. Some around 4 minutes apart, sometimes 7 minutes. They asked if we wanted a private room, or shared, for the labour part (different to the after recovery, where Ushka wanted shared room or it would be too lonely.) We picked private since everything was available. (Shared would also be private for a short time too). It was the same room that we had with Lokien, so it all came screaming back then. |
|
| |
|
| == Acknowledgements ==
| | They checked the dialation, it was at 4cm. (It was 2cm when we had the Doctor's appointment on the earlier Tuesday.) Ushka got hooked up to the monitoring contraption. The baby's heartbeat was excellent, and contractions still not so regular. We did a lot of walking around the corridors, as you do. Time went forward slowly, eventually we started connecting to the Internet to check mails, and chat. Called Emily to check if there were any problems picking up Lokien, but it sounded like they had a good time. Emily even sent some pictures of Lokien and Lily which was nice to see. |
|
| |
|
| Special thanks for Design suggestions, Implementational ideas and
| | We asked for another dilation check at about 22:00 and unfortunately it was still at 4cm. We tried to sleep a little, but when they saw I would rather be on the floor than to sleep in a chair, they recommended the shared room as I could get a little bit of tatami to sleep on myself. So that was nice. We slept from about 00:00 to about 03:00 or so, but other woman arrived. We could hear the door bell, then they came in. Sleeping to the sound of women having contractions, and "itaiii", followed by baby screams sure is...different.. |
| Debug sessions go to:
| |
|
| |
|
| Brendan Knox <friar@drunkmonk.net>
| | So, somewhere between 03:00 and 04:00 the contractions started to get more regular, generally about every 2 minutes or so. They still weren't too bad according to Ushka, and the other expecting women who were first-timers sounded like they had a rougher time. Since the labour room had about 4 women, including us, plus another in the private room, they were leaving things until the last minute. They only have two delivery rooms after <span class="plainlinks">[http://www.thepiggybackrider.com/ <span style="color:black;font-weight:normal; text-decoration:none!important; background:none!important; text-decoration:none;/*CITATION*/">child carrier</span>]</span> all, so they want to minimize the time spent in there. Ushka got hooked up to the monitor again so we could see the progress, and I liked being able to see the contraction coming before she knew, so that I could get ready with back rubbing etc. Eventually, they were on top of each other, and fully dilated so Ushka was wheeled into the delivery room. We got room 2 again, which is where we had Lokien. At least there is symmetry. |
|
| |
|
| The lovely and cute icon for LION library was done by: | | This was around 07:40 or so, and straight on the bed. We had two nurses, the "higher ranking" nurse was the same one that delivered Lokien. When the monitor for pain/contraction hit 99 for the first time (clocked it!) Ushka did a nice power-chuck, but she totally missed me as I could see it coming (alas, no containers in reach!) The nurse had her sit on a chair (crouching position) which made it go a little too fast, so back to the bed she went. This time when the nurses asked her not to push (just do the hup-hup thing) Ushka managed not too, which probably helped a great deal. Eventually I saw the head (with hair) and an ear. Face down, soon a shoulder, and then the rest came out. The tray that the baby is landing in, was not really big enough. Amusing, Japanese size fits all.. |
|
| |
|
| Adam Fothergill <speedghost@btinternet.com>
| | Kajerik was quite purple, but colour changed quite rapidly. Another 4 nurses came in to see the big baby, and they weighed him three times to be sure. Kajerik was breathing better, so they actually handed him to Ushka directly, when I guess we were mentally prepared for him to be wheeled off to the incubator like last time. But not so, baby and mother went into the temporary recovery room while Ushka's room was being prepared. After a while, they took the baby off for checks and further measurements. The time was 07:35 when ha was born. |
|
| |
|
| ... do check out Airburst and his other work on www.strangeflavour.com | | Over all, very different. Just more relaxed, not much freaking-out at all. Knew what was coming even though it is just as painful and uncomfortable. But at least we knew there was an end to it. This time I was certainly much more aware just how much waiting around there is. We could easily have watched 3 movies! (But then you'd not be "bonding"! Indeed). I felt a little sorry for the first-timers, but there isn't much one can do except reassurances. I still do not "get" why Japanese women chose to go through that alone! That is just insane. |
|
| |
|
| (The icon is NOT part of the CVS distribution at the present time, you
| | Now, where do I get a vasectomy? |
| may see the icon on the homepage for LION.)
| |
| | |
| Additions thanks to:
| |
| | |
| * The #Amiga! chaps without whom we would not have enough pessimism in the world!
| |
| | |
| NOTE regarding the documentation. I've tried my best to keep this up
| |
| to date, there is nothing more frustrating that having documentation
| |
| not match the sources. However, the most frequent mistake I have made
| |
| is in the change of the type of a "handle". You may find it referred
| |
| to as "void *", "connection_t *" or "lion_t *". It should be "lion_t *" but I may have missed a few.
| |
| | |
| | |
| | |
| == Introduction ==
| |
| | |
| What is it, just how does the lion library work, and how do I use it?
| |
| | |
| === What is it? ===
| |
| | |
| It's Lund's Input Output library, run best in non-blocking mode, to simply
| |
| development of your Networked Application. So far it compiles on any
| |
| Operating System I've had a chance to try it on. (NetBSD, FreeBSD,
| |
| OpenBSD, BSDI, Linux, IRIX, Windows, MacOsX, Solaris). It includes
| |
| nice and simple API to do any Networking, File IO and Pipe (fork,
| |
| system() and piping helper programs)
| |
| | |
| === How do I link against it? ===
| |
| | |
| Compile the library, using the method most appropriate to your
| |
| system. You should then have a liblion.a and lion.h file. You want to
| |
| use the lion.h include in your code, and link against the library. Some OS may require extra libraries to be linked. For example libsocket, libnsl on Solaris.
| |
| | |
| == API ==
| |
| | |
| === LiON flags ===
| |
| | |
| Available lion_flags are:
| |
| | |
| LION_FLAG_NONE - Do nothing special
| |
| LION_FLAG_FULFILL - Always return a valid node, signal failure
| |
| with events.
| |
| LION_FLAG_EXCLUSIVE - Open file in exclusive mode.
| |
| | |
| | |
| === Initialisation ===
| |
| | |
| You need to initialise it, and release it once your program is
| |
| exiting, this is accomplished by calling:
| |
| | |
| int lion_init(void);
| |
| void lion_free(void);
| |
| | |
| Return codes:
| |
| 0 means success.
| |
| | |
| | |
| lion_free will iterate all connections and close them nicely for you,
| |
| releasing all memory. Incase your program have finished all IO, but is
| |
| not read to exit, all work is done.
| |
| | |
| The main outline of the code assume you call the lion_poll function
| |
| frequently, the more the better. It does all the connection state
| |
| logic, input parsing to strings and buffering with flow control. This
| |
| function can either be used to block, that is, used as the main call
| |
| to yield CPU until there is traffic on any socket. This is a common
| |
| method for servers, where nothing is done until there is network
| |
| traffic.
| |
| | |
| You can also call lion_poll in a polling sense, without blocking, which
| |
| returns immediately. This can be used in client situations, where the
| |
| CPU yield is elsewhere, often tied to the screen refresh.
| |
| | |
| int lion_poll( int utimeout, int stimeout );
| |
| | |
| utimeout and stimeout represent the micro-seconds and seconds that
| |
| poll should block with. If both of these are 0, it is a poll method.
| |
| | |
| Return codes:
| |
| -1 means error (don't call lion_poll again - quit)
| |
| 0 timeout occurred
| |
| 1 traffic/state change (normal return code).
| |
|
| |
| | |
| === How do I distinguish connections from each other? ===
| |
| | |
| You have two methods. each function takes that of a network handle,
| |
| which from your point of view is just a "void *" - although it will
| |
| give you type checking warnings if you use "lion_t *". A unique one is
| |
| returned for each new connection, communication input, and passed
| |
| along to all the output functions.
| |
| | |
| You can optionally specify a "user_data" void * pointer, to your own
| |
| structure or data. This is to help you gain faster access to your own
| |
| internal data. (Rather than having to search through it matching
| |
| against the network handle). However, if you do not care for this
| |
| feature, simply pass NULL.
| |
| | |
| === How do I know what is going on, and where do I receive input? ===
| |
| | |
| All state changes and input is relayed to the caller, by the use of
| |
| one function lion_userdata, which should be defined by the user of the
| |
| library (presumably you!). It is called for all state changes, and
| |
| input on any socket. Note, you can have ALL your lion events passed to
| |
| this ONE function. Or, you can set a different handler function per
| |
| lion type with the "lion_set_handler()" API method. This can aid in
| |
| making readable and clean code. See the advanced note at the end of
| |
| this section.
| |
| | |
| | |
| Remember this function should be defined in _your_ code.
| |
| | |
| int lion_userinput( lion_t *handle, void *user_data,
| |
| int status, int size, char *line);
| |
| | |
| The first two arguments are explained in the previous
| |
| paragraph. "Status" passed here is one of "enum lion_status" which is
| |
| defined as:
| |
| | |
| LION_INPUT - New input received on socket, one line.
| |
| LION_BINARY - New input received on socket, binary chunk.
| |
|
| |
| LION_BUFFER_EMPTY - Output buffer is now empty
| |
| LION_BUFFER_USED - Required buffering on output
| |
|
| |
| LION_CONNECTION_LOST - Connection was lost/broken
| |
| LION_CONNECTION_CLOSED - Connection was closed, by you or peer.
| |
| LION_CONNECTION_CONNECTED - A pending connection was established.
| |
| LION_CONNECTION_NEW - A new (incoming) connection on a
| |
| listen socket.
| |
|
| |
| LION_CONNECTION_SECURE_ENABLED - Request for SSL was successful.
| |
| LION_CONNECTION_SECURE_FAILED - failed to upgrade to SSL on socket.
| |
|
| |
| LION_FILE_OPEN - File opened successfully.
| |
| LION_FILE_CLOSED - File reached EOF and has been closed.
| |
| LION_FILE_FAILED - Failed to open.
| |
|
| |
| LION_PIPE_FAILED - fork, or child failed to start.
| |
| LION_PIPE_RUNNING - child successfully started to run.
| |
| LION_PIPE_EXIT - child has finished executing.
| |
| | |
| "line" and "size" passed here are encoded as follows:
| |
| | |
| LION_INPUT:
| |
| LION_BINARY:
| |
| "size" has the number of bytes in input buffer "line". In
| |
| binary mode this represents a chunk of data. You are
| |
| guaranteed size being larger than 0.
| |
| In text mode "line" points to a single line of input, guaranteed
| |
| null-terminated, and without CR/NL and at least 1 byte in size.
| |
| | |
| LION_CONNECTION_CLOSED:
| |
| LION_FILE_CLOSED:
| |
| LION_PIPE_EXIT:
| |
| "Size" is 0 for sockets and files. (successful/normal close)
| |
| In the case of pipes, "Size" will have the return code of the
| |
| child if it is known, or -1 if it is not. If the return code
| |
| is required, the application can request to receive a second
| |
| LION_PIPE_EXIT event for when it is known. If the child never exits,
| |
| this second events will never come, but the application can
| |
| chose to kill() the child, since the pipe is known to be closed.
| |
| | |
| LION_CONNECTION_LOST:
| |
| LION_FILE_FAILED:
| |
| LION_PIPE_FAILED:
| |
| "Size" has the error code, if know, of the failure, from errno.
| |
| "Line" has the error message, typically from sys_errlist.
| |
| | |
| LION_BUFFER_EMPTY:
| |
| LION_BUFFER_USED:
| |
| Buffering events. Discussed further in the flow control
| |
| section, but generally when you receive a buffer used event
| |
| you should pause your reader by calling
| |
| lion_disable_read. Then enable read again by calling
| |
| lion_enable_read once you receive buffer empty event.
| |
| | |
| LION_CONNECTION_SECURE_ENABLED:
| |
| LION_CONNECTION_SECURE_FAILED:
| |
| If you have requested a secure SSL/TLS connection these events
| |
| inform you whether or not the upgrade to secure connection
| |
| succeeded or failed. It is up to the application as to what
| |
| action should be taken. If in-secure connections is not
| |
| allowed, calling lion_close upon receiving the secure failed
| |
| event is sufficient. Or if no action is taken, communication
| |
| continues as usual, but insecurely.
| |
| | |
| | |
| * NOTE
| |
| | |
| Please be aware the any reference _what so ever_ to the handles after
| |
| either status LION_CONNECTION_LOST, LION_CONNECTION_CLOSED,
| |
| LION_FILE_CLOSED, LION_PIPE_EXIT [*], LION_FILE_FAILED or LION_PIPE_FAILED
| |
| is a _serious error_ and will most likely cause core dumps. It is best
| |
| to NULL any local reference to the handle should you store those.
| |
| | |
| [*] The exception here is that if the return code is not known, you
| |
| can ask for an additional event (one only) to be signalled when it is
| |
| known, if the child eventually does exit. Use lion_want_returncode()
| |
| to request the actual return code. Note there is an issue with
| |
| returning -1 and sensing the -1 that means we do not have the
| |
| returncode yet. But the second event, when asked for, will have the
| |
| final true returncode.
| |
| | |
| * ADVANCED NOTE
| |
| | |
| If you find it is getting messy having all events coming through the
| |
| one function, you can actually set a different event handler for any
| |
| lion_t *handler in your sources. See:
| |
| | |
| lion_t *lion_set_handler( lion_t *, lion_handler_t * );
| |
| | |
| which returned the previous handler. There is also a
| |
| | |
| lion_handler_t *lion_get_handler( lion_t *);
| |
| | |
| If you chose to use lion_set_handler (and you probably will) you
| |
| should pass LION_FLAG_FULFILL with the methods so that it will not
| |
| deliver events before you have had a chance to set the handler to be
| |
| used. The FULFILL flag will essentially delay the EVENTS from being
| |
| posted until next lion_poll() call is issued.
| |
| | |
| === How do I reply/send data? ===
| |
| | |
| There are three functions available to transmit data on a handle. You
| |
| can either use the low-level buffer send, which works just like the
| |
| libc write() call. Or you can use the supplied socket printf function
| |
| which lets you print formatted strings.
| |
| | |
| int lion_printf(lion_t *handle, char const *fmt, ...);
| |
| int lion_send(lion_t *handle, char *buffer, unsigned int len);
| |
| int lion_output(lion_t *handle, char *buffer, unsigned int len);
| |
| | |
| All return number of actual bytes sent.
| |
| | |
| It is preferred that you use lion_send() as it has the full logic with
| |
| compression. If compression is not desired using lion_output() is
| |
| sufficient. SSL is dealt with inside lion_output().
| |
| | |
| == Networking functions ==
| |
| | |
| === How do I actually make a new connection? ===
| |
| | |
| Using the lion_connect call you generate a new network handle, placing
| |
| its socket in the pending state, allowing you to receive
| |
| LION_CONNECTION_CONNECTED status and, of course, actual data input.
| |
| | |
| lion_t *lion_connect( char *host, int port,
| |
| unsigned long iface, int iport,
| |
| int lion_flags, void *user_data );
| |
| | |
| host is a string, either "host.domain.com" or in Internet dot-notation
| |
| "192.168.0.1". Port is a number between 1 and 65535 inclusive.
| |
| | |
| Return codes:
| |
| void * - handle to new socket.
| |
| NULL - Connection failed.
| |
| | |
| '''Bold text'''
| |
| THIS FUNCTION NEEDS UPDATING. IT IS THE ONLY FUNCTION THAT TAKES A
| |
| "CHAR *" FOR HOSTNAME. IT SHOULD BE CHANGED, AND lion_addr() SHOULD BE USED.
| |
| | |
| In case of failure, lion_userdata is called with the actual failure
| |
| code as described previously.
| |
| | |
| iface and iport are optional, if you wish to bind to a specific interface for
| |
| outgoing packets. Generally you specify NULL to let the system do
| |
| automatic routing. Similarly, iport will bind to a specific source
| |
| port. For example, if you are serving FTP data, you may wish to appear
| |
| to connect FROM port 20.
| |
| | |
| lion_flags are extra features settable. See the discussion regarding
| |
| the FLAGS elsewhere in the is document.
| |
| | |
| Please be aware that hostname lookups are _not_ nonblocking, so if
| |
| this is not desirable, pass it dot-notation syntax only. This is
| |
| considered as a potential future extention to lion library. If you
| |
| really want nonblocking name lookup, you can consider using the pipe
| |
| syntax to retrieve the IP.
| |
| | |
| === What about a listening, incoming socket? ===
| |
| | |
| They aren't much harder would you believe, but they are a two-stage
| |
| process. That mean you first call lion_listen to configure your
| |
| listening socket. This then makes status LION_CONNECTION_NEW to be
| |
| signaled whenever there is a new connection. Your sources then should
| |
| call lion_accept which will return _a new_ network handle. The network
| |
| handle for the listen handle is still active (for any more
| |
| connections, but you can close this if new connections are not wanted)
| |
| and the new network handle representing the new connection.
| |
| | |
| lion_t *lion_listen( int *port, unsigned long iface,
| |
| int lion_flags, void *user_data );
| |
| lion_t *lion_accept( lion_t *node, int close_old,
| |
| int lion_flags, void *user_data,
| |
| unsigned long *remhost, int *remport );
| |
| | |
| 'port' here is the actual listen port you which to open, if you don't
| |
| care, and just want the system to open any available port, pass it
| |
| 0. It will be filled in with the actual port opened.
| |
| | |
| 'interface' is an optional IP representing the Network Interface to
| |
| use. This is generally required on Multi-homed hosts, but in most
| |
| cases just pass 0.
| |
| | |
| lion_accept takes the network handle of the listen socket.
| |
| | |
| Return codes:
| |
| void * - handle to new socket
| |
| NULL - failure.
| |
| | |
| In case of failure, lion_userdata is called with the actual failure
| |
| code as described previously.
| |
| | |
| "remhost" and "remport" are optional if you wish the remote hosts IP
| |
| and port filled in. Alternatively you can pass NULL for both here, and
| |
| use lion_getpeername() as well.
| |
| | |
| * Windows developers. Due to poor implementatin of SO_REUSEADDR you can bind the same wildcard port multiple times in Windows, which can be frustrating if you have a main listening socket, and rely on bind() to fail to determine if a daemon is already running. In this situation, lion_accept() will take the LION_FLAG_EXCLUSIVE type. It has no effect on Unix so it is safe to define it.
| |
| | |
| | |
| | |
| Additionally, the Network Layer can do some compression for you, but
| |
| since it is essentially still is an ascii-line protocol, the
| |
| compressed data needs to be base64 encoded. So you don't get maximum
| |
| compression out of it. You enable outgoing compression by calling:
| |
| | |
| void lion_compress_level( int level );
| |
| | |
| Where 'level' is the number of bytes from which we start considering
| |
| compression. '0' disabled it. Say you set it to '256', only then if a
| |
| packet is larger than that do we attempt to compress. The packet may
| |
| still not be rejected should its compressed size, plus that over
| |
| base64 overhead, exceed that of the original size. Example compression
| |
| levels look like:
| |
| | |
| Output is 1035 bytes.
| |
| Compressed size: 236 bytes.
| |
| Base64 size: 322
| |
| | |
| Which can still be considered a significant improvement.
| |
| | |
| lion uses simple ZIP deflate compression, so don't expect anything fabulous.
| |
| | |
| Miscellaneous functions to aid in your application:
| |
| | |
| void lion_close( lion_t *handle );
| |
| | |
| Close a handle, making sure to flush any outstanding data. Once data
| |
| is flushed, the lion library automatically calls lion_disconnect() and
| |
| issues the user with the appropriate event. You do not need to call
| |
| either functions if you receive one of the closed, lost or failed
| |
| events.
| |
| | |
| void lion_disconnect( lion_t *handle );
| |
| | |
| If you just want the handle closed without regard for any out-standing
| |
| data yet to be written you can call this instead of lion_close().
| |
| | |
| enum lion_type lion_gettype( void *handle );
| |
| | |
| Returns the type of this handle. Currently the types are
| |
| | |
| LION_TYPE_NONE - Should never happen, and probably signifies an
| |
| internal error
| |
| LION_TYPE_SOCKET- Handle is a network socket.
| |
| LION_TYPE_FILE - Handle is a regular file on disk.
| |
| LION_TYPE_PIPE - Handle is a pipe from lion_fork() or lion_system().
| |
| | |
| lion_t *lion_adopt(int fd, enum lion_type type, void *user_data);
| |
| | |
| Take a file descriptor already opened (by application or otherwise)
| |
| and make it part of the lion library engine. Generally not required as
| |
| it is by far better to use lion API to open new entities, but useful
| |
| for situations where you want to perhaps process input from stdin. So
| |
| calling lion_adopt with "fileno(stdin)" can be useful. Windows users
| |
| should be aware that the code to deal with Window's STDIN handles is
| |
| currently lacking.
| |
| | |
| void lion_set_userdata( lion_t *handle, void *user_data );
| |
| void *lion_set_userdata( lion_t *handle );
| |
| | |
| Allows you to set a handles user_data at a later stage, if it is not
| |
| know by the time you call lion_connect/lion_accpet/lion_open etc.
| |
| | |
| int lion_isconnected(lion_t *handle);
| |
| | |
| Returns non-zero/TRUE is the handle is currently in connection
| |
| state. It does not make any sense to use this on non LION_TYPE_SOCKET.
| |
| | |
| unsigned long lion_getsockname(void *handle);
| |
| | |
| Return the sockname of a connection. Usually required if you are to
| |
| send the IP of a socket, like that in FTP protocol. It can also be
| |
| used with the "interface" on a listening socket.
| |
| | |
| int lion_fileno(void *handle);
| |
| | |
| In some situations you may need to get at the actualy file-descriptor
| |
| used with a socket. This really is strongly discouraged but I have
| |
| left it in here. It is however expected to be used with file IO. For
| |
| example if you want to call lseek(), lstat() and other file IO
| |
| function. However, be aware of what function you use this
| |
| with. Calling close() using lion_fileno() will confuse lion nicely,
| |
| and most likely result in unexpected things.
| |
| | |
| void lion_find( int (*)compare(lion_t *handle, void *arg1, void *arg2),
| |
| void *arg1, void *arg2);
| |
| | |
| Iterate all nodes available. Calling a user-defined "compare" function
| |
| for each one. The compare function should return non-zero to keep
| |
| iterating (and lion_find() will return NULL). Or return zero to stop
| |
| iterating (and lion_find() returns the current node). Can be used both
| |
| for listing all nodes, or to look for a particular node.
| |
| | |
| You can use functions like lion_get_type(), lion_get_handler() and
| |
| lion_get_userdata() to uniquely identify a node you are interested in.
| |
| | |
| int extra_iterate(lion_t *vnode, void *arg1, void *arg2)
| |
| {
| |
| printf("Called with node %p\n", vnode);
| |
| return 1; // 0 stops iteration (node found), 1 or anything else
| |
| // keeps going
| |
| }
| |
| | |
| main()
| |
| {
| |
| lion_find(extra_iterate, NULL, NULL);
| |
| }
| |
| | |
| | |
| === Buffer sizes ===
| |
| | |
| If you wish to change the default buffer size in LiON (default is currently 1400 bytes) you can call:
| |
| | |
| | |
| void lion_buffersize(int);
| |
|
| |
| Return codes:
| |
| None.
| |
| | |
| This should be called immediately after '''lion_init()''' to be truly global, however the function can be called at anytime and will only affect handles created after the call. This function has purposely left without sanity checking to give the calling API maximum control. Call it with '''-1''' if you so wish.
| |
| | |
| It is also possible to set buffersize for an individual handle, but this only affects the behavior in '''BINARY''' mode. Specifically the desired buffer size for the next receive operation.
| |
|
| |
| void lion_set_buffersize(lion_t *, int);
| |
|
| |
| Set desired buffer size of following LION_BINARY received data. '''Size''' of '''0''', or '''size''' larger than the default size
| |
| (as set by '''lion_buffersize()''') resets the buffer size to the default size.
| |
|
| |
| Return codes:
| |
| None.
| |
| | |
| | |
| int lion_get_buffersize(lion_t *);
| |
|
| |
| Return codes:
| |
| Size of buffer, or the default buffer size if not set.
| |
| | |
| === Example sources? ===
| |
| | |
| There should be a few examples in the sample/ directory, but, roughly
| |
| something like this should work:
| |
| | |
| -------------------------------cut here-----------------------------------
| |
| /* This isn't the best of examples, as we do not even bother remember the
| |
| lion_t node returned by connect and so on. So it assumes you only
| |
| really have one connection, and therefor only get events from one lion
| |
| connection. But it serves as an example. */
| |
|
| |
| #include <stdio.h>
| |
| #include "lion.h"
| |
|
| |
|
| |
| static int do_exit = 0;
| |
|
| |
| int main(int argc, char **argv)
| |
| {
| |
| lion_init();
| |
| lion_compress_level( 0 ); // No compression thanks
| |
|
| |
| lion_connect("localhost", 21, NULL, 0, LION_FLAG_NONE, NULL);
| |
|
| |
| while( !do_exit) {
| |
| lion_poll(0, 10);
| |
| }
| |
| lion_free();
| |
| }
| |
|
| |
| int lion_userinput( lion_t *handle,
| |
| void *user_data, int status, int size, char *line)
| |
| {
| |
| switch( status ) {
| |
|
| |
| case LION_CONNECTION_LOST:
| |
| printf("Connection was lost.\n");
| |
| break;
| |
|
| |
| case LION_CONNECTION_CLOSED:
| |
| printf("Connection was gracefully closed.\n");
| |
| break;
| |
|
| |
| case LION_CONNECTION_CONNECTED:
| |
| printf("Connection successfully established.\n");
| |
| break;
| |
|
| |
| case LION_INPUT:
| |
| printf("Connection has input: '%s'\n", line);
| |
| break;
| |
|
| |
| default:
| |
| break;
| |
| }
| |
| }
| |
| -------------------------------cut here-----------------------------------
| |
| | |
| === What about binary, or chunk-by-chunk data transfer? ===
| |
| | |
| There is an API call to set a handle into binary mode:
| |
| | |
| void lion_enable_binary(lion_t *handle);
| |
| void lion_disable_binary(lion_t *handle);
| |
| | |
| There is an older, deprecated, function that _toggles_ the state of
| |
| binary mode. It is suggested that the developer avoids this function.
| |
| | |
| void lion_setbinary(lion_t *handle);
| |
| | |
| which usually would follow after lion_connect(), lion_accept(),
| |
| lion_open(), lion_fork() calls. There is no race condition as the
| |
| socket would not be put into read-fd until next iteration.
| |
| | |
| You can also switch to binary at any time, and it works like a toggle
| |
| so call it again to return back to text/line-by-line mode. When in
| |
| binary mode note that size will have the amount of bytes in the
| |
| buffer starting from "line".
| |
| | |
| case LION_INPUT: // TEXT input
| |
| (code)
| |
| case LION_BINARY: // BINARY input
| |
| (code)
| |
| | |
| | |
| == SSL/TLS options ==
| |
| | |
| If the network library was compiled with SSL/TLS support the following
| |
| additional functions can be called. The compiler switch "WITH_SSL" is
| |
| required during compile time, as well the ssl and crypto libraries.
| |
| | |
| The initialisation functions should be called and set before
| |
| lion_init() is called.
| |
| | |
| void lion_ssl_ciphers( char *);
| |
| void lion_ssl_rsafile( char *);
| |
| void lion_ssl_egdfile( char *);
| |
| | |
| These functions are to set the list of ciphers, the RSA certificate
| |
| .pem file and the optional EGD socket path should the OS not have
| |
| /dev/urandom.
| |
| | |
| The default values of these functions should they not have been called
| |
| are:
| |
| | |
| cipher: "RC4-SHA:RC4-MD5:DHE-DSS-RC4-SHA:DES-CBC3-SHA:DES-CBC3-MD5:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA"
| |
| rsafile: "lion.pem"
| |
| egdfile: "/var/run/egd-pool"
| |
| | |
| If you only want client-side SSL support, and therefore do not require
| |
| the use of a RSA certificate, simply let it fail to find the .pem
| |
| file. The network library with disable server-side SSL automatically.
| |
| | |
| To switch a socket into SSL/TLS use the following call:
| |
| | |
| int lion_ssl_set( lion_t *, ssl_type_t );
| |
| | |
| The type is either LION_SSL_SERVER, or LION_SSL_CLIENT, to signify which
| |
| end of the SSL/TLS protocol you are attempting to emulate. If
| |
| TLS_SSL_SERVER is used, the RSA .pem file is required during
| |
| lion_init().
| |
| | |
| This function can be called on an already connected socket, as well as
| |
| a newly created socket. Once the outcome of the secure switch is
| |
| known, the network library issues an appropriate event via the API:
| |
| | |
| LION_CONNECTION_SECURE_ENABLED
| |
| LION_CONNECTION_SECURE_FAILED
| |
| | |
| The network library user is not required to perform any tasks with
| |
| these events, but if a secure socket is _required_ it is recommended
| |
| that the user calls "lion_disconnect()" if the
| |
| "LION_CONNECTION_SECURE_FAILED" event is received. The network layer
| |
| will then close the socket and post the appropriate event.
| |
| | |
| The type should either be LION_SSL_CLIENT, or LION_SSL_SERVER
| |
| depending on which end of the connection you wish to be. Generally, if
| |
| you are issuing a lion_connect() call, you should be LION_SSL_CLIENT.
| |
| | |
| If the events are not desired the user can optionally call:
| |
| | |
| int lion_ssl_enabled( lion_t * );
| |
| | |
| To determine the SSL/TLS status on a socket, from say within the
| |
| connected event and decide if "lion_disconnect()" should be called.
| |
| | |
| To generate your own self-signed certificate you can use:
| |
| | |
| openssl req -new -x509 -days 365 -nodes -out lion.pem -keyout lion.pem
| |
| | |
| === SSL and events, and auto sensing ===
| |
| | |
| One concern worth mentioning is the order in which we receive events
| |
| with regards to connections and SSL enabled.
| |
| | |
| Simplest case.
| |
| | |
| * Application has already received the CONNECTED event on a socket, and has decided to attempt to upgrade it to SSL now. An example of this would be with the FTP protocol, after a "AUTH SSL" has been exchanged.
| |
| | |
| === lion_connect() and LION_SSL_CLIENT example: ===
| |
|
| |
| (CONNECTED event has occurred at some point, and possibly IO.)
| |
|
| |
| [1] application calls lion_set_ssl();
| |
|
| |
| [2] as we are already connected, lion attempt to start SSL.
| |
|
| |
| [3] application receives either SECURITY_ENABLED, or SECURITY_FAILED
| |
| depending on the outcome.
| |
| | |
| | |
| | |
| | |
| === lion_accept() and LION_SSL_SERVER example: ===
| |
| | |
| (CONNECTED event has occurred at some point, and possibly IO.)
| |
|
| |
| [1] application calls lion_set_ssl();
| |
|
| |
| [2] as we are already connected, lion attempt to start SSL.
| |
|
| |
| [3] application receives either SECURITY_ENABLED, or SECURITY_FAILED
| |
| depending on the outcome.
| |
| | |
| Now for the slightly more complicated situations. The idea here is we
| |
| ask for SSL before we are even connected. You are then guaranteed to
| |
| receive either of the two SECURITY_ENABLED or SECURITY_FAILED events
| |
| _before_ you receive the CONNECTED.
| |
| | |
| The idea is then that your application can start to communicate its
| |
| own protocol when it receives an CONNECTED event. (By sending the
| |
| greeting or whatever is preferred). Your application can then chose to
| |
| only accept SSL connections: If you receive SECURITY_FAILED simply
| |
| call lion_disconnect(). You will then NOT receive the CONNECTED
| |
| event. Or do nothing in that event, and your CONNECTED event handler
| |
| can deal with both plain text, and SSL sockets.
| |
| | |
| === lion_connect() and LION_SSL_CLIENT example: ===
| |
| | |
| [1] application calls lion_connect()
| |
| [2] application calls lion_set_ssl() immediately after.
| |
|
| |
| [3] if the connection failed, lion posts CONNECTION_LOST
| |
| [3a] stop
| |
|
| |
| [4] if connection is successful, lion enters SSL authentication.
| |
|
| |
| [5] if SSL authentication fails, lion posts SECURITY_FAILED
| |
| [5a] if lion_close or lion_disconnect was called, lion posts CONNECTION_CLOSED
| |
| [5b] or if we are still connected, lion posts CONNECTION_CONNECTED.
| |
| [5c] stop
| |
|
| |
| [6] if SSL authentication succeeds, lion posts SECURITY_ENABLED
| |
| [6a] if lion_close or lion_disconnect was called, lion posts CONNECTION_CLOSED
| |
| [6b] or if we are still connected, lion posts CONNECTION_CONNECTED.
| |
| [6c] stop
| |
| | |
| === lion_accept() and LION_SSL_SERVER example: ===
| |
| | |
| [1] application receives the CONNECTION_NEW event.
| |
| [2] application calls lion_accept()
| |
| [3] application calls lion_set_ssl() immediately after.
| |
|
| |
| [4] if the connection failed, lion posts CONNECTION_LOST
| |
| [4a] stop
| |
|
| |
| [5] if connection is successful ....
| |
|
| |
| [6] if connection has input, lion peeks to determine is it looks like
| |
| SSL
| |
|
| |
| [7] if appears not to be SSL, lion posts CONNECTION_CONNECTED
| |
| [7a] stop
| |
|
| |
| [8] if SSL authentication fails, lion posts SECURITY_FAILED
| |
| [8a] if lion_close or lion_disconnect was called, lion posts CONNECTION_CLOSED
| |
| [8b] or if we are still connected, lion posts CONNECTION_CONNECTED.
| |
| [8c] stop
| |
|
| |
| [9] if SSL authentication succeeds, lion posts SECURITY_ENABLED
| |
| [9a] if lion_close or lion_disconnect was called, lion posts CONNECTION_CLOSED
| |
| [9b] or if we are still connected, lion posts CONNECTION_CONNECTED.
| |
| [9c] stop
| |
| | |
| | |
| === Encrypted files ? ===
| |
| | |
| As of version 1.63 (io.c) you can now ask files to be encrypted on
| |
| disk. Currently it only uses blowfish cipher, but there is no reason
| |
| why we could not support all ciphers in libcrypto. (stream ciphers).
| |
| | |
| After you open your file ( lion_open() ) issue:
| |
| | |
| void lion_ssl_set( handle, LION_SSL_FILE );
| |
| | |
| You should also call
| |
| | |
| void lion_ssl_setkey( handle, char *key, int size )
| |
| | |
| To set the key for the file. The rest is transparent. Both text input
| |
| and binary mode will work as per usual. Be aware that if you get the
| |
| key wrong, and are in text mode, you will receive interesting
| |
| garbage. (But lion should never crash from it). Both these calls (in
| |
| any order) should be done right after you open the file, so that no
| |
| data is delivered.
| |
| | |
| * WARNING:
| |
| | |
| If you are to use encrypted files please be aware that if you use
| |
| lion_fileno() and lseek() it will NOT work. (Except if you lseek to
| |
| the very start of the file, then call lion_ssl_setkey() again - this
| |
| clears the ivec and num again.
| |
| | |
| | |
| | |
| | |
| == Buffering? Flow-control ? ==
| |
| | |
| While this is more complicated, it is also done for you. As lion has no
| |
| way to know that two handles are supposedly connection, and there is
| |
| no real way for it to know that either, it is currently handled
| |
| differently.
| |
| | |
| Basically, how it works is that the output calls (printf, output and
| |
| send) will always succeed. They do this by buffering if the write()
| |
| would block, and if the buffer is full, it doubles the buffer space.
| |
| | |
| However, you could fairly quickly this could be dangerous. For example
| |
| if you have a handle that is reading at a high rate (like 1M/s) but
| |
| your sending socket is only going at a much slower rate. (say, 10k/s).
| |
| | |
| Within 10 seconds you would have a 10MB buffer!
| |
| | |
| So, the idea is, when a output command required the use of the output
| |
| buffer (internal to lion) it will send the application the
| |
| LION_BUFFER_USED even.
| |
| | |
| The application should then call
| |
| | |
| void lion_disable_read( lion_t *handle );
| |
| | |
| on the _reading_ handle. This will stop input processing on the
| |
| incoming handle, so that your outgoing socket can catch up.
| |
| | |
| Once the output buffer has been emptied out, lion will issue the
| |
| application the LION_BUFFER_EMPTY event, and it should call
| |
| | |
| void lion_enable_read( lion_t *handle );
| |
| | |
| On back you go. This slows down the reading to match that of the
| |
| writing speed. Buffer will probably never grow, unless the data is
| |
| expanded.
| |
| | |
| === Diagrams of flow control: ===
| |
| | |
| Method two, text mode.
| |
| | |
| read handle | application | write socket
| |
| ----------------------------------------------------------------
| |
| | |
| |
| line "hello" --> | |
| |
| | --> "hello" |
| |
| | parsing, action, send |
| |
| | data "world" --> |
| |
| | | --> "world"
| |
| | | no or partial write
| |
| | | set write trigger to
| |
| | | empty buffer when
| |
| | | possible, return
| |
| | *[1] | <-- OK, but buffered.
| |
| | OK+buff <-- |
| |
| | Signal read socket to |
| |
| | sleep, request buffer |
| |
| | empty event. |
| |
| | <-- sleep |
| |
| sleep <-- | |
| |
| Set socket to sleep | |
| |
| | want buffer-empty |
| |
| | event --> |
| |
| | | --> arm event
| |
| | |
| |
| | | Buffer emptied,
| |
| | | send event.
| |
| | | <-- buffer empty
| |
| | buffer empty <-- |
| |
| | Send wake-up |
| |
| | <-- resume |
| |
| resume <-- | |
| |
| set socket to read | |
| |
|
| |
| *[1] - Note that this write is essentially successful, but buffered because of network lag. The pplication can continue to send data, which will continue to be buffered. The buffer should grow dynamically as well, transparently. Since the buffer grows you don't _technically_ need to issue the sleep, but with TCP timeouts being large, you could end up with a monstrous buffer if the read is frivolous.
| |
| | |
| Whether or not we send "OK, but buffered" immediately we need to
| |
| buffer, or only after some water-mark has been reached is
| |
| implementation specific. The OS will also buffer it, so if we need to
| |
| buffer, the OS has probably some 64k buffered already. Water mark may
| |
| not be required.
| |
| | |
| | |
| == File IO Functions ==
| |
| | |
| === What about if I want to read, or write files? ===
| |
| | |
| The best way is to use the lion API for file IO, which is:
| |
| | |
| lion_t *lion_open( char *file, int flags, mode_t modes,
| |
| int lion_flags, void *user_data );
| |
| | |
| Which works fairly similar to that of normal open(). File is the
| |
| filename, flags the usual (O_RDONLY, O_CREAT etc) flags, note it is
| |
| better if you do not use O_EXCLUSIVE as this does not exist under
| |
| WIN32. Instead, use "lion_flags" to indicate you want exclusive access
| |
| to the file.
| |
| | |
| You can call lion_disconnect(), lion_close(), lion_printf(),
| |
| lion_send() and lion_output() as per other methods.
| |
| | |
| * NOTE: If you are opening a file for writing, you should call
| |
| lion_disable_read() afterwards. Otherwise the lion library will try to
| |
| read from it, receive EOF, and close it. Perhaps lion should do this
| |
| automatically since it knows you asked for a file in write-only mode?
| |
| | |
| * NOTE:
| |
| Windows' users should be aware that opening files in read-write mode
| |
| is currently not implemented.
| |
| | |
| * NOTE: If you do not use LION_FLAG_FULFILL, the handle returned by
| |
| lion_open() (if not NULL) is valid to be used immediately. All other
| |
| lion methods, you need to wait for the appropriate event signalling
| |
| readiness. (_CONNECTED, _PIPE_RUNNING, _FILE_OPEN). This should be
| |
| changed to automatic buffering. See TODO section.
| |
| | |
| | |
| == Pipe IO Functions ==
| |
| | |
| | |
| === How to call another program and deal with its I/O ? ===
| |
| | |
| You can fork off a child process and have a pipe to communicate with
| |
| it, as well as, executing another program and communicate with it. If
| |
| you wanted to, rather than using file IO to read file "/tmp/roger" you
| |
| can pipe off "cat /tmp/roger" instead. Although this isn't as
| |
| efficient of course, it is merely mentioned as an example.
| |
| | |
| To simply fork() a new process, and have CPU return to both child and parent:
| |
| | |
| lion_t *lion_fork( int (*start_address)(lion_t *,void *),
| |
| int flags, void *user_data, void *arg);
| |
| | |
| This differs slightly from traditional Unix fork() but this is to be
| |
| compatible with the Win32 version. There are some side issues that
| |
| needs to be made aware here.
| |
| | |
| "start_address" is a function pointer, that takes a lion_t *handle
| |
| which is the pipe back to the parent process, plus your user_data
| |
| pointer, if set and an optional "arg" pointer. (I am unsure if I actually
| |
| implemented the "arg" passing?)
| |
| | |
| Generally, I suspect most users of this API probably will not call
| |
| lion_fork() directly, but one of lion_system() or lion_execve().
| |
| | |
| * NOTE - It is best if you call lion_fork() for any forking work you need to do. However, an exception to that is if you want to daemonise, you can just call normal fork() and do the usual dup2() and etsid() business before you call lion_init().
| |
| | |
| Please be aware that you can't just call "_exit()" as a child, as that
| |
| would take the parent process with you under Windows. If you need to
| |
| explicitly exit the child, and returning from your "start_address"
| |
| function is not feasible, you can call:
| |
| | |
| void lion_exitchild( int return_code );
| |
| | |
| Please be aware the under Win32, the lion_fork() function does not, in
| |
| fact, create a new process but merely a new thread. Lion could have
| |
| opted to go for a full fork() implementation like that of cygwin, but
| |
| decided it was undesirable. A new thread is quite fast, but certain
| |
| steps needs to be taken to ensure it works.
| |
| | |
| If you are defining variables globally (or statically to a module) you
| |
| need to ensure you get a new fresh copy of this variable after you
| |
| call lion_fork (only under Win32, but if you want to be portable, you
| |
| should use this in Unix too) you can declare your variables as:
| |
| | |
| THREAD_SAFE lion_t *linked_list_head = NULL; or
| |
| THREAD_SAFE unsigned int length = 0;
| |
| | |
| So, this has the nice side effect that you can call lion_fork(), and
| |
| in your child, (which has been released under Unix, and is thread_safe
| |
| under Windows) you can now call lion_poll() as per usual!
| |
| | |
| The only node already in the lion library, under effect from
| |
| lion_poll() is the pipe node back to parent, which you can use as per
| |
| normal to send and receive information between processes.
| |
| | |
| * NOTE: You will most likely set a new event handlers (using lion_set_handle) in your child, or suffer the consequences. (It will work, but under Win32 it is messy)
| |
| | |
| | |
| However, if you fork just to run another program, use....
| |
| | |
| lion_t *lion_execve( char *base, char **argv, char **env,
| |
| int with_stderr, int flags, void *user_data );
| |
| | |
| Start a child process to communicate with. Works very similar to that
| |
| of execve() (which is the function it eventually calls or CreateProcess under Windows.)
| |
| | |
| You need to prepare the argv[] list before you call this. You use
| |
| "with_stderr" to determine if you want "stderr" to be redirected as
| |
| input to the parent, or have it redirected to /dev/null. "env" should
| |
| be a list of strings as explained in "man execve". Generally
| |
| "HOME=/usr/home/lundman" and similar. You can pass NULL if you do not care about environment variables.
| |
| | |
| lion_t *lion_system( char *cmd, int with_stderr, int flags, void *user_data );
| |
| | |
| Wrapper to lion_execv() which takes a string of the command you wish
| |
| to execute, break it up into argv[] based on spaces, and is "" aware.
| |
| | |
| contrib/libdirlist is a good example of the use of lion_fork(). It
| |
| spawns N new children, that the main lion application can communicate
| |
| with using the pipe between the two.
| |
| | |
| There are a few samples in the samples/ directory that show how to use
| |
| lion_execve() and lion_system().
| |
| | |
| ///Windows developers be aware.///
| |
| | |
| When you use lion_execv(), or lion_system() to spawn a new process that you wish to communicate with (via stdin/stdout) there are two methods in doing so. The default is that lion will create HANDLEs and two threads to translate between the "parent pipe" and the new process's stdin/stdout. This will work great for most command-line applications.
| |
| | |
| However, if you are spawning an application that uses lion, and you would like to use lion_adopt() to bring in stdin to be processed like other lion handles, that it would not work. (As stdin/stdout are HANDLES, and select() only works on SOCKETS in Window). You can pass lion_flag:
| |
| | |
| LION_FLAG_STDIO_AS_LION
| |
| | |
| to lion_execv() and lion_system telling lion to not wrap HANDLES around stdin/stdout, but leave it
| |
| as a SOCKET.
| |
| | |
| It is unlikely anyone will need this feature.
| |
| | |
| == Capping / rate limit ==
| |
| | |
| Any lion handle can be rate limited. This means you can set the
| |
| maximum speed of input, and output. Please be aware that it is more
| |
| efficient to limit a reader as opposed to a writer, but not always
| |
| possible. The writer rate limit is purely advisory and up to you to
| |
| follow it.
| |
| | |
| Please note there are two types to this. The global "total cps rate"
| |
| limit set for "everything" in lion, and the "groups" set limit which
| |
| is explained later.
| |
| | |
| Use:
| |
| void lion_rate_in ( lion_t *handle, int cps);
| |
| void lion_rate_out( lion_t *handle, int cps);
| |
| | |
| By setting the input rate limit, your input handler will only get
| |
| events when the rate of input is less or equal to that of your "cps"
| |
| setting. Be aware that this is only as accurate as the frequency of
| |
| which you call lion_poll(). Ie, if you want 1k/s, then you need to
| |
| call lion_poll() which a sleep of 1 second, or less. If you need only
| |
| 8k/s, you can call lion_poll with a sleep of 8 seconds etc. (Certainly
| |
| if you only have one connection going).
| |
| | |
| The output rate limit works by sending "fake" BUFFER_USED events when
| |
| the output rate as exceeded you specified rate. You should then stop
| |
| sending until BUFFER_EMPTY is received, and if you do this for
| |
| buffering anyway, it is automatic. If you chose to ignore the
| |
| buffering events, the output rate limit is not enforced.
| |
| | |
| | |
| | |
| === GROUPS rate control ===
| |
| | |
| In conjuction with the total capping control lion has, we also provide
| |
| a means to group connections together and set a cap on them. This
| |
| gives you the ability to put a rate limit on a single connection
| |
| (group of one) or a group of connections (say, any connection done as
| |
| a certain user). Lion has no knowledge of such application specific
| |
| information as to what a "user" is, it is up to the application to set
| |
| the grouping requirements.
| |
| | |
| Group work by asking for an available group from lion, which is simply
| |
| an "integer". It is better if the application attempts not to
| |
| interpret this value, or assume that it is always contiguous. It could
| |
| be changed to a pointer in future.
| |
| | |
| You ask for a new group with:
| |
| | |
| int lion_group_new( void );
| |
| | |
| It is up to you to remember this value. Similarly you release it when
| |
| you are done with it:
| |
| | |
| void lion_group_free( int group );
| |
| | |
| To set the specific limits of this group is set by:
| |
| | |
| void lion_group_rate_in( int group, int limit);
| |
| void lion_group_rate_out( int group, int limit );
| |
| | |
| Where "limit" is in K per second. Setting it to "8" would limit the
| |
| connection(s) to a maximum of 8192 bytes / second.
| |
| | |
| To assign, or remove, a lion_t into/from a group that you have created:
| |
| | |
| void lion_group_add( lion_t *handle, int group);
| |
| void lion_group_remove( lion_t *handle, int group);
| |
| | |
| Alas, currently a lion_t handle can only belong to ONE group at a
| |
| time. This is a limitation that we can fix in future. (Technically,
| |
| you can belong to two groups, the global rate limit - group 0 - and
| |
| any other group set here).
| |
| | |
| * Please be aware that "rate limit out" is purely advisory, ie it sends the buffering events LION_BUFFER_USED/EMPTY as needed to maintain rate limit, but if your application opts to ignore these then the rate limit is pointless.
| |
| | |
| * Please also be aware of the frequency of lion_poll() execution as discussed in the global rate limit.
| |
| | |
| Finally, rate_out is by far the most inefficient. A better solution,
| |
| if it is at all possible in your application would be to rate the
| |
| reader. If for example you are reading from a file to send on a
| |
| socket, it is MUCH more efficient to use
| |
| lion_group/global_rate_in() on the FILE HANDLE, as opposed to setting
| |
| a limit on rate_out on the network handle.
| |
| | |
| == UDP support. ==
| |
| | |
| UDP in lion works very similar to that of normal TCP, or any other
| |
| type really. However, one can think of two levels of UDP support, the
| |
| rudimentary support, and the slightly more advanced version.
| |
| | |
| The first way to use UDP is the most basic, open a datagram socket to
| |
| send from, and receive from. You will receive all packets that come
| |
| in, and it is up to you to deal with it as appropriate.
| |
| | |
| To create yourself a new UDP socket, use:
| |
| | |
| lion_t *lion_udp_new( int *port, unsigned long iface,
| |
| int lion_flags, void *user_data );
| |
| | |
| Where "port" is optional local port to use, or pass NULL (or set port
| |
| to 0 before calling) to open the first available port in your OS's
| |
| anonymous range. Incoming data come with the usual LION_INPUT and
| |
| LION_BINARY events for the handler set. (Or default handler).
| |
| | |
| When you receive input, the host and port to reply is already set (and
| |
| you can additionally inspect it using lion_getpeername() ). So you can
| |
| also just call lion_printf(), lion_send() or lion_output() right here.
| |
| | |
| However, if you are wishing to send to a specific socket, on a new
| |
| udp socket, or having not just received information you need to use a
| |
| two step process.
| |
| | |
| First assign the host/port of the destination using:
| |
| | |
| lion_t *lion_udp_bind( lion_t *handle, unsigned long host,
| |
| int port, NULL );
| |
| | |
| and then you can use the normal lion output functions (lion_printf,
| |
| etc).
| |
| | |
| NOTE: The user_data field is NULL! This means it should merely set a
| |
| new host/ip for an existing node, and not create a new instance as
| |
| discussed in the advanced section.
| |
| | |
| === Advanced UDP ===
| |
| | |
| In many situations, like that of games perhaps, you want to assign a
| |
| specific handle to a unique instance, and perhaps more importantly the
| |
| user_data, so that you receive events with only that specific node.
| |
| | |
| Perhaps a good way to explain it is to think in terms of a game. You
| |
| open a listening UDP socket that will receive input from
| |
| everywhere. Once your protocol has progressed far enough to create a
| |
| new player in your game, and assigns it a unique node for that player
| |
| you can ask for a (new) bound lion_t type for that particular player's
| |
| host/ip. To which you can use the user_data to point to this new
| |
| players data.
| |
| | |
| Any future data received from that player's host and ip will come in
| |
| as the NEW lion_t handle, which that players user_data. Additionally,
| |
| you do not need to call lion_udp_bind() to communicate with THAT
| |
| player, you can just call lion_printf() on THAT users NEW handle.
| |
| | |
| Even thought technically it still uses just the one UDP socket, lion
| |
| will automatically pass your the correct handle and user_data based on
| |
| the remote IP/host pair. Any non-registered input (input that isn't
| |
| bound to any specific lion_t) will come to the initial/default handle.
| |
| | |
| Please note that you can close the initial handle, and just use the
| |
| newly bound handle. This means you will no longer receive data that is
| |
| NOT from any registered host/ip. If that is so desired.
| |
| | |
| Additionally, you can create a new UDP socket, then bind it
| |
| specifically to a known IP/port directly. You will only receive
| |
| information from this host. (No unknown/anonymous packets is passed to
| |
| you).
| |
| | |
| To create a new handle/instance for a udp socket, use:
| |
| | |
| lion_t *lion_udp_bind( lion_t *handle, unsigned long host,
| |
| int port, void *user_data );
| |
| | |
| lion_t *lion_udp_bind_handle( lion_t *handle,
| |
| lion_t *handle,void *user_data );
| |
| | |
| With user_data being NOT NULL, we create a new instance of lion_udp
| |
| and return a new lion_t handle. The latter function takes the host/ip
| |
| from an existing connection, often used with the LION_INPUT event to
| |
| assigned a new handle from one that we just received information.
| |
| | |
| Once all instances of a UDP socket has been closed, the actual socket
| |
| is also released.
| |
| | |
| You should probably avoid large packets, or packets that would require
| |
| fragmentation. The limit usually lies somewhere around 1500 bytes.
| |
| | |
| Buffering may not always make sense with UDP. You can chose to ignore
| |
| the buffering events, or try to deal with them. But by nature udp is
| |
| not guaranteed.
| |
| | |
| | |
| Please see the samples directory for further information.
| |
| | |
| | |
| == TIMERS ==
| |
| | |
| Lion has a simple set of timer functions. They generally are accurate
| |
| to 0.01 seconds, but they are not real-time timers. Lion will make a
| |
| good effort to honour timers on the appropriate moment, but do not
| |
| expect them to be sufficient for real time applications like NTP etc.
| |
| | |
| To arm a timer, you can use:
| |
| | |
| timers_t *timers_new(unsigned long major, unsigned long minor,
| |
| int flags, lion_timer_t callback, void *userdata);
| |
| | |
| Where the arguments major and minor change slightly dependent on the
| |
| flags used. If the timer is in relative time, then major and minor
| |
| specifies "seconds" and "micro seconds (usec)" from now. If set to 4
| |
| and 50000 respectively, the timer will trigger in 4.5 seconds from
| |
| being created.
| |
| | |
| If flags specify TIMERS_FLAG_ABSOLUTE, then major and minor become
| |
| hours and minutes of the day. If set to 15 and 42, the timer will
| |
| trigger at 15:42 in the afternoon.
| |
| | |
| The valid flags are:
| |
| | |
| TIMERS_FLAG_RELATIVE : Default type
| |
| TIMERS_FLAG_ONESHOT : Default type
| |
| TIMERS_FLAG_REPEAT : REPEAT this timer indefinitely. Instead of ONESHOT
| |
| TIMERS_FLAG_ABSOLUTE : Set absolute time of day, instead of RELATIVE.
| |
| | |
| Callback is the function to be called when the timer expires. Defined
| |
| as:
| |
| | |
| lion_timer_t callback(timers_t *timer, void *userdata);
| |
| | |
| "userdata" is a normal "void *" value that just passed along for the
| |
| API user's benefit.
| |
| | |
| | |
| * Please be aware that, unless TIMERS_FLAG_REPEAT has been specified,
| |
| the timers_t structure is released immediately after the callback
| |
| function has been executed. Do not refer to this structure after.
| |
| | |
| * In addition to this, if TIMERS_FLAG_REPEAT is specified, it is
| |
| perfectly valid for the callback function to modify the values of the
| |
| timers_t structure. For example, the function could increase the
| |
| values of major and minor to adjust when the next repeated timer
| |
| expires. Or change the callback function to call for the next trigger.
| |
| | |
| Return values are a pointer to a timer_t, which is a structure holding
| |
| the timer information. The function will return NULL if the timer
| |
| could not be created.
| |
| | |
| At the time of this documentation, the timers_t structure is defined
| |
| as:
| |
| | |
| struct timers_struct {
| |
| struct timeval when;
| |
| unsigned long major;
| |
| unsigned long minor;
| |
| int flags;
| |
| lion_timer_t callback;
| |
| void *userdata;
| |
| };
| |
| | |
| The value "when" is computed automatically when the timer is created,
| |
| so changing its value will have no effect.
| |
| | |
| Please note, if you would rather allocate your own timer, using the
| |
| "timers_newnode()" function, you can call timers_add(timers_t *node)
| |
| instead of timers_new(). But it has no immediate advantage in doing
| |
| so.
| |
| | |
| | |
| | |
| Timers can be cancelled before the trigger, or at any time if they are
| |
| REPEATING timers by calling:
| |
| | |
| int timers_cancel(timer_t *timer);
| |
| | |
| As with calling timers_freenode(), the timer structure is released and
| |
| should not be referenced after calling timers_cancel().
| |
| | |
| * Currently repeating timers, although generally accurate to 0.01
| |
| seconds between each trigger, will accumulate errors over time. This
| |
| can be fixed if so that it is repeated based on epoch time. Please
| |
| contact me if this is the case. You could also use ABSOLUTE time, and
| |
| increase the major/minor values in your callback to simulate a
| |
| work-around to this problem. (Alas, with less precision).
| |
| | |
| | |
| == Misc Functions ==
| |
| | |
| int lion_fileno( lion_t *);
| |
| | |
| Get the actual file-descriptor used in a lion handle. It is probably better not to use this function unless absolutely needed.
| |
| | |
| void lion_get_bytes( lion_t *node, bytes_t *in, bytes_t *out );
| |
| | |
| Get the number of bytes received and sent on a lion handle.
| |
| | |
| time_t lion_get_duration( lion_t *node );
| |
| | |
| Get the duration (number of seconds) that this connection was/has-been active.
| |
| | |
| void lion_get_cps( lion_t *node, float *in, float *out );
| |
| | |
| Get the CPS (Kilobytes per second) of this handle.
| |
| | |
| unsigned long lion_addr( char * );
| |
| | |
| Convert an IP address to unsigned long. This needs fixing for IPv6
| |
| | |
| void lion_killprocess( lion_t * );
| |
| | |
| Terminate child process started by lion_fork(), lion_system() etc.
| |
| | |
| == DEBUGGING ==
| |
| | |
| Lion has some debug support built into it. I frequently found it
| |
| frustrating to attempt to follow one lion_t handle when I had many
| |
| handles going. So I added support to _trace_ a particular lion_t
| |
| handle (or as many as you want).
| |
| | |
| You can turn on tracing either by calling the direct functions:
| |
| | |
| void lion_enable_trace(lion_t *);
| |
| void lion_disable_trace(lion_t *);
| |
| | |
| Or another alternative is to use the flag LION_FLAG_TRACE with any of
| |
| the lion_t creation functions. For example: lion_connect, lion_listen,
| |
| lion_accept, lion_fork, lion_execve, lion_open and lion_udp_new. (Any
| |
| function that accepts the lion_flag type).
| |
| | |
| By default the output of tracing is to "stderr". However, you can
| |
| specify and alternative log file by using the function:
| |
| | |
| void lion_trace_file(char *filename);
| |
| | |
| Presumably you should call this before you enable your first lion_t
| |
| handle to be traced.
| |
| | |
| | |
| | |
| == Caveat ==
| |
| | |
| Finally, the biggest and most common mistake done when using this
| |
| network library, including when I use it myself :) is that I forget to
| |
| pass the actual handle rather than whatever node I use internally. If
| |
| I have defined a "net_node" structure to hold the information
| |
| required, as well as the "handle" used when talking to the network
| |
| library, I often find myself calling the lion_* function with just
| |
| "net_node" as opposed to "net_node->handle".
| |
| | |
| | |
| | |
| == TO ADD, TO DO, SUGGESTIONS, MISSING BITS, BUGS ==
| |
| | |
| * lion_tie/marry/untie/divorce API calls
| |
| * Windows files open for read and write. (just read, or just write is done)
| |
| * Windows pipe children currently does not connect <stdin> on child to the pipe to parent process. This most likely needs a thread just to relay data between the two.
| |
| | |
| tie() details:
| |
| | |
| Semantics. If the reader socket is closed (finished) lion will issue
| |
| _CLOSED even as per normal, and the other end is _also_ closed
| |
| automatically! This takes care of the most common situation where the
| |
| application want to do a 1:1 transfer.
| |
| | |
| However, you can optionally call lion_untie() on the handles in the
| |
| _CLOSED event, this tells lion you wish to keep the writer open. lion
| |
| will then re-issue the _CONNECTED event to tell the application that
| |
| the socket is once again ready and available. You could then open a
| |
| new handle, and tie again.
| |
| | |
| For example, to send a whole score of items down one writer pipe, you
| |
| could have
| |
| | |
| case LION_CONNECTION_CONNECTED:
| |
|
| |
| for item in (big list of items)
| |
| handle = lion_connect on $item
| |
| lion_tie(handle, writer);
| |
|
| |
| break;
| |
|
| |
| case LION_CONNECTION_CLOSED:
| |
| lion_untie(handle);
| |
| break;
| |
|
| |
| | |
| === Other TODO: ===
| |
| | |
| * contrib/libresolve
| |
| | |
| If you call lion_connect() (other calls to?) and then issue
| |
| lion_printf/output before the socket is connected, the data is just
| |
| lost. There is no reason why lion could not just buffer this data
| |
| (buffering logic already exists) and assuming successful connection
| |
| send it at that time.
| |