Mserv comes with a library, mservcli, which is useful for those writing programs that wish to communicate with an Mserv server. If you wish to write an application in a different language other than C, it is a good starting point to convert all the code contained within the mservcli library.
You should probably read the documentation on the client mode so that you are familiar with the data this library is manipulating.
This library connects in to Mserv in COMPUTER or RTCOMPUTER protocol mode (see client mode documentation).
For an example of using these functions, see the mservcmd source.
mservcli_data structure
struct mservcli_data { unsigned int maxparams; unsigned int params; char **param; };
This structure forms the basis for returning data from the server. When you pass a pointer to this structure to the library you should have allocated a block of memory 'param' to be pointers to the result parameters, and you should have also have set 'maxparams' to be the extent of this block (the name 'maxparams' is somewhat misleading, the maximum number of items is actually one less than maxparams due to a terminating NULL).
Example:
struct mservcli_data data; char *param[64]; data.param = param; data.maxparams = 64;
modes of connection
As you should know, from reading the documentation on the client mode that Mserv provides, there are two computer-processable modes: COMPUTER and RTCOMPUTER.
These modes are essentially the same, except that the RTCOMPUTER mode features real-time delivery of information. Real-time information is defined as everything that the server sends to the client that was not as a direct result of the immediately preceeding command, e.g. broadcast information.
If you tell this library to connect using the real-time option, then you should provide a real-time handler. The real-time handler allows for this data to be delivered to your application asynchronously with the main server<>client flow of information.
real-time handler
If you supply a real-time handler, the function definition should look like:
void (*rth)(void *, int, struct mservcli_data *)
This means that your function should return 'void' (i.e. not return any data) and take three parameters. The first parameter is entirely up to you and would have been previously supplied by you to the mservcli_rthandler function. This is to allow you to have two connections to two different servers with the same handler, and allows you to supply private information.
The second parameter is the Mserv return code. This is normally a number in the range 600-699 which indicates a broadcast message (see the section about the mserv client mode for detailed information on the various broadcast messages).
Lastly a filled-in mservcli_data structure is returned with all the parameters for the particular return code setup in the param array (params of them, NULL terminated and NULL padded to maxparams). Note that the mservcli_data structure returned here is actually the one you passed to mservcli_rthandler.
struct mservcli_id *mservcli_connect(const struct sockaddr_in *servaddr, char *buffer, unsigned int buflen, const char *user, const char *pass, int rtflag)
Connects to an Mserv server at servaddr. The buffer is used to store the results from the server and is approximately equal to the maximum line length it is possible to receive. If buffer is NULL and buflen is 0 then the client will allocate the buffer on your behalf (which as of writing is 1024 bytes).
user and pass should be pointers to a null-terminated string indicating the username and password to connect to the server as. Note that the server accepts 'guest'/'guest' by default to allow guest access.
rtflag, if set, indicates that you want the client to tell the server it is a real-time client. If you set this flag you will want to call mservcli_rthandler as detailed below.
This function returns a pointer to an mservcli_id on success, NULL if an error occured; errno is set as appropriate. Notable errno codes are EACCES which occures when the server refuses your user/pass combination, ENOMEM when the system is out of memory plus any errnos resulting from socket() or connect().
Example:
struct sockaddr_in sin; struct hostent *hent; struct mservcli_id *id; sin.sin_family = AF_INET; sin.sin_port = htons(port); if ((hent = gethostbyname(host)) == NULL) { fprintf(stderr, "myprog: unknown host\n", host); exit(2); } sin.sin_addr = *(struct in_addr *)hent->h_addr_list[0]; if ((id = mservcli_connect(&sin, NULL, 0, user, pass, 0)) == NULL) { if (errno == EACCES) { fprintf(stderr, "myprog: server said access denied\n"); exit(1); } fprintf(stderr, "myprog: unable to connect to server: %s\n", strerror(errno)); exit(1); }
int mservcli_free(struct mservcli_id *id)
Closes any connection to the server and frees any buffer space that was automatically allocated by the library. Note that this does not quit the server nicely - you should use mservcli_send to send a quit command plus you should then wait for it to be acknowledged using mservcli_getresult.
This function returns 0 on success and -1 if it failed; errno is set as appropriate.
int mservcli_rthandler(struct mservcli_id *id, void (*rth)(void *, int, struct mservcli_data *), void *private, struct mservcli_data *data)
Registers a function as a handler for receiving the asynchronous real-time data from the server. private can be a pointer to anything and will be passed onto your handler in the first parameter. A pointer to an mservcli_data structure is required which will be filled in and passed onto the handler.
This function returns 0 on success and -1 if it failed; errno is set as appropriate.
int mservcli_send(struct mservcli_id *id, const char *output)
Call this function to send data to the server, for example a command. Each line should be terminated with a CRLF pair, e.g. "\r\n" in C.
This function returns 0 on success and -1 if it failed; errno is set as appropriate.
Example:
if (mservcli_send(id, "HELP\r\n")) { fprintf(stderr, "myprog: unable to send command: %s\n", strerror(errno)); exit(1); }
int mservcli_getresult(struct mservcli_id *id)
After you have called mservcli_send you should call this function which will process the next returned line from the server; the response to your command.
After you have called this function you MUST call either mserv_getdata until it stops returning data OR you can call mserv_discarddata if you're not interested in the data (e.g. you only want to know the return code).
This function returns the result code from the server, or -1 if there was an error; errno is set as appropriate.
Example:
if ((code = mservcli_getresult(id)) == -1) { fprintf(stderr, "myprog: unable to get result to command: %s\n", strerror(errno)); exit(1); }
char *mservcli_getresultstr(struct mservcli_id *id)
This returns a textual equivalent of the result code returned from the server. This text comes from the server itself so can be in any language and should NOT be parsed. This function should only be called after mservcli_getresult.
Example:
printf("Server returned code %d which means '%s'\n", code, mservcli_getresultstr(id));
int mservcli_getdata(struct mservcli_id *id, struct mservcli_data *data)
This should only be called after a call to mservcli_getresult, and fills in the passed mservcli_data structure to hold the data from the server.
Returns -1 if there was an error with errno set as appropriate, or 0 if there is still more data to be fetched or 1 if there was no data and there is no more to get.
Example:
unsigned int i; int code; /* connection, mserv_send, mservcli_getresult... */ do { if ((code = mservcli_getdata(id, &data)) == -1) { fprintf(stderr, "myprog: unable to get data: %s\n", strerror(errno)); exit(1); } if (code) break; /* process data, e.g print each item separated with colons */ for (i = 0; i < data.params; i++) { printf("%s%s", (i ? ":" : ""), data.param[i]); } putchar('\n'); } while (1);
int mservcli_discarddata(struct mservcli_id *id)
Instead of calling mservcli_getdata to get the data for each and every line of output from the server, you can call this function which will discard the rest of the data. It is equivalent to calling mservcli_getdata until it returns 1 (there is no more data).
This function returns 0 on success and -1 if it failed; errno is set as appropriate.
int mservcli_poll(struct mservcli_id *id)
Whilst you have no command to send you should call this periodically for real-time data. There is no point calling this if you did not set rtflag in mservcli_connect. If there is any real-time data your rthandler will be called.
This function returns 0 on success and -1 if it failed; errno is set as appropriate.
int mserv_stricmp(const char *str1, const char *str2)
A case insensitive version of strcmp, exactly the same as strcasecmp (if your machine has it, which some don't, hence why this routine exists).
int mserv_strnicmp(const char *str1, const char *str2, int n)
Like mserv_stricmp except that it only compares the first n characters.
const char *mserv_stristr(const char *s, const char *find)
A case insensitive version of mserv_strstr.
char *mserv_strsep(char **str, const char *stuff)
This function is an implementation of the system strsep() call. It is present as many systems do not have one.