Notification is a novel evolution of our enhanced Server-Side Queue (system queue) support. Using the facilities of c-tree system queues, client applications can direct the FairCom Server to monitor a data file and place notification messages on a queue when changes are made to the file.
This chapter discusses both synchronous and asynchronous processing:
- Synchronous File Notification - The file notification feature is a memory-based queue that is synchronously executed.
- Asynchronous Record Update Notifications - Record Update Notification callback functions allow applications to externally process records as they are modified (added, updated, and deleted) and are asynchronously processed and persisted.

Synchronous File Notification
File notification gives your c-tree application the ability to respond to a variety of events. With the proper handling of these events, you can enable such robust features as syncing data between servers in a replicated fashion, or triggering security and auditing actions based upon a specified file event.
Each notification message includes the following details:
- The type of operation (add, delete, or update)
- For a transaction-controlled file, the transaction number in which the change occurred
- The record offset of the modified record.
The following optional information can also be included in notification messages:
- The unique key value involved in the operation. (An update may return both old and new key values.)
- The record image involved in the operation. (An update may return both old and new record images.)
- The node name of the client that performed the operation.
Asynchronous Record Update Notifications
Many c-tree applications store documents and other non-trivial data types. c-tree allows nearly any type of data to be stored, including text, XML, JSON, PDFs, word processing documents, and email. Frequently, these types of data require alternate indexing algorithms compared to how standard data types are indexed with basic b-tree algorithms. What is needed is a method to hand off these types of record updates to alternative handling.
In V11 and later, a set of Record Update Notification callback functions allows applications to externally process records as they are added or updated within a c-tree database.
These callback functions are called when records in the file are modified (added, updated, and deleted) and are asynchronously processed and persisted. Contrast this with the FairCom DB notification feature which is a memory-based queue. While standard notification can trigger callback handling, this is done in a synchronous manner which can impact up-front application performance. The record update notification callback also handles change events, however, its intent is to allow user-defined actions to occur when a record in a particular table is modified, in a deferred asynchronous manner. With this deferred handling, update events are deferred for background processing while maintaining application responsiveness.
Implementing Record Update Notifications
Function prototypes to handle record updates are located in the following c-tree source module:
ctree\source\ctrucbdll.c
To create a functional shared library, build the module using the FairCom DB build utility mtmake and choose the FairCom DB CTUSER model option. ctuser.dll (or libctuser.so) is generated and this module is then copied into your server's binary folder location.
The callback feature defines three functions:
- File open callback function called when a connection opens a file,
- File close callback called when a connection closes or deletes a file, and
- Record update callback called when an ISAM-level record add, update, or delete operation is performed on a file.
The ctRecordUpdateCallbackControl() function, described below, is used to add and delete callback function definitions. A file can have more than one callback function definition. Each callback function definition is identified by its name, which is a case-sensitive ASCII string.
Each callback definition consists of the following attributes:
- Callback physical .DLL name
- Callback logical identifier name
- Callback function names for file open, file close, and record update
- Parameter string (optional)
- Callback time: this is the time when the record update callback is called. Supported options are:
- called during a record update
- called when a transaction commits (available only for a transaction-controlled file; for a non-transaction-controlled file, this option causes the callback to be called during a record update)
- written to transaction log or a memory queue and processed by a background thread after a transaction commits or the operation completes
- written to transaction log or a memory queue and no further action taken: the application is responsible for processing any queued entries
Restrictions
- FairCom DB allows a callback function definition to be added when the data file is open in shared mode. In standalone mode, adding a callback function requires the data file to be open in exclusive mode.
- A callback function definition can only be deleted when the data file is open in exclusive mode.
Management API Function
See also:
- Update Callback Specifications
- Option to specify external library name in platform-independent format
Update Callback Specifications
File Open and File Close Callbacks
The file open and close callback functions are intended to allow an application to manage resources that will be used by the record update callback function. Typically an application will allocate resources in the file open function and will free the resources in the file close function. For example, initializing an alternative indexing environment on open, and freeing that environment context at close.
The file open callback function is called when the following events occur:
- When a connection opens a file that has a callback definition, the callback function is called at the end of the file open operation. If it is an ISAM level file open call such as
OPNIFIL()orOPNRFIL(), the callback is called after the data file and its associated indexes have been successfully opened. - When a connection adds a callback definition to the file, the callback function is called after the callback definition resource in the data file has been successfully updated.
A callback that is added to a data file that is open in shared mode becomes visible to other connections that already have the file open on their next record add, update, or delete operation. In that situation, the other connection finds that the new callback exists and it calls the file open callback function for the newly-added callback before calling the record update callback function.
The file close callback function is called when the following events occur:
- When a connection closes or deletes the file, the callback function is called right before the data file is closed, so the connection still has the data file and its associated index files open.
- When a connection deletes a callback definition from the file, the callback function is called after the callback definition resource in the data file has been successfully updated.
The file open and close functions have the following prototypes:
NINT rucbOpenFileCallback(pRUCBF prucbf);
NINT rucbCloseFileCallback(pRUCBF prucbf);The record update callback function parameter structure, RUCBF, has the following definition:
/* record update callback parameters */
typedef struct rucbf {
pTEXT datnam; /* Data file name. */
pTEXT params; /* Optional callback parameter string. */
NINT calltm; /* Current context for this call. */
FILNO datno; /* User file number of data file. */
pVOID psession; /* User-defined connection-level pointer. */
pVOID ptable; /* User-defined table-level pointer. */
} RUCBF, *pRUCBF;
The two state pointers, psession and ptable, are available for use by the user-defined callback function code.
The psession state pointer is shared by all callback functions for all files in a given connection. It is appropriate for storing connection-wide state information. One way to use this pointer is to allocate memory and set psession to point to that memory on the first call to the file open callback function in a connection. The application can also maintain a connection-wide reference count, and when the file close function finds that the reference count is zero, it can free the memory.
The ptable state pointer is specific to a particular callback function for a particular file. It is appropriate for storing table-level state information.
Record Update Callback
When a user adds, updates, or deletes a record at the ISAM level, the callback is called at one of the following events:
- If the callback event is set to
RUCBonrecupd, or if the callback time is set toRUCBontrancmtand the file is not under transaction control, the callback is called right after the record and its keys have been added, updated, or deleted. - If the callback event is set to
RUCBontrancmtand the file is under transaction control, the callback is called when the transaction that modified the record is committing. - if the callback event is set to
RUCBonqueuethrd, the callback is called after the transaction commits, when the deferred index thread has read the entry for that record modification operation from the transaction logs. - if the callback event is set to
RUCBonqueueapp, the callback is never called. It is up to the application to read the entry for that record modification operation from the transaction logs and take the appropriate action.
The record update callback function has the following prototype:
NINT rucbRecordUpdateCallback(pRUCBF prucbf,pDFRKY pdfrky);The RUCBF structure is the same as mentioned above and the additional DFRKY structure contains the information about the record add, update, or delete operation and has the following definition:
typedef struct dfrky {
COUNT opcode; /* deferred index operation code */
TEXT status1; /* status bit field #1 */
TEXT status2; /* status bit field #2 */
ULONG dfrkctr; /* deferred index create counter */
LONG fid[3]; /* unique file ID of data file */
LONG oreclen; /* size of old record image (rewrite) */
LONG reclen; /* size of record image */
LONG datnamlen; /* length of data file name plus null */
LONG8 orecbyt; /* old record offset (rewrite) */
LONG8 recbyt; /* record offset */
TEXT varinf[1]; /* variable length information:
** For ctDFR_ADDKEY and ctDFR_DELKEY:
** null-terminated data file name
** old record image (rewrite)
** record image
** For ctDFR_RWTKEY and ctDFR_RWTPKEY:
** null-terminated data file name
** old record image
** record image
** For ctDFR_LOADKEY:
** null-terminated data file name
** null-terminated index file name
*/
} DFRKY, ctMEM **ppDFRKY;
Your record update callback implementation handles one of following four update operations provided in the opcode from the DFRKY structure:
-
ctDFR_ADDKEY- A record was added -
ctDFR_RWTKEY- A record was updated -
ctDFR_RWTPKEY- A record was partially updated -
ctDFR_DELKEY- A record was deleted
You'll find this framework available in the ctrucbdll.c module.
First and Last Operations
An application that uses the record update callback function feature needs to know what operations are associated with a particular transaction and what are the first and last operations for that transaction. In V11.5 and later FairCom Server provides this information to the record update callback function:
- The first operation in a transaction has the
ctDFR_TRANFRSbit set in the status1 field of thepdfrkyparameter that is passed to the record update callback function.
The last operation in a transaction has the ctDFR_TRANLST bit set in the status1 field of the pdfrky parameter that is passed to the record update callback function. So, if a transaction has only one DFRKEY entry, that entry will have both the ctDFR_TRANFRS bit and the ctDFR_TRANLST bit set. If a transaction has more than one DFRKEY entry, the first entry will have the ctDFR_TRANFRS bit set and the last entry will have the ctDFR_TRANLST bit set.
- An optional parameter of type
pRUCBSTTis passed to the record update callback function. This structure has the following definition:
typedef struct rucbstt_t {
LONG verson; /* structure version */
LONG avail; /* padding- available for use */
LONG8 tranno; /* transaction number for the operation */
} RUCBSTT, *pRUCBSTT;Note For FairCom Server to pass this third parameter to your record update callback function, your record update callback DLL or shared library must export the function
rucbCheckVersionCallback(), which has the following function prototype:
NINT rucbCheckVersionCallback(pRUCBACB prucbacb,pNINT pversion);prucbacb is the record update callback definition in the format of the record update callback add operation structure, RUCBACB. The function can choose to examine the record update callback definition to decide which version of the RUCB API it supports. For example, it can choose based on the callback name (prucbacb->cbname) or the name of the record update callback function (prucbacb->fncnames[2]).
The function should set pversion to the version of the record update callback API your DLL uses and return zero to indicate success. Supported versions are:
- a) Version 1 of record update callback API. Your record update callback function must conform to the following prototype:
NINT rucbRecordUpdateCallback (pRUCBF prucbf,pRUCBO prucbo); - b) Version 2 of record update callback API. Your record update callback function must conform to the following prototype:
NINT rucbRecordUpdateCallback (pRUCBF prucbf,pRUCBO prucbo,pRUCBSTT prucbstt);
If your record update callback DLL does not export a function named rucbCheckVersionCallback(), FairCom Server uses version 1 of the record callback API.
Option to specify external library name in platform-independent format
Features such as the record update callback function use an external shared library or DLL that c-tree loads to support user-defined callback functions. The library name is stored in a resource in the file. Because the name was platform-dependent (starting with lib and ending in .so on Unix and ending with .dll on Windows for example), a data file containing a record callback library reference could not be easily used on both Unix and Windows systems.
In V11.5 and later, c-tree supports specifying the DLL or shared library name in a way that causes c-tree to automatically convert the library name to the standard format for the platform on which it is running.
To use this feature, specify ^ as the first character of the library name. For example, specifying the name ^mycallback causes c-tree to convert the name to libmycallback.so on Unix systems (libmycallback.sl on HP/UX and libmycallback.dylib on MacOSX), and to mycallback.dll on Windows systems.
When the library name starts with ^, the conversion is ALWAYS applied. For example on Linux, the library name ^liberty is converted to libliberty.so.
This new feature can also be used in the following situations:
- when using a conditional index external library
- when using a data record filter external library
- when using a deferred index external library
Using File Notification
To use the notification process the user must:
- Open a system queue by calling
ctSysQueueOpen(), which returns a queue handle. - Call
ctNotify()to establish the notification process. - Call
ctSysQueueRead()to read notification messages from the queue. - When notifications are no longer needed, call
ctNotify()to stop notifications orctSysQueueClose()to close the notification queue.
Enabling Notification for Actions on a File
This section discusses the steps required to enable notification for actions on a file.
Open the Files
To enable notification for actions on a c-tree data file, start by opening the data file using a
c-tree file open API function. For example:
COUNT datno;
datno = OpenFileWithResources(-1, "customer.dat", ctSHARED);
Open a Server-Side Queue (system queue)
After opening the data file, open a server-side (system) queue on which notification messages will be placed.
NINT qhandle;
qhandle = ctSysQueueOpen("myNotificationQueue", 0);
Establish Notification
After creating the server-side (system) queue, establish notification for actions on the file using the ctNotify() c-tree API function. The ctNotify() function is used to cause the server to send messages to a system queue when the specified action is taken on the specified c-tree data and index files. The syntax for the ctNotify() function is:
ctCONV NINT ctDECL ctNotify(NINT opcode, NINT objhandle, NINT qhandle,
NINT contents, NINT controls);Parameters
The ctNotify opcode parameter specifies which actions on the resource should be notified. The following values are supported:
| Values | Explanation | Level |
|---|---|---|
ctNT_ADDREC |
Notify that a new record was added to the data file | ISAM |
ctNT_DELREC |
Notify that a record was deleted | ISAM |
ctNT_RWTREC |
Notify that a record was modified | ISAM |
ctNT_CLSOBJ |
Notify that an object was closed | |
ctNT_ISMUPD |
Notify on any change (add, delete or rewrite) to data file. This is identical to ctNT_ADDREC | ctNT_DELREC | ctNT_RWTREC
|
ISAM |
ctNT_TOUCH |
Notify that the file was updated, only once per transaction. This opcode cannot be used in combination with others and the contents parameter must be 0. No details of the update are conveyed, only a “ping” that the file has been touched. | ISAM and low-level |
ctNT_PARTIAL |
Notify that a notification request was started in the middle of a transaction and not all the updates generated a notification. One of the first four opcodes in this table must also be specified when using this option. | ISAM |
ctNT_ISMUPP |
defined as:
|
The ctNotify objhandle parameter is the file number (datno) of an ISAM data file and qhandle is a server-side (system) queue handle returned by a call to ctSysQueueOpen().
The contents parameter determines what optional details are returned in the variable-length region of the notification message and may be set to the following values or any combination (by OR-ing) of them:
| Values | Explanation |
|---|---|
ctNT_CON_UNQKEY |
Unique key |
ctNT_CON_NODNAM |
Node name of actor |
ctNT_CON_RECBUF |
Record buffer on add or update (not on delete) |
To receive different notification contents for different actions, multiple calls to ctNotify() are required. For example, to get unique key values on a record delete notification, and full record images on either an add or rewrite notification, two ctNotify() calls are required: one call with a contents field of ctNT_CON_RECBUF to set up the add and rewrite notification, and one call with a contents field of ctNT_CON_UNQKEY for the delete.
The controls parameter is reserved for future use and must be set to zero.
ctNotify returns NO_ERROR (0) on success.
For example, to monitor ISAM updates to a data file, a call of the form below will cause each ISAM update to the data file specified by datno to generate an entry in the queue specified by qhandle.
ctNotify(ctNT_ISMUPD,datno,qhandle,0,0);See also:
ctNotify, ctSysQueueOpen, ctSysQueueClose, ctSysQueueRead
Receiving Notifications for Actions on a File
After establishing notification on a c-tree data file, use the ctSysQueueRead() function to read notification messages from a system queue. Non-transaction ISAM updates are immediately processed and placed into the system queue by the notify system. Transaction (including pre-image) updates are not processed until they are committed. The following sections discuss the format of a notification message and how to read notification messages returned by ctSysQueueRead().
Notification Queue Message Format
A notification message always starts with a fixed portion followed by an optional variable-length region.
The fixed portion is made by the ctNOTBLK structure defined in ctport.h:
typedef struct notblk {
ULONG action; /* actual opcode */
LONG actor; /* thread ID */
LONG tranhw; /* transaction # HW */
LONG tranlw; /* transaction # LW */
LONG opcode; /* requested opcodes */
LONG objhandle; /* requested (user) handle */
LONG idxmemno; /* index member number */
ULONG contents; /* actual contents bit map */
ULONG controls; /* requested controls bit map */
LONG datahw; /* data HW (eg rec pos) */
LONG datalw; /* data LW */
LONG auxdhw; /* auxilary info HW (eg old pos) */
LONG auxdlw; /* auxilary info LW */
ULONG varlen; /* remaining length (if any) */
} ctNOTBLK, * pctNOTBLK;The contents member determines what is in the variable-length region and should be the same used in the ctNotify() call.
The variable-length portion may contain the following items, depending on the options specified in the contents parameter to ctNotify():
- key values (present if
ctNT_CON_UNQKEYOR-ed in contents), - actor node name (present if
ctNT_CON_NODNAMOR-ed in contents), - full record image (present if
ctNT_CON_RECBUFOR-ed in contents)
These items always appear in this order, even if not all are specified to be returned.
Fixed Portion of Notification Queue Message
The members of the fixed portion of the notification queue message are set as follows:
The action member of the ctNOTBLK structure contains the particular opcode of the monitored event as follows:
| Values | Explanation |
|---|---|
ctNT_ADDREC |
A new record was added to the data file |
ctNT_DELREC |
A record was deleted |
ctNT_RWTREC |
A record was modified |
ctNT_CLSOBJ |
The file was closed (no one has it open) |
ctNT_TOUCH |
The file was updated |
ctNT_PARTIAL |
A notification request was started in the middle of a transaction and not all the updates generated a notification. |
The actor member contains the thread ID of the user/client that performed the operation.
The tranhw and tranlw members contain the transaction number in which the operation occurred: tranhw is the high-word, tranlw the low-word
The opcode member is the opcode originally requested in the ctNotify() call. This may be different from the action member as the action is actual event that occurred, while opcode is the combination of events monitored. For instance if you call ctNotify(ctNT_ISMUPD | ctNT_PARTIAL,...) the action member can be ctNT_DELREC while the opcode is ctNT_ISMUPD | ctNT_PARTIAL.
The objhandle member contains the data file number on which the event occurred or, in case the notification request is for an index, the host index file number on which the event occurred.
The idxmemno member contains the index member number on which the event occurred. The actual index file number is given by objhandle + idxmemno.
The contents member is the actual contents of the variable-length part. This may be different from the contents parameter of the ctNotify() call. For example, when the action performed is ctNT_DELREC no record is returned in the optional full record image part even if it was requested by the ctNotify() call, and the contents value reflects this situation by not having ctNT_CON_RECBUF OR-ed in.
The controls member is reserved for future use.
The datahw and datalw members are the high word and low word of the record offset in the data file after modification (add, rewrite, delete).
The auxdhw and auxdlw members, in case of a record update, contain the original record offset in the data file (high word and low word) whether or not the record has been moved.
The varlen member of the ctNOTBLK structure indicates the length of the variable-length message portion.
Optional Key Values
If the contents member of the ctNOTBLK contains ctNT_CON_UNQKEY, the notification message contains in the variable portion, just after the ctNOTBLK, information about the unique key generated. The information is stored in this way:
FILNO rkeyno;
COUNT keylen;
TEXT key[]; /* buffer of keylen bytes */If action is ctNT_RWTREC there is an additional field
TEXT oldkey[]; /* buffer of keylen bytes */-
rkeynois the index number, starting from 1 relative to the data file (1 is the first index, 2 the second index...). It is set to 0 if there is no unique index. -
keylenis the length of the key. -
keyis a buffer ofkeylenbytes containing the key stored now in the index file. -
oldkeyis a buffer ofkeylenbytes containing the old key stored in the index file before an update, whether or not the key changed.
Optional Actor Node Name
If the contents member of the ctNOTBLK contains ctNT_CON_NODNAM, the 32-byte node name appears next in the notification message (immediately following the ctNOTBLK structure or the key values if key values were requested).
TEXT nodnam[32];-
nodnamis a null terminated string containing the node name of the actor that modified the file causing the notification triggering.
Optional Full Record Image
If the full record image is requested and the operation is not a delete, the record length and record contents appear next in the notification message:
LONG reclen;
TEXT recbuf[]; /* buffer of reclen bytes */-
reclenis the record length -
recbufis a bufferreclenbytes long containing the record that is currently contained in the data file.
Reading Notification Messages
Due to the variable length nature of the notification message, it is necessary to provide the ctSysQueueRead() function with a buffer large enough to contain the entire message; otherwise, the ctSysQueueRead() call will fail with error TQUE_ERR (638) and the message is not read.
When not requesting any optional values (contents set to 0 in ctNotify() call), the notification message length is fixed and the notification message size is sizeof(ctNOTBLK).
When requesting optional information, the message length size can be either retrieved by calling ctSysQueueMlen() or evaluated by adding to the fixed message length the lengths of each option requested as described in the following table.
| ctNotify contents parameter | Message length |
|---|---|
ctNT_CON_UNQKEY |
+ 2* sizeof(COUNT) + If the size in bytes ( Else |
ctNT_CON_NODNAME |
+ 32 |
ctNT_CON_RECBUF |
+ sizeof(LONG) + If the record is a fixed length record: If the record is variable-length and you know that there is a maximum record size: Else there is no way to guarantee that the message is large enough and |
For instance, suppose that contents is ctNT_CON_UNQKEY | ctNT_CON_RECBUF and the key length is not known and the record length is fixed to 40, then the message length will be:
sizeof(ctNOTBLK) + 2 * sizeof(COUNT) + 2 * MAXLEN + sizeof(LONG) + 40If the message length is known or can be estimated, a notification message can be read by passing the address of a buffer, whose size is greater or equal to the message size, the buffer size and the timeout value to ctSysQueueRead(),. For example:
NINT qhandle; /* Set by call to ctSysQueueOpen */
NINT rc;
pTEXT buffer;
buffer = (pTEXT) malloc (msglen);
/* Read next available queue message with 5 second timeout. */
rc = ctSysQueueRead(qhandle,buffer,msglen,5000);If the message length is not known, it is necessary to determine the size with a call to ctSysQueueMlen(). This function takes the queue handle and an optional timeout, so it can be used to wait until the next message is available in the queue and to determine the size of the message. After determining the message size, allocate a sufficiently-sized buffer and pass its address and size to ctSysQueueRead(). For example:
NINT qhandle; /* Set by call to ctSysQueueOpen */
NINT rc;
NINT msglen;
pTEXT pbuffer;
/* Read size of next available queue message with 5 second timeout. */
msglen = ctSysQueueMlen(qhandle,5000);
/* Allocate buffer to hold queue message. */
pbuffer = (pTEXT)malloc(msglen);
/* Read next available queue message. */
rc = ctSysQueueRead(qhandle,pbuffer,msglen,0);The following code demonstrates how to extract information from the message read from a notification queue.
Notification Queue Example
COUNT ProcessQueueMessage (pTEXT buff) /* buff is a pointer to the message */
{
/* cast the buffer to a ctNOTBLK structure */
/* in order to make easier to extract the */
/* information in the fixed portion */
pctNOTBLK pnotblk = (pctNOTBLK)buff;
/* set info to point at the beginning of the */
/* variable-length part */
pTEXT info = buff + sizeof(ctNOTBLK);
ctrt_printf("action actor tranlw opcode objhandle contents
controls datalw varlen\n");
ctrt_printf("%8d %5d %6d %6d %9d %8d %8d %6d %6d\n",
pnotblk->action, pnotblk->actor, pnotblk->tranlw, pnotblk->opcode,
pnotblk->objhandle, pnotblk->contents, pnotblk->controls,
pnotblk->datalw, pnotblk->varlen);
/* if the action is ctNT_CLSOBJ return */
if (pnotblk->action == ctNT_CLSOBJ)
return (1);
/* if contents contains ctNT_CON_UNQKEY there */
/* is unique key information to extract */
if (pnotblk->contents & ctNT_CON_UNQKEY)
{
FILNO rkeyno;
COUNT keylen;
/* extract relative index number */
cpybuf(&rkeyno, info, 2);
/* step over the index number */
info += 2;
/* extract the key length */
cpybuf(&keylen, info, 2);
/* step over the key length */
info += 2;
ctrt_printf("Unique Key:\n");
ctrt_printf("\tIndex number (relative) %d\n",rkeyno);
ctrt_printf("\tkeylen %d\n",keylen);
/* your key value handling here */
/* the key starts at the memory pointed by info */
/* and its length is keylen */
/* skip over the key */
info += keylen;
/* if the action is ctNT_RWTREC there is also */
/* the old key value to retrieve */
if (pnotblk->action == ctNT_RWTREC)
{
/* your key handling code here */
/* skip over the key */
info += keylen;
}
}
else
{
ctrt_printf("Unique Key [NOT REQUESTED]\n");
}
/* if contents contains ctNT_CON_NODNAM there */
/* is a node name information to extract */
if (pnotblk->contents & ctNT_CON_NODNAM)
{
/* print the node name that is pointed */
/* by info */
ctrt_printf("Node Name: %s\n",info);
/* skip over the nodename */
info += 32;
}
else
{
ctrt_printf("Node Name [NOT REQUESTED]\n");
}
/* if contents contains ctNT_CON_RECBUF */
/* there is a record image to extract */
if (pnotblk->contents & ctNT_CON_RECBUF)
{
LONG notrln;
/* extract the record length */
cpybuf(¬rln, info, 4);
/* skip over */
info += 4;
ctrt_printf("Record Buffer Length: %d\n",notrln);
/* your record buffer handling here */
/* the record buffer starts at info */
/* and is notrln bytes long */
info += notrln;
}
else
{
ctrt_printf("Record Buffer [NOT REQUESTED]\n");
}
/* sanity check to verify that we reached */
/* the end of the message */
if ((buff + sizeof(ctNOTBLK) + pnotblk->varlen) != info)
{
ctrt_printf("Message parsing problem\n");
}
return (0);
}
Disabling Notification for Actions on a File
Notification for actions on a file is terminated by a physical file close or an explicit call of the form:
ctNotify(opcode,objhandle,qhandle,contents,controls|ctNT_CTL_STOP);where all the arguments must agree with a previous call to notify except that the controls parameter must include the ctNT_CTL_STOP flag.
Notification Callbacks
Instead of passing the notification information to a queue, use the FairCom Server SDK to make calls to ctCallback() to associate a callback function to a notification.
ctCallback() is used in the same manner as ctNotify() except that the third parameter is a pointer to a callback function instead of a queue handle:
NINT ctCallback(NINT opcode, NINT objhandle, ctCallbackPtr cbptr,
NINT contents, NINT controls);A notification setup with ctCallback() causes the function pointed to by cbptr to be called (instead of a message written to a queue). This capability is only available with the FairCom Server SDK, and ctCallback() can only be called from code compiled into the server itself (using the FairCom Server SDK).
The prototype for the callback function pointer is:
typedef NINT (*ctCallbackPtr)(pVOID msg, NINT msglen, pVOID aux,
NINT auxlen);The callback function returns NO_ERROR (0) on success and a non-zero value on failure. Parameter msg and the optional parameter aux are input parameters. If both msg and aux are passed in, then they should be conceptually pasted together to form one long message.
It is important to note that as currently coded, the target file’s header semaphore is held while the callback function is executed. Therefore the callback function cannot introduce pauses or delays or attempt to lock the header of the target file.