This chapter describes the user-level programming interface to the Tru64 UNIX Event Manager (EVM). The principal issues addressed are as follows:
Determining which status changes should be established as events
Designing the contents of events
Using EVM's API functions and utilities to post, subscribe to, and retrieve events
In the context of this chapter, an event is an indication that something has happened that may be of interest to one or more of the following:
A system administrator, application manager, or some other class of user
System monitoring software
The operating system
An application program
Interested parties may be part of the local system or they may be running on a remote system.
The chapter describes the contents of EVM events and the programming interface to the EVM subsystem. Details about the functions supported by the EVM programming interface are provided in online reference pages. (Section 14.7 provides summary descriptions and examples of some of the most commonly used EVM functions.)
The event viewer and the command-line interface are not addressed in this chapter. For information about the viewer, see the online help; for information about command-line operations, see the System Administration manual.
This chapter discusses the following topics:
Event and event management (Section 14.1)
An overview of how EVM events are handled (Section 14.2)
Starting and stopping EVM (Section 14.3)
Authorization to post and receive events (Section 14.4)
Contents of an EVM event (Section 14.5)
Designing a set of events (Section 14.6)
The EVM programming interface (Section 14.7)
Adding an event channel to EVM (Section 14.8)
14.1 Events and Event Management
EVM provides a centralized means of posting, distributing, storing, and reviewing event information -- regardless of the event channel used by individual event posters and without requiring existing posters to change how they interact with their current channels. EVM makes event information more accessible to system administrators than was previously possible with Tru64 UNIX systems, and it provides a flexible infrastructure that can be used as an event distribution channel by:
Internal development groups at Compaq
Independent software vendors
Customer-application developers
Existing event channels
The mechanism used to pass event information is known as an event (or event notification), and the component generating the event is known as the event poster. EVM's event-posting mechanism is a one-way communication channel. It is intended to allow the poster to communicate information to any entity that cares to receive it. It is not necessary for the poster to know which entities, if any, are subscribing to receive an event being posted.
An entity that expresses an interest in receiving event information is known as an event subscriber. Depending on the event, subscribers might include system administrators, other software components, or ordinary users. It is quite possible that some events will never be subscribed to.
Events can be posted and subscribed to by any process, and the same process can be both a poster and a subscriber. However, in all cases, the ability to post and receive specific events is governed by security authorizations (see Section 14.4).
Previous versions of the Tru64 UNIX system supported a variety
of disparate channels for handling events, some standard and some proprietary.
In the simplest case, an
event channel
is a static
ASCII log file containing event information, from a single source, that a
user can view by means of standard UNIX tools (for example,
more
).
Examples of more active channels include the system message logger
(syslog
) and the binary event logger (binlog
),
both of which use daemon processes to receive, log, and forward event information
from multiple sources.
(See
syslogd
(8)
and
binlogd
(8)
for information
about
syslog
and
binlog
.)
EVM provides a single point of focus for multiple event channels by combining events from all sources into a single event stream. Interested parties can either monitor the combined stream in real time or view historical events retrieved from storage. EVM's viewing facilities include a graphical event viewer, which is integrated with the SysMan application suite, and a full set of command-line utilities, which allow events to be filtered, sorted, and formatted in a variety of ways. EVM can also be configured to perform automatic notification of selected conditions.
Note
EVM is a facility for broadcasting messages and should not be used to implement a private point-to-point communication channel between two processes. Using EVM for such purposes could have a negative impact on system performance. If you need to establish communication with another process and the information you will be sending to that process is not of interest to any other processes, you should use a more direct communication channel, for example, sockets or pipes.
žFIGURE x.x - TBS. USE "VISUALIZING EVM" FIGURE FROM JEM'S SLIDES***Ÿ
Figure 14-1
provides an overview of posting,
subscribing, and retrieval operations.
(See
evmget
(1)
for information
on event retrieval operations.)
Figure 14-1: EVM Overview
14.2 Overview of How EVM Events Are Handled
An EVM event is an opaque data package that can be passed between various software components and stored for later review. The data package is opaque in the sense that API programmers must not access the package directly or depend on its format to remain consistent over time, even though it may be possible to discover the format by examining private header files.
In order to be posted, an event must match an event template that is stored in an EVM database. Events that do not have templates cannot be posted. Data items for an event can be supplied in either the posted event itself or the matching template. The contents of the event received by the event subscriber results from merging the data items in the posted event with the data items in the event template. See Section 14.6.3.2 and Section 14.6.3.3 for more information about matching and merging.
A typical event life cycle involves the following operations:
Templates are created for all events that some process may want to post, and these templates are stored in an EVM database when the product or subsystem is installed.
Processes that are interested in receiving events establish a connection to the EVM daemon and then issue subscription requests, specifying a filter that identifies the set of events in which they are interested.
When a process or kernel component detects an eventworthy status change, it connects to EVM and posts the event that corresponds to that status change.
Alternatively, if the event is posted to an existing Tru64 UNIX event channel, the channel processes the event in its own way, which may include logging it. The channel then converts the event to EVM format and passes it to EVM.
EVM validates the request to post the event, checking, among other things, whether the event has a corresponding template in the EVM template database and whether the poster is authorized to post the event. If it is valid, EVM creates a merged version of the event by combining data items in the posted event with data items in the template.
EVM passes the merged event to all processes that have subscribed to receive the event.
Subscribers handle the event in some appropriate way; for example, they could store it, send mail to the system administrator, or initiate an application failover.
14.3 Starting and Stopping EVM
EVM is automatically started at system startup time. Similarly, it is automatically stopped when the system is shut down.
See the System Administration manual for information about starting and stopping the EVM daemon.
See
Section 14.6.3.4
for information on how EVM
establishes the event template database at startup and for information on
how to subsequently modify that database.
14.4 Authorization to Post and Receive Events
Security is an important consideration when dealing with the posting and receipt of events. Uncontrolled access to certain event information could provide an unauthorized user with sensitive information about system operation, and uncontrolled posting of certain events could allow an unauthorized user to initiate critical system actions, for example, a system shutdown.
The system administrator controls access to events at any level by modifying
the EVM authorization file,
/etc/evm.auth
.
See the
System Administration
manual for information about how to control
access to events.
14.5 Contents of an EVM Event
The format, type, and purpose of the data carried in events varies widely. In a simple case, an event is a simple text message. In a more complicated case, an event might be a complex set of binary data that requires an understanding of its format to be able to translate it. For two cooperating applications, an event might be a simple binary flag or count. Note, however, that an event is not required to carry data. In some cases, transmitting only a particular event type from poster to subscriber may be all that the subscriber needs to recognize that a particular status change has occurred.
Event data is complicated by the need to be able to translate it in various ways. For an event to be human readable, it must be possible to combine numeric data with explanatory text. At the same time, the information in the same event may need to be seen as binary data by a subscribing process. In addition, it may also be necessary to have the human-readable form in multiple languages.
The EVM data mechanism allows data to be carried in any way that is appropriate to the event. The constructs that provide this capability are the EVM format data item (one of EVM's standard data items) and EVM variable data items.
Event data structures contain two types of data items:
Standard data items -- A fixed set of items with predefined names.
Variable data items -- Items whose names and types are typically defined by the event designer.
When you create an event as part of a post request, you can include
any number of data items.
When you post the event, EVM automatically adds
certain environmental standard data items such as the host name and the timestamp.
14.5.1 Standard Data Items
Standard data items are those data items that are commonly required in an event and are understood (and may be acted upon) by EVM. The names of standard data items are specified as enumerated constants; consequently, the names themselves do not take up any space in an event.
Some standard data items are inserted in an event by the application that posts the event or by the template for the event. Others are automatically inserted, as needed, by EVM components. You can extract any data item from an event.
Table 14-1
defines some of the commonly used
standard data items.
See
EvmEvent
(5)
for a complete list of the standard
data items.
Table 14-1: Standard Data Items
Data Item | Description |
Event name (Section 14.5.1.1) | Names the event. |
Format (Section 14.5.1.2) | Specifies the event message string. |
Priority (Section 14.5.1.3) | Indicates the importance of the event. Does not affect the order of event distribution. |
I18N catalog (Section 14.5.1.4) | Name of the I18N catalog file for internationalized events. |
I18N msgset ID (Section 14.5.1.4) | Identifies the message set within the I18N message catalog. |
I18N message ID (Section 14.5.1.4) | I18N message ID for event format. |
Reference (Section 14.5.1.5) | Provides a reference to the event explanation text. |
The following sections provide detailed descriptions of the standard
data items defined in
Table 14-1.
14.5.1.1 Event Name Data Item
The event name is the primary way to identify an event. The name must be present for an event to be posted. Although it can be any syntactically valid string, the name should generally identify the posting subsystem and indicate what happened.
The event name is an ASCII character string. It consists of a dot-separated series of components, with the leftmost component representing the most general (least specific) part of the event. Component substrings can include any combination of letters, digits, and underscore characters. No restriction is placed on the number of components that can be included in an event name; however, at least three components must be present for an event to be posted. An event name cannot start or end with a dot.
By convention, the first three components of the event name should identify, respectively, the vendor, product, and application or subsystem. To ensure consistency and facilitate any future changes, it is good programming practice to use a macro to define the common name components.
For example, the names of all events posted by Tru64 UNIX subsystems begin with the following components:
sys.unix.subsystem
The next component after the required components in the event name should identify the event. For example, a file system might post an event with the following name:
sys.unix.fs.filesystem_full
The naming scheme provides an open-ended way to identify events, allowing you to provide detail to any level. Careful naming helps subscribers select specific events or event classes for viewing and monitoring. The more detail in the event name, the more precise the specification criteria can be.
For example, given the event name
myco.myprod.env.temp.high
, a system administrator could monitor all temperature-related events
by specifying
myco.myprod.env.temp
or only the temperature-too-high
events by specifying
myco.myprod.env.temp.high
.
14.5.1.1.1 Reserved Component Names
By convention, the practice of beginning component names with an underscore character (_) to identify a particular type of entity is reserved for system use. The identifying component is appended to the base event name for the event, and, depending on the entity being identified, the following component might then identify the specific instance.
For example, an event named
sys.unix.hw.registered
might contain a variable named
hwid
with a value of 1234,
indicating that the event refers to a device with that hardware ID.
However,
there is no way to search for events relating only to that device because
it is not possible to filter events by the contents of a variable.
Appending
the reserved component name
_hwid
, followed by the hardware
ID, when the event is posted, yields the posted name:
sys.unix.hw.registered._hwid.1234
Now, you can find all events pertaining to hardware device 1234 with the following command:
evmget -f "[name *._hwid.1234]" | evmshow
Or, you can find all events that include any hardware ID with the following command:
evmget -f "[name *._hwid]" | evmshow
Where a reserved component name is followed by an instance value, the posted event should contain a variable with the same name (including the leading underscore) and value. This allows a subscribing client to retrieve the value from the event through EVM's normal API functions, rather than having to parse the event name to find the value.
Local sites and third-party product vendors should establish their own
conventions using a double underscore to start reserved component names, for
example,
_ _prod_code
.
The following reserved name components have been identified:
Component Name | Following Value | Comments |
_hwid |
EHS hardware ID | Identify events referring to hardware device IDs. |
_hostname |
System host name | This is the host name referred to by the event, which may not be the posting host. For example, a monitoring application could include this in events reporting that it has detected a failure of another system. |
_failure |
None | Identifies failure events. |
The following general rules apply to the use of reserved components:
It is not necessary to include a reserved component in an
event's template name when registering the event, because the EVM name matching
scheme finds the best match for a posted event.
In the example shown previously
in this section, the template (and the base name of the event) would be
sys.unix.hw.registered
.
Reserved components are appended to the
name to give a further level of detail when the event is posted.
An event can include any number of trailing reserved component names, in any order, and in any component position following the event's base name.
Any product or subsystem can post events containing reserved
components names.
This convention allows all events referring to hardware
components to be found easily even if some are not being posted with a
sys.unix.hw
prefix.
14.5.1.1.2 Comparing Event Names
Use EVM's API functions to compare the name of an event with the name
you are looking for.
Do not use
strcmp()
; the name of the
incoming event may have more components than you expected.
See
Section 14.7.10.9
for more information on event name matching.
14.5.1.2 Event Format Data Item
The format data item is a human-readable character string that summarizes the event. It can include references to other data items contained in the event. Consider the following examples:
Application close-down has started Close-down of application $app has started
In the second example,
$app
is replaced by the name
of the application.
The format line should begin with a brief and consistent identification of the posting component, and it should be a concise indication of what has transpired. Consider the following guidelines:
It is not necessary to write a full sentence, but be sure that what you write can be clearly understood.
Do not write multiple sentences and do not include a period at the end of the message. (Use the event's explanation text to provide the full (and more grammatical) description of what happened. See Section 14.6.2 for information on how explanation text is handled.)
Try to place the most important information -- the phrase that tells the administrator what happened -- at the beginning of the line to make it unnecessary for the administrator to scroll the viewer window horizontally to find this information. Variable data is often better placed towards the end of the line.
You do not have to include a reference to every variable in the format string. The administrator can easily get the variable data by requesting a detailed description of the event. However, try to include the key data, such as the name of a file system or device.
You can include variable names for substitution
by preceding them with the
$
character, for example,
$app
.
If necessary, you can include standard data items by preceding
their names with the
@
character, for example,
@host_name
.
You can specify standard data item names in upper-
or lowercase, but variable names are case sensitive.
You can escape the special meaning of
$
and
@
characters in the format text by preceding them with a backslash
(\
).
To include a literal backslash in the text, use a
double backslash (\\
).
To separate the name of the data
item or variable from adjacent text, you can surround the name in braces,
for example,
${app}
.
Table 14-2
shows some examples of the way that
variables and data items are substituted into an event's format text to produce
a formatted version of the event.
Table 14-2: Substituting Variables into Event Text
Format Data Item | Variable Data Item | Resulting Text |
Application started |
None | Application started |
Debug message: $msg |
msg (string) = "Checkpoint reached" |
Debug message: Checkpoint reached |
Temperature too high: $temp |
temp (float) = 87.2 |
Temperature too high: 87.2 |
This event was posted by @user_name |
None | This event was posted by jem |
14.5.1.3 Event Priority Data Item
The priority of an event is used as the basis for
event selection to log, sort, review, and take action -- human or automated.
The priority is not used to establish the order in which events will be delivered
to subscribing EVM clients; events are delivered in the order in which they
are received by EVM.
The priority is an integer value in the range 0-700,
with 0 being the least significant priority.
See
EvmEvent
(5)
for information
about the various priority levels.
You can expect that system administrators will respond to event priorities in the following ways:
Configure the logger to log all events with priority 200 (notice level) or higher.
Configure the logger to send mail or issue an alert (for example, page someone) when an event of priority 600 (alert level) or higher is posted.
Use the viewer to search for all events with priority 300 (warning level) or higher.
Use the viewer to peruse logged events of all priorities to help analyze problems or to verify that the system is operating correctly.
Do not hardcode the priority into the posting code; it should always be established in the template file.
When choosing the priority of an event, try to be consistent with other
events in your own application and other similar applications.
Do not try
to use the full range of possible priorities if they are not applicable to
your application.
Use the priority information in
EvmEvent
(5)
as a guideline,
and look at existing templates to see what priorities are being used by similar
events.
Choose the priority of an event carefully and objectively. Think about what you are reporting, not the possible consequences. For example, some application failures are critical, but not all of them are. The determination of which applications are critical should be left to a higher-level component. The higher-level component can subscribe to error events and issue a critical-level event if it is notified of an application error that it knows to be critical.
Based on these considerations, events that indicate an error situation but do not necessarily meet the critical criteria should be given a priority of 400 (error level), not 500 (critical level). On the other hand, an event reporting an excessive system temperature is almost always critical.
Event priorities for events that are related to each other should be
established independently of each other.
This means, for example, that if
you post a failure event with a priority of 500 to indicate that an application
has failed and you later post a related event to indicate that is has been
restored, the second event should have a lower priority, for example, 200.
Giving the restore event a priority of 500 may cause an action that is not
appropriate for the event.
14.5.1.4 I18N Catalog Name, Message Set ID, and Message ID Data Items
If your events contain text that is likely to be viewed by administrators in different countries, consider internationalizing them. To internationalize your events, you need to supply an I18N catalog file containing the format strings for all of the events. Note that you should still include the text within the event, in case the catalog file is not available when the event text is viewed.
The catalog file should be located according to normal I18N rules, and you must include the name of the file in your event specifications. You can provide different versions of the catalog file for different language locales. See the Writing Software for the International Market manual for more information.
You can optionally break the catalog file into multiple message sets and specify the message set ID in your events. Note that all messages pertaining to a particular event must belong to the same set.
The data items
I18N_catalog
and
I18N_msgset_id
define the catalog name and, if applicable, the message set for
the format text and any string variables contained in the event and having
an associated message ID.
The
I18N_format_msg_id
data item
gives the message ID for the format text.
If no catalog or message ID is supplied,
the format text specified in the format data item is used to display the event
summary.
See
Section 14.6.4
for information on how to establish
translations for event text.
14.5.1.5 Reference Data Item
The reference data item is used to find the event explanation text for a detailed display of the event. The value of this item is passed to the event channel's explain script, along with the name of the event, to allow it to identify the explanation text associated with the event. Because each channel can have its own explain script, the format of the field might be different for each channel -- however, for events that are stored in and retrieved from the EVM log (the evmlog channel), this item should contain a string of the following form:
cat:catalog_name[:set_number]
The catalog_name is the name of an I18N catalog containing the explanation text for the event. To allow the explain script to locate the appropriate message, each explanation message in the catalog must begin with the name of the event enclosed in braces.
The optional set_number is the number of the catalog message set containing the explanation. If no set number is supplied, the whole catalog is searched.
Third-party product vendors and local applications should not add explanations
to the Tru64 UNIX explanation catalog,
evmexp.cat
,
but should provide separate catalogs.
The value of this item should generally
be set as a global data item in the template file.
See
Section 14.6.2
for details on how to
write event explanation text.
14.5.2 Variable Data Items
You can use variable data items in your events to provide any information that is different for each instance of an event. For example, if you are posting an event because you have detected a high temperature, you could include the actual temperature as a floating-point value in a variable data item.
Variable data items have the following properties:
A name
A type
A value
A size (implicit for most types)
An I18N message ID (optional -- applies to string variables only)
A variable name can be made up of any combination of upper- or lowercase alphanumeric characters and the underscore (_) character. Names should be meaningful, but remember that the variable name is carried inside the event, and the longer the name, the larger the physical event.
A variable data item can be extracted and used directly by the subscriber or combined with the event format text string to produce a formatted version of the event for display.
Table 14-3
shows the variable types that EVM
supports.
Table 14-3: EVM's Variable Data Types
Type Identifier | Size and Type |
EvmTYPE_BOOLEAN |
8-bit integer |
EvmTYPE_CHAR |
8-bit character |
EvmTYPE_INT16 |
16-bit signed integer |
EvmTYPE_INT32 |
32-bit signed integer |
EvmTYPE_INT64 |
64-bit signed integer |
EvmTYPE_UINT8 |
8-bit unsigned integer |
EvmTYPE_UINT16 |
16-bit unsigned integer |
EvmTYPE_UINT32 |
32-bit unsigned integer |
EvmTYPE_UINT64 |
64-bit unsigned integer |
EvmTYPE_FLOAT |
32-bit floating-point value |
EvmTYPE_DOUBLE |
64-bit floating-point value |
EvmTYPE_STRING |
Null-terminated character string |
EvmTYPE_OPAQUE |
Binary data whose size must be specified explicitly |
In general, because variables contain information that is specific to
an instance of an event, they should be included in the event by the poster.
However, for documentation purposes, it is also useful to include variable
names and types, along with dummy values, in the template version of the event.
See
Section 14.6.3
for information on templates.
14.6 Designing a Set of Events
When designing an application or subsystem, you need to also design an associated set of events.
EVM events need to be designed with care. Events must meet the requirements of two styles of interfaces: the human style (readable text) and the program style (binary data). Once an event is posted, it can be seen and acted upon in either its text form or its binary data form.
Designing an event involves the following considerations:
Decide on a family name for a set of related events. (See Section 14.5.1.1 for details.)
Create a list of the status changes that might be of interest to a monitoring entity, and choose a name for each event. (See Section 14.6.1 for details.)
Decide on the contents of each event. All events need a name. Most events need a format string and a priority, and many also need variables. For each variable, consider the type and possible values. (See Section 14.5 for details.)
Write a detailed description of each event for documentation purposes. Include details on what the event means, when it might occur, any actions the user or responsible subscriber should take in response to it, and the contents of the event (particularly any variable data items). The explanation text is usually held in a catalog file and can be accessed for display. (See Section 14.6.2 for details.)
For each event, decide which items go into the template and which will be coded by the poster. Except for the event name, all items in both posting code and templates are optional. If an optional item is declared in both places, the poster's declaration has precedence. (See Section 14.6.3 for details on templates, Section 14.5 for details on event items that are commonly posted, and Section 14.6.3.3 for details on how data items in templates and posted events are merged.)
Decide whether the events should be internationalized. If so, choose a name for the I18N catalog file and establish any required message sets within the catalog. (See Section 14.6.4 for details.)
Designers must recognize that an EVM event is an interface upon which
other programs or subsystems may rely.
Therefore, once established, the interface
should generally not be changed.
14.6.1 Deciding Which Status Changes Are Eventworthy
The importance of an event can vary from application to application, and it may be difficult in some cases to decide whether a status change recognized by your code is important enough to communicate to others.
It is advisable to post events for the following types of occurrences:
When a component makes a change to the system. For example, a system management program might post an event if it adds a new user to the system or changes the network configuration.
When a potentially significant error occurs. For example, a system management program should post an event if it finds that a key system file is missing, and a device driver should post an event if it detects a disk failure.
When a boundary indicating that a failure is likely to occur is passed. For example, file system management software might post a warning event when it detects that a file system has passed a boundary and become at least 95 percent full. Take care, however, to avoid posting repeated events of this nature if the state oscillates around the boundary. Your code should typically only post the same event again if the condition is still true after some preset time interval has elapsed, even if the state has dropped below the boundary several times during the time interval.
When a user is granted a privilege or takes some action that affects the operation of the system. For example, a system management program might post an event when a disk is mounted or unmounted, or when the system is being closed down.
Do not post events for the following types of occurrences:
In response to an error made by a user in a session that is under your control and where you have direct communication with the user. For example, a configuration program should not post an event just because a user responded incorrectly to a prompt.
If you are dealing with an "error" that meets the following criteria: it is expected and is normal behavior, you know why it is happening, and it will not cause a system administrator to take any action.
If the condition that you have just detected was reported very recently, and reporting it again will serve no useful purpose.
If you do post an event, avoid posting the same event repeatedly over a short period of time if, for example, a condition oscillates between true and false. In some cases, it may be helpful to post an occasional summary event, stating, for example, that the same incident has occurred multiple times within a specified length of time.
High levels of event activity can cause the loss of events because an
application may not be able to handle the message load.
See
Section 14.7.10.10
for information on how to handle missed events.
14.6.2 Writing Event Explanation Text
You should supply explanation text for every EVM event.
Explanation
text is not included within an event when it is posted, but might be held
in a catalog file and referenced by the contents of the event's reference
and name data items (see
Section 14.5.1.5).
The explanation
text for
sys.unix
events is physically held in a catalog
file named
evmexp.cat
.
To display the explanation text
for an event, use the
-x
or
-d
option
of
evmshow
.
Your explanation should include the name of the event and a description of what it means. If an event can mean different things, depending on the context (for example, the number of occurrences within a given time period or the presence of certain other events), then state that fact and provide a couple of sample situations. Whenever an action is required, state what the action is. If the action varies with the context, then state that fact and provide examples. If the event does not require an action from the user, it is often helpful to state that explicitly.
Example 14-1
shows sample explanation text
for a system event.
Example 14-1: Sample Event Explanation Text
Example 1: EVENT sys.unix.evm.daemon.event_activity Explanation: This high-priority event is posted by the EVM daemon when it detects a high number of events occurring over several minutes. Action: Use the EVM event viewer or the evmget(1) command to review the event log for the source of the activity. If the log does not show high activity around the time at which this event was posted, it is likely that the events were low priority, and hence were not logged. You can monitor low-priority events by running the evmwatch(1) command with an appropriate filter, or by temporarily reconfiguring the EVM logger to log low-priority events. Note: You can change the parameters that control the posting of this event by modifying the daemon configuration file, /etc/evmdaemon.conf.
14.6.3 Designing Event Templates
Each posted event must have a template, and each template can support one or more events. A template defines the event name and any fixed data item within the event.
An event template is used for two purposes:
To register the event with EVM. An event that is not registered cannot be posted.
To allow data items that are common to most or all instances of an event (for example, message catalog information) to be held in the template for that event. Including fixed data items in a template makes it easy to update this information and relieves event posters from the need to repeatedly provide this information in their post requests. See Section 14.6.3.1 for information on how to decide what to include in an event template.
See Section 14.6.3.3 for a description of the way that the contents of event templates are merged with the contents of a posted event.
Templates are stored in files that are held in a centralized EVM database
(see
Section 14.6.3.4).
You can put any number of event
templates in a template file.
14.6.3.1 Deciding what to Put in an Event Template
Deciding which items should be supplied by event posters and which should be supplied in the template is a design-level decision. As a general rule, it is better to include constant data items in the event template than to have them hardcoded into an event by posting programs.
A key benefit of the event template mechanism is that it allows you to centralize the fixed attributes of all of your application's events, making it very easy to change them during the development cycle and providing a single place to look for the information once the application has been deployed.
As a general rule, try to minimize the amount of event information that you hardcode into a posting application program and to put as much as possible into the event template. Typically, your application should only provide:
Event name (required -- it must have at least three components and be at least as long as the event name in the matching template)
Contents of any variables
The template should generally include:
Event name (required -- it must have at least two components)
Priority
Format text
I18N message catalog information (for internationalized events)
Cluster_event flag (if applicable)
Variables (initialized to zero values or empty strings)
Although the posting application is generally expected to supply values for variable data items when it posts an event, it is helpful to include the variable in the template as well because this makes the template more valuable as a point of documentation. In the template, you should typically give variables zero values (or for string variables, an empty string). In some special cases, it may be useful to provide a real default value in a template (which can be overridden by the poster) -- if this is the case, be sure to describe the situation in a comment in the template file.
The following source file is an example of an event template file containing one event:
# Example event file priority 200 # Default priority ref cat:myapp_exp.cat # Global reference event { name myco.myapp.env.temperature format "Temperature is $temperature" var { name temperature type FLOAT value 0,0 } }
You can include as many variables as you like in an event, but note
that opaque variables (binary structures) are not supported in templates.
14.6.3.2 Matching the Names of Posted Events with Event Template Names
Each time an attempt is made to post an event, EVM looks in its template database for the template whose event name matches the name of the posted event. If no match is found, EVM returns an error code to the program that posted the event. If a match is found, EVM then retrieves any data items held in the template and combines them with the items supplied by the program that posted the event. This operation produces the merged event that is distributed to subscribers. See Section 14.6.3.3 for details on the merging operation.
The template-matching process requires a match only between the leftmost components of a posted event's name and all of the components of a template event's name. EVM looks for the best match in its database, using the following rules:
The closest match is the template event whose name exactly matches the most components of the posted event, when compared from left to right.
A match is considered to have occurred if the posted event has at least as many components as the closest database entry, but not if it has fewer components.
Components must match exactly.
At least two components are required in the template name, and at least three are required in the name of the posted event.
It is recommended that you provide a separate template for each distinct event your application will post because this allows you to centralize the event's unique information by storing it in the template. However, the benefit of the best-match mechanism is that it allows you to extend an event's name with different instance information each time you post it; for example, you could add a device name or temperature value as additional components. Having these additional instance components makes it easier to filter and sort events. See Section 14.5.1.1.1 for examples of ways to extend an event's name.
Table 14-4
shows some examples of event name
matching between event templates and posted events.
Table 14-4: Name Matching Examples
Posted Event Name | Template Event Name | Match? |
myco.myprod.env |
myco.myprod.env |
Yes |
myco.myprod.env.temp.high.70 |
myco.myprod.env.temp |
Yes |
myco.myotherprod |
myco.myotherprod.start |
No -- the posted event has too few components |
14.6.3.3 Merging Data Items from Templates and Posted Events
After the EVM daemon has successfully validated a posted event, it merges the data items contained in the posted event with any data items contained in the template, and then distributes the merged event to any clients who have subscribed to the event. The merge process gives the event designer maximum flexibility in deciding which text and data items are to be provided by the template and which are to be provided by the poster.
Figure 14-2
illustrates the concept of event merging.
Figure 14-2: Posted Event and Template Merging
If the same data item is supplied in both the template and the posted event, the posted value is used in the merged event.
The merge process yields a canonical (binary) event structure that contains
a combination of the data items from the posted event and the template.
The
merged event is distributed to subscribers in its canonical form, not as a
formatted message, and subscribers must use the EVM API functions to format
the event for display or to extract information from it.
The API functions
are described in
Section 14.7.
14.6.3.4 Installing Template Files -- Location, Naming, Ownership, and Permission Requirements
Event template files should normally be installed as part of the product
or application installation process.
System templats are stored in subdirectories
under
/usr/share/evm/templates
, while third-party product
and local application templates should be stored under the local template
directory,
/var/evm/adm/templates
.
A link is provided from
the system template directory to the local directory.
To add templates for
a product or local application, the application's installation process should
create an appropriately named subdirectory of the local template directory
and install its templates in the new directory.
EVM's template search policy
follows symbolic links, so you may also install the templates in a directory
more closely associated with the application, and connect it to the local
directory with a link.
Template files must have the suffix
.evt
, must be
owned by
root
or
bin
, and must have
one of the following access permissions: 0600, 0400, 0640, or 0440.
Also be
sure to give the new directory appropriate permissions.
After you install the file, run (as root) the command
evmreload -d
to cause EVM to recognize the new templates, and then
check for any errors.
See
evmreload
(8)
for details.
14.6.3.5 Checking Event Template Registration
You can check whether your templates are registered by using
evmwatch
with the
-i
option.
For example, the
following command lists the names of all event templates registered for the
myapp
application:
evmwatch -i -f "[name myco.myprod.myapp]" | evmshow -t "@name"
Use the
-d
option of
evmshow
to
display the template details.
Note that
evmwatch
will
not return the templates of any events for which you do not have access authorization,
so you may need to be logged in as root to see your templates.
14.6.4 Establishing Translations for Event Text (I18N)
The objective of event internationalization (I18N) is to allow an event's format data item, and the values of any string variables contained in the event, to be converted to another language for display. If you are developing a product that will be used internationally, you can include support for translation of any or all of these items.
Because different users may want to view the same stored event in different languages, language interpretation must be performed on-the-fly when the event is formatted for presentation, not when it is posted. It is impractical for an event to carry the text in all possible languages, so its associated message catalog must be available when the event is formatted. Product developers are responsible for providing message catalogs and for including them for installation along with their products. To handle the case in which the catalog is not available, an internationalized event can carry a default native-language string.
An internationalized event can contain the following items:
An I18N message catalog name
An I18N message set identifier (optional)
An I18N message identifier for the format data item
A separate I18N message identifier for each internationalized string variable
Default native-language strings for any or all of the previous items in this list
All message identifiers for the event must relate to the same message catalog, and they must all belong to the same message set (1, by default).
In general, the catalog ID, set ID, and message ID for the event format string should all be supplied in the event template because the format string is usually fixed. Where events contain string-type variables, the variables are likely to refer to items such as device names or application names, which usually will not need to be translated, regardless of the language for display -- and hence in most cases it will not be necessary to supply a message ID. In the rare cases in which the value of a string variable does need to be translated, the poster must supply the message ID.
Table 14-5
shows some internationalized values for
an example event.
Table 14-5: Example Data Item Values for an Internationalized Event
Event Data Item | Value | Message ID |
Name | acme.prod.env.temp |
n/a |
Message catalog | acme_prod.cat |
n/a |
Format string | Temperature of sensor $sensor is
$temperature |
541 |
String variable "sensor" | S27 |
n/a |
String variable "temperature" | high |
542 |
English and French versions of
temperature.cat
contain:
English
541:Temperature of sensor $sensor is $temperature
542:high
543:low
French
541:La temperature du senseur $sensor est $temperature
542:haute
543:basse
When the viewer has to display the event, it invokes a format function, which, depending on the user's locale setting, returns either of these strings if it can find the appropriate catalog file and if the file contains the specified messages:
"Temperature at sensor S27 is high" "La temperature du senseur S27 est haute"
If the format function cannot interpret the events from a catalog, it uses the values that are carried in the event and returns the following message regardless of the user's locale:
"Temperature at sensor S27 is high"
If an event file is passed to another system for analysis, it is possible that the associated catalog file will be unavailable. If the event includes default values, as in the previous example, it can still be displayed in the original language. If the event does not include defaults, the formatted string can still show the event name, along with a dump of any variables. For example:
Event "myco.myprod.env.temp": $sensor = "S27" $temperature = "high"
See the
Writing Software for the International Market
manual for additional information on I18N issues.
14.7 The EVM Programming Interface
EVM events are opaque data structures that
can only be accessed and manipulated by using EVM's application programming
interface (API) functions.
The following sections provide programming information
and examples of some of the operations commonly performed on EVM events.
14.7.1 The EVM Header File
Programs that use EVM functions must include the following header file:
#include <evm/evm.h>
14.7.2 The EVM API Library
Programs that use EVM API functions must link against either the shared
library
libevm.so
or the static library
libevm.a
.
The shared library is located in the root-partition directory,
/shlib
, and consequently is available to programs that may need
to be run when the system is in single-user mode.
However, because the EVM
daemon is not started until run level 2, any attempt to connect to the daemon
while in single-user mode will fail.
There is a symbolic link from
/usr/shlib
to the shared library in
/shlib
,
making the library available to programs linked using the default library
search path.
14.7.3 Return Status Codes
The status codes returned by EVM functions are enumerated in the
header file
evm/evm.h
.
EVM functions commonly return the
following values:
EvmERROR_NONE
-- The operation completed
without error.
EvmERROR_INVALID_ARGUMENT
-- One of
the arguments passed to a function was invalid.
EvmERROR_INVALID_VALUE
-- A structure
contained an invalid value.
EvmERROR_NO_MEMORY
-- An operation
failed because an attempt to acquire heap memory failed.
EvmERROR_NOT_PRESENT
-- A requested
item is not present in the event.
The EVM API does not use signals in its normal processing, and in general does not interfere with an application program's use of signals. However, because Tru64 UNIX's default action is to silently terminate a process that attempts to write to a local connection if there is no process to read the data, there is a danger that a client process could exit without trace if the EVM daemon should terminate before or during activity on the connection.
To prevent this from happening, the
EvmConnCreate()
function checks whether the caller has already established a handler for SIGPIPE,
and, if not, the function installs a default handler.
The handler takes no
action if the signal occurs, but its presence prevents the client from terminating.
A program can override the EVM handler by setting its own handler either
before or after the call to
EvmConnCreate()
.
If it is important
that the program takes the default action, set the action to SIG_DFL after
calling
EvmConnCreate()
.
14.7.5 EVM In Multithreaded Programs
All EVM API functions are thread-safe; in the few cases where it is necessary to use internal static storage, they use locks to protect the storage from simultaneous access by separate threads. Nevertheless, if you are using EVM API calls in a multithreaded program, you need to take certain precautions to avoid synchronization errors:
If possible, restrict the use of all entities returned by any API function to the thread in which it was established. These items include:
Entity | Type | Returned by |
Connection context | EvmConnection_t |
EvmConnCreate() |
Event | EvmEvent_t |
EvmEventCreate() ,EvmEventCreateVa() ,EvmEventRead() ,EvmEventDup() |
Data item | EvmItemValue_t |
EvmItemGet() |
Data item list | EvmItemList_t |
EvmItemListGet() |
Variable | EvmVarValue_t |
EvmVarGet() |
Variable list | EvmVarList_t |
EvmVarListGet() |
Event filter | EvmFilter_t |
EvmFilterCreate() |
Connection fd | EvmFd_t |
EvmConnFdGet() |
If it is necessary to refer to these entities in more than one thread, it is essential that you protect them against simultaneous access or update by using locks.
If you do not follow these rules, it is highly likely that random errors
will occur.
14.7.6 Reassigning and Replicating EVM Events
If you need to reassign EVM events after they have been created, received, or read, it may be useful to understand how an event is held in memory.
The
EvmEvent_t
type defines a pointer to a short
handle structure that holds some control information and a pointer to the
event body.
When you use
EvmEventCreate()
or any related
function to create a new event, the function allocates heap memory separately
for the handle and the event body, stores the pointer in the handle, and returns
the pointer to the handle as the function's
EvmEvent_t
output argument.
You must use the returned pointer any time you need to refer
to the event.
If you modify the event (by adding a variable, for example), it is likely that the space used by the event body will be freed and reallocated. When this happens, the address stored in the handle is automatically updated to reflect the new location; hence, the reallocation is completely transparent. The location of the handle remains unchanged for the life of the event.
If you need to transfer an event from one variable to another, you can
do so using a simple C-language assignment statement between variables of
type
EvmEvent_t
.
Because the value you hold is simply a
pointer to a constant location -- the event's handle -- you can,
if you wish, continue to use both of the variables to refer to the event.
However, if you subsequently use
EvmEventDestroy()
to destroy
the event, you must be sure to discard both references.
You must also be aware that reassigning an event copies just the reference
to its handle; it does not copy the event body.
If you need to make a completely
independent copy of the event, you can do so using the
EvmEventDup()
function call.
14.7.7 Callback Functions
EVM posting and subscribing clients connect to the EVM daemon using
the
EvmConnCreate()
function call, and must specify one
of three possible response modes:
EvmRESPONSE_IGNORE
,
EvmRESPONSE_WAIT
or
EvmRESPONSE_CALLBACK
.
The
modes are described in
EvmConnCreate
(3).
Subscribing clients are required to specify
EvmRESPONSE_CALLBACK
as the response mode, and incoming events are passed to them by
the callback function that you supply as the fourth argument to
EvmConnCreate()
.
An example of the use of a callback function by
a subscribing client is given in
Section 14.7.10.6.
When working with a callback function, it is important to understand
that your function is not called asynchronously, in the manner of a signal
handler.
Rather, your program must monitor the connection for input activity,
using
EvmConnWait()
or
select()
or a
related function, and then call
EvmConnDispatch()
to handle
the activity.
EvmConnDispatch()
then reads an incoming
message from the connection and invokes your callback function, if necessary.
The possible reasons the callback being invoked are listed in
EvmCallback
(5).
As with any function, the arguments passed to your callback function are passed on the program stack and are available only within the scope of the function; therefore, if you want to save any values for use after you have returned from the callback, you must copy them to global memory space before returning.
Note that if the callback is reporting an incoming event and you wish
to preserve the event instead of handling it and then destroying it within
the callback, you can simply declare a globally accessible variable of type
EvmEvent_t
and assign the incoming event to it, for example:
/* In global declarations */ EvmEvent_t SavedEvent; ... /* In your callback function */ SavedEvent = cbdata->event;
In this case, you must not destroy the event in the callback function, because the assignment copies just the reference to the event, not the event body. Remember, however, that you must still arrange to destroy the event from elsewhere once you have finished with it; otherwise, you may have introduced a memory leak.
See
Section 14.7.6
for a description of
event assignment.
14.7.8 Handling Disconnections
If your program is a subscribing client, it is particularly important that you handle disconnection from the EVM daemon correctly. A disconnect should not occur under normal operation, but may occur in a system that is being tested or if a fault is encountered.
Although you should always test the return code from functions dealing with a connection, failure to do so is most likely to cause a system impact in a subscribing client, which may spend most of its time waiting for activity on a connection.
If a disconnect occurs, your program will drop out of its
select()
or
EvmConnWait()
call, and subsequent
calls to
EvmConnCheck()
and
EvmConnDispatch()
will return failure status codes.
When this happens, you should not return
immediately to the
select()
or
EvmConnWait()
call because that would result in a CPU-bound loop.
Instead, check whether
the status code indicates a connection error and, if so, destroy the connection
using
EvmConnDestroy()
and then attempt to reconnect.
If
the initial attempt to reconnect fails, you should arrange to try again after
a reasonable delay -- say 30 seconds -- and continue to retry periodically
until the connection is re-established.
Note that not
all
error codes returned by the connection
functions indicate a disconnect.
In particular, it is very possible that the
program will drop out of
EvmConnWait()
as a result of a
signal.
If this is indicated, the correct action is to return immediately
to the
EvmConnWait()
call.
14.7.9 Using Event Filters
An event filter is used to identify the set of events in which you are interested. Once established, a filter evaluator is given a string defining the events of interest, and can then be passed a series of events, returning for each a Boolean indication of whether the event passes the filter.
Filters are used in EVM subscribing client programs to specify the set
of events for which they wish to subscribe, and may also be used in determining
the action to be taken for the events they receive.
To limit the set of events
returned, filters can also be used by several of EVM's command-line utilities
as start options.
See the
System Administration
manual for information on the use
of filters by command-line utilities, and see
EvmFilter
(5)
for a formal
description of filter syntax.)
See
Section 14.7.10.8
for an example of how to use
filters.
14.7.10 Sample EVM Programming Operations
Examples of the following operations are provided in the sections that follow:
Performing simple event manipulations (Section 14.7.10.1)
Using variable-length argument lists (Section 14.7.10.2)
Adding and retrieving variables (Section 14.7.10.3)
Posting events (Section 14.7.10.4)
Reading and writing events (Section 14.7.10.5)
Subscribing for event notification (Section 14.7.10.6)
Handling multiple I/O sources (Section 14.7.10.7)
Using filter evaluators (Section 14.7.10.8)
Matching event names (Section 14.7.10.9)
Dealing with missed events (Section 14.7.10.10)
14.7.10.1 Performing Simple Event Manipulations
All EVM clients need to work with the EVM event, an opaque binary structure that can hold standard data items and variables. Example 14-2 shows you how to create an event, add items to it, and then retrieve the items from it.
The example introduces the following functions:
EvmEventCreate
-- Creates an empty
event.
(See
EvmEventCreate
(3)
for details.)
EvmEventDestroy
-- Destroys a previously
created event, freeing its memory.
This function must be used if it is necessary
to free an event.
Although the event reference points to a structure allocated
from the heap, that structure contains references to other structures, and
hence using
free
() directly on the event reference will
result in lost memory.
(See
EvmEventDestroy
(3)
for details.)
EvmItemSet
-- Sets data item values
in the event.
The list of items and variables to be supplied is the same as
that supplied for
EvmEventCreateVa
.
(See
EvmItemSet
(3)
for details.)
EvmItemGet
-- Supplies the value of
a specified event data item.
(See
EvmItemGet
(3)
for details.)
EvmItemRelease
-- Releases any memory
that was allocated when a specified data item was retrieved from an event
by
EvmItemGet
().
(See
EvmItemRelease
(3)
for details.)
Example 14-2: Performing Simple Event Manipulations
#include <stdio.h> #include <evm/evm.h> main() { EvmEvent_t event; EvmItemValue_t itemval; EvmStatus_t status; EvmEventCreate(&event); [1] EvmItemSet(event,EvmITEM_NAME,"myco.examples.app.started"); [2] EvmItemSet(event,EvmITEM_PRIORITY,200); status = EvmItemGet(event,EvmITEM_NAME,&itemval); [3] if (status == EvmERROR_NONE) { fprintf(stdout,"Event name: %s\n",itemval.NAME); EvmItemRelease(EvmITEM_NAME,itemval); } EvmEventDestroy(event); [4] }
You can create an empty event with
EvmEventCreate()
.
When you use this function, you supply a pointer to the event
handle, and you receive an event that contains no standard data items.
Even
though it is empty, the event does take up memory, and you must eventually
use
EvmEventDestroy()
to free the space.
[Return to example]
You can add any of the standard data items to the event using
EvmItemSet()
.
In most cases, however, the only item you will want
to add in your program is the name of the event -- other standard items
will be automatically added when you post the event, or are better included
in the event template.
See
EvmItemSet
(3)
for a list of the settable items.
[Return to example]
You can retrieve any item from the event using
EvmItemGet()
.
The value is copied from the event into storage referenced through
your
EvmItemValue_t
structure, so you must use
EvmItemRelease()
to release the storage once you have finished with
it.
Retrieving the item does not remove it from the event; you receive a copy
and you can get it as many times as you wish.
This piece of code retrieves the event's name (which was added earlier), prints its value, and then releases the storage. You should always check the return status from the get operation because you may be requesting an item that is not present in the event. [Return to example]
When you have finished with the event, free the storage space used by the event. [Return to example]
You can reduce the size and improve the efficiency of your code by creating an event and adding items to it in a single step, using the varargs (variable-length argument list) version of the create function -- and you can add items to an existing event efficiently by using the varargs version of the item-set function.
Example 14-3 introduces the following functions:
EvmEventCreateVa
-- Creates an event
and supplies item names and values in a single call.
(See
EvmEventCreateVa
(3)
for
details.)
EvmItemSetVa
-- Sets data item values
in the event.
The list of items and variables to be supplied is the same as
that supplied for
EvmEventCreateVa
.
(See
EvmItemSetVa
(3)
for details.)
Example 14-3: Using Variable-Length Argument Lists
#include <stdio.h> #include <evm/evm.h> main() { EvmEvent_t event; EvmEventCreateVa(&event, [1] EvmITEM_NAME,"myco.examples.app.started", EvmITEM_PRIORITY,200, EvmITEM_NONE); EvmItemSetVa(event, EvmITEM_NAME,"myco.examples.app.finished", [2] EvmITEM_PRIORITY,100, EvmITEM_NONE); EvmEventDestroy(event); [3] }
Each item you include in
EvmEventCreateVa()
must have an identifier and a value, and the argument list must be terminated
with an
EvmITEM_NONE
identifier.
[Return to example]
The varargs version of
EvmItemSet()
uses
the same style of argument list as
EvmEventCreateVa()
.
In this example, items that are already present in the event are being added,
so the new values just replace the old ones.
[Return to example]
When you have finished with the event, free the storage space used by the event. [Return to example]
Example 14-4 shows you how to add variable data values to an event and how to retrieve the variables from an event.
The example introduces the following functions:
EvmVarSet
-- Sets the value of a named
variable data item in an event.
This function is used both for adding a variable
and for altering the value of an existing variable.
(See
EvmVarSet
(3) for details.)
EvmVarGet
-- Returns details on a
specified event variable in a given variable structure.
The caller must free
any memory used by the variable by calling
EvmVarRelease
().
(See
EvmVarGet
(3)
for details.)
EvmVarRelease
-- Releases any memory
that was allocated when the specified variable was retrieved from an event
by
EvmVarGet
().
It does not release the specified variable
structure, only any heap storage referenced by the structure.
(See
EvmVarRelease
(3)
for details.)
Example 14-4: Adding and Retrieving Variables
#include <stdio.h> #include <evm/evm.h> void main() { EvmEvent_t event; EvmStatus_t status; EvmVarValue_t varval_1, varval_2; [1] EvmVarStruct_t varinfo; EvmEventCreateVa(&event, EvmITEM_NAME,"myco.examples.app.finished", EvmITEM_NONE); varval_1.STRING = "my_app"; varval_2.UINT16 = 17; EvmVarSet(event,"progname",EvmTYPE_STRING,varval_1,0,0); [2] EvmVarSet(event,"exit_code",EvmTYPE_UINT16,varval_2,0,0); status = EvmVarGet(event,"progname",&varinfo); [3] if (status == EvmERROR_NONE) { fprintf(stdout,"%s: %s\n",varinfo.name_p, varinfo.value.STRING); EvmVarRelease(&varinfo); } status = EvmVarGet(event,"exit_code",&varinfo); if (status == EvmERROR_NONE) { fprintf(stdout,"%s: %d\n",varinfo.name_p, varinfo.value.UINT16); EvmVarRelease(&varinfo); } EvmEventDestroy(event); [4] }
To add a variable to the event, you must first place the value
in a union of type
EvmVarValue_t
.
The members of the union
have the same names as the EVM variable types.
[Return to example]
Use
EvmVarSet()
to add the variables to
the event, giving them meaningful names.
The final two arguments are set to
0 unless you are adding an opaque variable or supplying an I18N message ID
for a string variable.
[Return to example]
You can retrieve the value of any variable by passing its name
to
EvmVarGet()
, which copies the value into an
EvmVarStruct_t
structure.
The structure also contains the name,
type, and size of the variable, so you can write generic code to handle any
type of variable.
Retrieving a variable does not remove the variable from
the event, and you can retrieve it as many times as you wish.
The returned
information always take up space in heap memory, so you must use
EvmVarRelease()
to clean up when you have finished with the value.
[Return to example]
When you have finished with the event, free the storage space used by the event. [Return to example]
The most likely reason for creating an event is to post it. Posting an event results in the event being distributed to subscribers by the EVM daemon. Before you can post the event, you must create a posting connection to the daemon.
Example 14-5 shows how to create the connection, post the event, and disconnect.
The example introduces the following functions:
EvmConnCreate
-- Establishes a connection
between an application program and EVM, and defines how I/O activity is to
be handled.
A separate connection must be established for each type of connection:
posting, listening (subscribing), or retrieving.
(See
EvmConnection
(5)
and
EvmConnCreate
(3)
for details.)
EvmEventPost
-- Posts an event to
EVM.
(See
EvmEventPost
(3)
for details.)
EvmConnDestroy
-- Destroys a specified
connection.
(See
EvmConnDestroy
(3)
for details.)
#include <stdio.h> #include <evm/evm.h> void main() { EvmEvent_t event; EvmStatus_t status; EvmConnection_t conn; status = EvmConnCreate(EvmCONNECTION_POST, EvmRESPONSE_WAIT, [1] NULL,NULL,NULL,&conn); if (status != EvmERROR_NONE) { fprintf(stderr,"Failed to create EVM posting connection\n"); exit(1); } EvmEventCreateVa(&event, [2] EvmITEM_NAME,"myco.examples.app.error_detected", EvmITEM_NONE); status = EvmEventPost(conn,event); if (status != EvmERROR_NONE) { fprintf(stderr,"Failed to post event\n"); exit(1); } EvmEventDestroy(event); [3] EvmConnDestroy(conn); }
You can create a connection to the EVM daemon using
EvmConnCreate()
.
The connection remains in place until your program
exits, or until you explicitly destroy it with
EvmConnDestroy()
.
In the following code segment, the first two arguments to
EvmConnCreate()
specify that the connection will be used for posting events, and
that you want the post function to wait for the daemon to acknowledge acceptance
of the event before returning, so that you can take action if the attempt
fails.
(See
EvmConnCreate
(3)
for other response options.) The
NULL
value for the third argument indicates that you are making a connection
to the EVM daemon running on the local system -- you will almost always
specify
NULL
here; see
EvmConnCreate
(3)
for details about
remote connection.
The fourth and fifth values are used for other response
types, and should always be
NULL
for wait-mode response.
The final argument receives the handle to the connection -- you must
supply this for all future calls being made on this connection.
[Return to example]
Create an event and post it. [Return to example]
Clean up by destroying the event and the connection. If you expect to be posting events periodically, it may be better not to destroy the connection, but to reuse it for all future events. This will save you the overhead of re-establishing the connection each time you have something to post. [Return to example]
You will need to use the EVM read and write functions if you are writing a program that performs any of the following operations:
Stores events in a file
Passes events to another process
Analyzes events stored in a file
Receives events from a process other than the EVM daemon
You cannot write events directly using the standard UNIX write functions because the event handle only contains a pointer to the body of the event -- and because the location of the body may change each time the event is modified. Conversely, when you read an event, it is not enough just to read the body; a handle has to be created to allow you to reference the event through the API functions.
Example 14-6 shows you how to write events to a file, to read them from a file into your program, and to validate them.
The example introduces the following functions:
EvmEventWrite
-- Writes an event to
a specified file descriptor.
(See
EvmEventWrite
(3)
for details.)
EvmEventRead
-- Creates a new event
structure and populates it with an event read from a specified file descriptor.
EvmEventDestroy
must be used to free the new event.
(See
EvmEventRead
(3) for details.)
EvmEventValidate
-- Performs a data
integrity check on the event.
This check is intended to validate an event
that has just been received over a connection or retrieved from storage.
(See
EvmEventValidate
(3)
for details.)
Example 14-6: Reading and Writing Events
#include <stdio.h> #include <fcntl.h> #include <evm/evm.h> void main() { EvmEvent_t event_in,event_out; EvmStatus_t status; EvmItemValue_t itemval; int fd; EvmEventCreateVa(&event_out, [1] EvmITEM_NAME,"myco.examples.app.saved_event", EvmITEM_NONE); fd = open("eventlog",O_RDWR | O_CREAT | O_TRUNC, [2] S_IRUSR | S_IWUSR); if (fd < 0) { fprintf(stderr,"Failed to open output log file\n"); exit(1); } status = EvmEventWrite(fd,event_out); if (status != EvmERROR_NONE) { fprintf(stderr,"Failed to write event to log file\n"); exit(1); } lseek(fd,0,SEEK_SET); [3] status = EvmEventRead(fd,&event_in); if (status != EvmERROR_NONE) { fprintf(stderr,"Failed to read event from log file\n"); exit(1); } status = EvmEventValidate(event_in); [4] if (status != EvmERROR_NONE) { fprintf(stderr,"Event read from logfile is invalid"); exit(1); } status = EvmItemGet(event_in,EvmITEM_NAME,&*itemval); [5] if(status == EvmERROR_NONE) { fprintf(stdout,"Event name: %s\n",itemval.NAME); EvmItemRelease(EvmITEM_NAME,itemval); } EvmEventDestroy(event_in); [6] EvmEventDestroy(event_out); }
Create an event containing a name. [Return to example]
Create an output log file and use
EvmEventWrite()
to write the event to it.
You can write the event to any file descriptor,
including a pipe to another process -- but because the event is a binary
data package, take care not to write it to a terminal or printer.
[Return to example]
Read the event back in using
EvmEventRead()
.
Note that you produce a different event this time, and you have to supply
a pointer to the event handle, not the handle itself.
[Return to example]
Because the incoming event has been outside the control of
this process, it is important to verify its integrity.
Use the
EvmEventValidate()
function to do this each time you either read
an event from a file or receive it from any process other than the EVM daemon.
[Return to example]
You can show that the event just read is the same event that was just written out by retrieving and displaying its name. [Return to example]
Free the space used by the event. [Return to example]
A program that subscribes for receiving event notifications must perform the following operations:
Create a listening connection to the EVM daemon
Tell the daemon which events it is interested in by passing a filter string
Monitor event activity on the connection and be prepared to handle each event as it arrives
Example 14-7
waits for events to arrive and displays
each incoming event on
stdout
.
The example introduces the following functions:
EvmConnSubscribe
-- Requests notification
of any posted events that match the supplied filter.
(See
EvmConnSubscribe
(3)
for
details.)
EvmConnWait
-- Blocks until activity
is detected on a specified connection.
When activity is detected, the calling
program can call
ConnDispatch
to handle the activity.
(See
EvmConnWait
(3)
for details.)
EvmConnDispatch
-- Handles any outstanding
I/O on a specified connection by calling the program's callback function as
needed.
(See
EvmConnDispatch
(3)
for details.)
EvmEventFormat
-- Formats an event.
(See
EvmEventFormat
(3)
for details.)
Example 14-7: Subscribing for Event Notification
#include <stdio.h> #include <evm/evm.h> void EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg, EvmCallbackData_t *cbdata); /*==================================================== * Function: main() *====================================================*/ main() { EvmConnection_t conn; EvmStatus_t status; status = EvmConnCreate(EvmCONNECTION_LISTEN, EvmRESPONSE_CALLBACK, NULL,EventCB,NULL,&conn); [1] if (status != EvmERROR_NONE) { fprintf(stderr,"Failed to create EVM listening connection\n"); exit(1); } status = EvmConnSubscribe(conn,NULL,"[name *.evm.msg.user]"); [2] if (status != EvmERROR_NONE) { fprintf(stderr,"Failed to subscribe for event notification\n"); exit(1); } for (;;) [3] { status = EvmConnWait(conn,NULL); if (status == EvmERROR_NONE) { fprintf(stderr,"Connection error\n"); exit(1); } if (EvmConnDispatch(conn) != EvmERROR_NONE) { fprintf(stderr,"Connection dispatch error\n"); exit(1); } } } /*==================================================== * Function: EventCB() *====================================================*/ void EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg, [4] EvmCallbackData_t *cbdata) { char buff[256]; switch (cbdata->reason) { [5] case EvmREASON_EVENT_DELIVERED: EvmEventFormat(buff, sizeof(buff), cbdata->event); fprintf(stdout,"Event: %s\n",buff); EvmEventDestroy(cbdata->event); [6] break; default: [7] break; } }
Use
EvmConnCreate()
to establish a connection
to the EVM daemon, this time specifying a listening connection.
The following
arguments appear in this code segment:
The first two arguments to
EvmConnCreate()
specify that the connection will be used for listening and that, when events
arrive, you want to be notified through a callback function.
The
NULL
third argument indicates that
you are making a connection to the local EVM daemon; see
EvmConnCreate
(3)
for details
on remote connection.
The fourth argument specifies the callback function to be
called when events arrive --
EventCB
is the next function.
The fifth argument is the callback argument -- a value
that will be passed to the callback function each time it is called to service
this connection.
You can use this argument for anything you want, but in this
example it is just set to
NULL
.
The final argument receives the handle to the connection.
The next step is to use the
EvmConnSubscribe()
function to let the EVM daemon know which events you are interested in receiving.
In this case, you will watch for user messages that can be posted with
evmpost
.
These events have the name
sys.unix.evm.msg.user
.
Note that calling
EvmConnSubscribe()
results
in your callback function being invoked with a reason code of
EvmREASON_SUBSCRIBE_COMPLETE
when you call
EvmConnDispatch()
.
[Return to example]
This example does not have anything to do except wait for arriving
events, so it loops forever, using
EvmConnWait()
to watch
for activity on the connection.
Passing
NULL
as the second
argument to
EvmConnWait()
indicates that you do not want
to time out if there is no activity.
EvmConnWait()
will
return each time any activity occurs -- not always because an event has
arrived.
For example, the EVM daemon may have sent some other message or the
process may have been interrupted by a signal.
Each time the function returns
without error, you must call
EvmConnDispatch()
to handle
the activity.
This will usually (but not always) result in
EventCB()
, the callback function, being called.
[Return to example]
The callback function is called by
EvmConnDispatch()
whenever it reads a message from the daemon that needs to be handled
by application code.
This includes incoming events; remember, however, that
you may need to consider other types of messages in your application.
The
following arguments appear in this code segment:
The first argument to the callback function is the connection handle. This is useful to identify the connection in some circumstances, but you may find that you do not have a use for it.
The second argument is the callback argument that you supplied when you established the connection. You may choose to use this value to identify the connection, or (in C++ code) to pass a pointer to an object instance, or you may not use it at all.
The final argument is the callback data, a pointer to a structure
containing information about the callback.
The code must examine the callback
data structure to find out why it was called.
The structure includes a reason
code, a status value, and (if an event is being delivered) a pointer to the
event.
See
EvmCallback
(5)
for details on the structure.
For this example, you are looking for incoming event messages, indicated
by the
EvmREASON_EVENT_DELIVERED
reason code.
As each event
comes in, you format it and display it, and then (because you are responsible
for the space it consumes) destroy it when you are done with it.
[Return to example]
The action taken always depends on the reason for the callback.
In this case, the reason is event delivered, so you use the
EvmEventFormat()
function to format the event for display, and then you print it
to
stdout
.
Note that
EvmEventFormat()
produces a text line that
is formed from the event's format data item (if present), and places the result
in the buffer that you supply.
Formatting does not cause the event itself
to change.
[Return to example]
The delivered event is using heap space, and it is the responsibility of the application to free the space once it has finished with the event. [Return to example]
Because we made a subscription request earlier, the function
will also be invoked with a callback reason of
EvmREASON__SUBSCRIBE_COMPLETE
.
For this example, this and any other reason codes can be ignored.
[Return to example]
If you are writing a program that has responsibilities other than just
listening for events, you may not want to use
EvmEventWait()
to wait for events to arrive.
For example, you may wish to use the
select
system call to wait for I/O activity on multiple file descriptors,
including the EVM connection.
Example 14-8
shows how to handle the EVM connection in
conjunction with input from
stdin
.
The example introduces the following functions:
EvmConnFdGet
-- Returns the file descriptor
(file number) associated with a specified connection.
(See
EvmConnFdGet
(3)
for details.)
EvmConnCheck
-- Checks whether any
I/O activity is outstanding on a specified connection.
(See
EvmConnCheck
(3)
for details.)
Example 14-8: Handling Multiple I/O Sources
#include <stdio.h> #include <sys/time.h> #include <evm/evm.h> void HandleInput(); void EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg, EvmCallbackData_t *cbdata); /*=============================================== * Function: main() *===============================================*/ main() { EvmConnection_t conn; EvmStatus_t status; fd_set read_fds; int conn_fd; EvmBoolean_t io_waiting; status = EvmConnCreate(EvmCONNECTION_LISTEN, EvmRESPONSE_CALLBACK, NULL,EventCB,NULL,&conn); if (status != EvmERROR_NONE) { fprintf(stderr,"Failed to create EVM listening connection\n"); exit(1); } status = EvmConnSubscribe(conn,NULL,"[name sys.unix.evm.msg.user]"); if (status != EvmERROR_NONE) { fprintf(stderr,"Failed to subscribe for event notification\n"); exit(1); } EvmConnFdGet(conn,&conn_fd); [1] for (;;) [2] { FD_ZERO(&read_fds); FD_SET(fileno(stdin),&read_fds); FD_SET(conn_fd,&read_fds); select(FD_SETSIZE,&read_fds,NULL,NULL,NULL); if (FD_ISSET(fileno(stdin),&read_fds)) HandleInput(); status = EvmConnCheck(conn,&io_waiting); [3] if (status != EvmERROR_NONE) { fprintf(stderr,"Connection error\n"); exit(1); } if (io_waiting) { status = EvmConnDispatch(conn); if (status != EvmERROR_NONE) { fprintf(stderr,"Connection dispatch error\n"); exit(1); } } } } /*=============================================== * Function: HandleInput() *===============================================*/ void HandleInput() [4] { char buff[256]; if (feof(stdin)) exit(0); if (fgets(buff,sizeof(buff),stdin) == NULL) exit(0); if (buff[0] == '\n') exit(0); fprintf(stdout,buff); } /*=============================================== * Function: EventCB() *===============================================*/ void EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg, [5] EvmCallbackData_t *cbdata) { char buff[256]; switch (cbdata->reason) { case EvmREASON_EVENT_DELIVERED: EvmEventFormat(buff, sizeof(buff), cbdata->event); fprintf(stdout,"Event: %s\n",buff); EvmEventDestroy(cbdata->event); break; default: break; } }
Use
EvmConnFdGet()
to find the file descriptor
assigned to the connection.
[Return to example]
In this example,
select
is used to wait
for I/O activity because this allows waiting on multiple file descriptors.
When the
select
call returns, check to see which of the
file descriptors has activity, and deal with it appropriately.
[Return to example]
EvmConnCheck()
is used here to determine
whether any activity is outstanding on the EVM connection.
Because the connection's
file descriptor is known,
FD_ISSET()
could have been used
for the same purpose.
[Return to example]
The
HandleInput()
function reads lines of
input from
stdin
and echoes them to
stdout
.
It terminates the program on error or if an empty line is read.
[Return to example]
The event callback function is identical to the one used in the previous example. [Return to example]
Some event subscribers need to monitor events using a variety of criteria, and then react in different ways according to the attributes of the incoming events. For example, the EVM logger reads a set of filter strings from its configuration file and subscribes for all of the events described in the strings. Then, as each event arrives, it has to determine which of the filter strings the event matches in order to write it to the correct set of logs.
One approach is to create multiple connections to the EVM daemon and subscribe on each connection with a separate filter string. However, this is costly in connection overhead and frequently results in the same event being sent across two or more of the connections.
A much better approach is to take all of the filter strings and combine them into a single logical string using the OR logical operator. This combined string is then used to subscribe for all matching events on a single connection. The combined string can then be discarded, but the original set of strings must be retained. It can be used to resubscribe later if the connection has to be re-established or if the filter has to change.
However, when an event arrives, you need to know which of the original filter strings it matches, so that you can decide what to do with it. You can use an EVM filter evaluator to do this. A filter evaluator is an object that can be created, loaded with a filter string, and then passed a series of events to determine which (if any) of the events match the filter. If you maintain a separate evaluator for each of the original filter strings, you can apply each incoming event to each of the evaluators to decide which evaluators match the event.
Example 14-9 demonstrates this technique by using three simple filter strings and by printing a different message according to which of the filters, if any, each incoming event matches.
The example introduces the following functions:
EvmFilterCreate
-- Establishes an
instance of a filter evaluator and returns a handle.
(See
EvmFilterCreate
(3)
and
EvmFilter
(5)
for details.)
EvmFilterSet
-- Passes a filter string
to the filter evaluator to be used in subsequent matches.
(See
EvmFilterSet
(3)
and
EvmFilter
(5)
for details.)
EvmFilterTest
-- Compares a specified
event with the filter string currently associated with the filter evaluator.
If the event matches the filter string,
EvmFilterTest
returns
EvmTRUE
; otherwise, it returns
EvmFALSE
.
(See
EvmFilterTest
(3)
and
EvmFilter
(5)
for details.)
EvmFilterDestroy
-- Destroys a filter
evaluator, freeing up all associated resources.
(See
EvmFilterDestroy
(3)
and
EvmFilter
(5)
for details.)
Example 14-9: Using Filter Evaluators
#include <stdio.h> #include <sys/time.h> #include <evm/evm.h> void EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg, EvmCallbackData_t *cbdata); #define FILTER_1 "[name *.class_1]" [1] #define FILTER_2 "[name *.class_2]" #define FILTER_3 "([name *.class_2] | [name *.class_3]) & [priority >= 300]" /*=============================================== * Function: main() *===============================================*/ main() { EvmConnection_t conn; Evmstatus_t status; int conn_fd; char *filter_string; status = EvmConnCreate(EvmCONNECTION_LISTEN, EvmRESPONSE_CALLBACK, NULL,EventCB,NULL,&conn); if (status != EvmERROR_NONE) { fprintf(stderr,"Failed to create EVM listening connection\n"); exit(1); } filter_string = (char *)malloc(strlen(FILTER_1) + strlen(FILTER_2) + strlen(FILTER_3) + 30); [2] sprintf(filter_string,"(%s) | (%s) | (%s)",FILTER_1,FILTER_2,FILTER_3); status = EvmConnSubscribe(conn,NULL,filter_string); [3] if (status != EvmERROR_NONE) { fprintf(stderr,"Failed to subscribe for event notification\n"); exit(1); } free(filter_string); for (;;) [4] { status = EvmConnWait(conn,NULL); if (status != EvmERROR_NONE) { fprintf(stderr,"Connection error\n"); exit(1); } if (EvmConnDispatch(conn) != EvmERROR_NONE) { fprintf(stderr,"Connection dispatch error\n"); exit(1); } } } /*=============================================== * Function: EventCB() *===============================================*/ void EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg, EvmCallbackData_t *cbdata) { EvmBoolean_t match; static EvmFilter_t f1,f2,f3; [5] static EvmBoolean_t filters_initialized = EvmFALSE; if (! filters_initialized) { if (EvmFilterCreate(&f1) != EvmERROR_NONE) { fprintf(stderr,"Failed to create filter evaluator\n"); exit(1); } if (EvmFilterSet(f1,FILTER_1) != EvmERROR_NONE) { fprintf(stderr,"Failed to set filter evaluator\n"); exit(1); } EvmFilterCreate(&f2); EvmFilterSet(f2,FILTER_2); EvmFilterCreate(&f3); EvmFilterSet(f3,FILTER_3); filters_initialized = EvmTRUE; } switch (cbdata->reason) { [6] case EvmREASON_EVENT_DELIVERED: EvmFilterTest(f1,cbdata->event,&match); if (match) fprintf(stdout,"Filter 1 event received\n"); EvmFilterTest(f2,cbdata->event,&match); if (match) fprintf(stdout,"Filter 2 event received\n"); EvmFilterTest(f3,cbdata->event,&match); if (match) fprintf(stdout,"Filter 3 event received\n"); EvmEventDestroy(cbdata->event); break; default: break; } }
This section of code defines three simple filter strings. The first two filter by name alone, but the third string selects events with either of two names, provided they have a priority of at least 300. [Return to example]
This section of code combines the three filter strings into a single logical expression, surrounding each substring with parentheses and separating them with the logical OR operator. This will cause the daemon to notify you of any event that matches at least one of the substrings. [Return to example]
Use the combined string to subscribe for the events, and then, because the string is no longer useful to you, free its space. If you have to resubscribe for any reason, you can always recombine the substrings. [Return to example]
Once the evaluators have been established, keep them so that you can use them for all events that you receive (that is, make them static entities). Alternatively, you could create a new set of evaluators each time you receive an event, and destroy them as you exit the function. However, keeping them around avoids the repeated setup/teardown overhead. [Return to example]
This section of code creates three filter evaluators using
EvmFilterCreate()
and loads them with the three filter strings.
For
clarity here, only the return status for the first filter is checked, but
in production code it is important to check it in each case.
Note that this
code is executed only the first time we receive an event.
[Return to example]
This section of code tests each incoming event against each of the three filter evaluators established in the previous section, and prints a different message for each evaluator that is matched by the event. It is quite possible that some events will match more than one evaluator; in which case, more than one message is printed. [Return to example]
The EVM naming policy allows an event name to be extended with any number of trailing components, yet still match its base name. This means that you cannot depend on an event having exactly the name you expect it to have because it may have extra trailing components.
Therefore, when you need to compare an event's name against a known name, do not use the usual string comparison functions because they will incorrectly fail to match the name if components have been added. Instead, you should use EVM's name-matching functions. These functions match an event name against the pattern you supply, ignoring any trailing components in the candidate name. They also allow you to include wildcard characters in your name pattern.
Example 14-10 introduces the following function:
EvmEventNameMatch
-- Takes the following
input arguments: an event name string (which may contain wildcard characters)
and an event.
Returns an indication of whether the event matches the name
string.
The following function is related to
EvmEventNameMatch
:
EvmEventNameMatchStr
-- Takes an event
name in a character string, rather than extracting it from an event.
Example 14-10: Matching Event Names
#include <stdio.h> #include <evm/evm.h> /*=============================================== * Function: main() *===============================================*/ main() { EvmStatus_t status; EvmEvent_t event; EvmBoolean_t match; char buff[80]; while (EvmERROR_NONE == EvmEventRead(fileno(stdin),&event)) [1] { EvmEventNameMatch("*.msg",event,&match); if (match) { EvmEventFormat(buff,sizeof(buff),event); fprintf(stdout,"%s\n",buff); } } }
This section of code reads events from
stdin
and displays only those events that match the wildcard string
*.msg
.
This match will work even though events of this type usually have
the name
sys.unix.evm.msg.user
or
sys.unix.evm.msg.admin
.
[Return to example]
When the EVM daemon sends an event to a subscribing client, it does so with a connection that depends upon fixed-size memory buffers. If the client does not deal with its incoming events within a reasonable time and there is a heavy event load, the buffer may fill and the daemon will be unable to send further events. The default sizes of a connection's send and receive buffers are both 8 KB.
Because the EVM daemon is a critical resource for many system components and applications, it cannot block while waiting for a client to clear its connection buffer. Consequently, if it fails to write to a connection because the buffer is full, the daemon marks the connection as blocked and continues with its other activities. When the client eventually reads its input and frees space in the buffer, the EVM daemon completes the failed write and the client will receive the event. However, if in the meantime any other events arrive that should be sent to the blocked subscriber, the daemon does not attempt to send them; instead, it counts how many have been missed and reports the number to the subscriber once the connection becomes unblocked. The subscriber must take any appropriate action, but there is no way for it to know which events have been missed.
The likelihood of your application missing incoming events depends on several factors:
The speed and load of the system
The set of subscribed-for events
The frequency with which events are being posted
The size of the connection's input buffer
The sizes of the events being posted
To minimize the risk of missing incoming events, design your application to deal with incoming events as fast as possible and to take appropriate action when it is notified that it has missed events.
Example 14-11
subscribes for all events and displays
each event on
stdout
.
If any events are missed, a special
message is displayed.
The risk of missing events is reduced by increasing
the size of the connection's input buffer above the default 8 KB.
The example introduces the following function:
EvmConnControl
-- Provides a means
of controlling and inquiring about certain features of the connection, including
the send and receive buffer sizes.
Example 14-11: Dealing with Missed Events
#include <stdio.h> #include <evm/evm.h> #define INPUT_BUFF_SIZE (32 * 1024) void EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg, EvmCallbackData_t *cbdata); /*=============================================== * Function: main() *===============================================*/ main() { EvmConnection_t conn; EvmStatus_t status; int conn_fd; status = EvmConnCreate(EvmCONNECTION_LISTEN, EvmRESPONSE_CALLBACK, NULL,EventCB,NULL,&conn); if (status != EvmERROR_NONE) { fprintf(stderr,"Failed to create EVM listening connection\n"); exit(1); } status = EvmConnControl(conn,EVM_CONN_RCV_SZ_SET,(void *)INPUT_BUFF_SIZE); [1] if (status != EvmERROR_NONE) { fprintf(stderr,"Failed to increase EVM connection buffer " "size to %d bytes\n", INPUT_BUFF_SIZE); exit(1); } status = EvmConnSubscribe(conn,NULL,"[name *]"); if (status != EvmERROR_NONE) { fprintf(stderr,"Failed to subscribe for event notification\n"); [2] exit(1); } for (;;) { status = EvmConnWait(conn,NULL); if (status != EvmERROR_NONE) { fprintf(stderr,"Connection error\n"); exit(1); } if (EvmConnDispatch(conn) != EvmERROR_NONE) { fprintf(stderr,"Connection dispatch error\n"); exit(1); } } } /*=============================================== * Function: EventCB() *===============================================*/ void EventCB(EvmConnection_t conn, EvmCallbackArg_t cbarg, EvmCallbackData_t *cbdata) { char buff[256]; switch (cbdata->reason) { case EvmREASON_EVENT_DELIVERED: EvmEventFormat(buff, sizeof(buff), cbdata->event); fprintf(stdout,"Event: %s\n",buff); EvmEventDestroy(cbdata->event); sleep(1); [3] break; case EvmREASON_EVENTS_MISSED: [4] fprintf(stdout,"*** Missed %d incoming events\n", cbdata->extension.eventMissedData.missedCount); break; default: break; } }
Once the connection has been created, the received-message
(input) buffer size is increased to reduce the chance of missing incoming
events.
The
EvmConnControl()
function supports several
different types of request, including setting the send buffer size and retrieving
the current buffer sizes.
[Return to example]
This example subscribes for notification of all events. [Return to example]
To demonstrate missing events, sleep for one second after each event is received. This simulates a heavy processing load and ensures that the input connection buffer will be filled if the event load is heavy. [Return to example]
The callback function will be invoked with reason code
EvmREASON_EVENTS_MISSED
if the daemon has been unable to send one
or more missed events.
The callback data member
extension.eventMissedData.missedCount
contains a count of the number of missed events.
[Return to example]
An EVM event channel is defined as any source of event information. An event channel may be an active channel, in which case it posts its own event information to EVM as soon as the event occurs, or it may be a passive channel, meaning that the information may accumulate within the channel but EVM has to take the action to look for it.
An event channel does not have to be a formal event notification mechanism. It can be anything that is capable of storing information or showing a status or a change of state. For example:
If an application or system components writes state information to its own log file, each line in the log file can be thought of as an event. The log file can be checked periodically for new lines, and an event can be posted for each new line found. The log file itself also serves as a place from which to retrieve historical events.
Some applications and system components provide a means of querying status. In these cases, the status can be monitored periodically, and an event can be posted whenever a significant change is detected. Because this type of event channel typically does not include a means of storing event information, EVM's own logging mechanism can be used to log the events.
If a central part of an application handles all event information for the application, it may be feasible to modify the handling code so that it also forwards event information to EVM. This is an example of an active event channel.
The process of taking an existing event channel and making it accessible through EVM is termed encapsulation.
EVM event channels are configured through the channel configuration file. This file is read by the EVM channel manager when EVM is started, and is also used by command-line utilities when channel information is needed. When you change this file, you must make the channel manager aware of the change by entering the following command:
evmreload -c
See
evmchannel.conf
(4)
for the syntax of the channel configuration file.
You can encapsulate an event channel by providing a set of executable programs
to handle the various channel functions.
The functions are:
The get function -- retrieves historical events from
the channel when
evmget
is run.
The details function -- provides a detailed display of
an event stream when
evmshow
is run with the
-d
option.
The explain function -- provides explanatory text for
an event stream when
evmshow
is run with the
-x
option.
The monitor function -- executed periodically by the EVM channel manager to check the channel status and post events if necessary.
The cleanup function -- executed daily by the EVM channel manager to perform any cleanup actions necessary for the channel.
Each of these functions is optional, and is defined for the channel by including the appropriate entry in a channel definition in the channel configuration file. Channel functions can be any kind of executable file and must operate as described in the following sections.
Channel functions that use temporary files should be sure to clean up before exiting, and they must also be able to clean up if they are interrupted.
You must also add event templates for any events which may be posted
or retrieved through a new event channel.
See
Section 14.6.3
for information about adding event templates.
You must also make sure that
the events are given the correct posting and access privileges, by modifying
the EVM authorization file as necessary.
See the
System Administration
manual for
details of how to control access to events.
14.8.1 The Get Function
The channel get function is executed by the EVM
get_server
, which is executed by the EVM daemon to handle event retrieval
requests made through
evmget
.
This function is always executed
as root and must take appropriate security precautions.
The function must support the following invocation syntax:
function-name
[-f
filter-string]
If desired, other arguments may be passed to the function by including them in the function's line in the channel configuration file.
When executed, the get function should retrieve events from the channel's
log files, convert them to EVM event format, and write the EVM events to its
stdout
stream.
If a
filter-string
is
supplied, only events that match the filter can be written to
stdout
.
Error messages can be written to
stderr
and
will be passed back to
evmget
for output to its
stderr
stream, so be sure that they are clearly identified as originating
in this function.
Nothing other than EVM events can be written to
stdout
.
The form of a get script depends very much on the form in which the native events are stored. In general, the steps will be:
Use standard UNIX tools such as
awk
and
sed
to select the event lines, removing blank lines and comments,
and reformat them as necessary for the next step.
This should be a reasonably
simple matter if the events are single lines of text, with a constant format
in each line, and include items such as a timestamp, host name, and message
in the same position in every line.
Convert the lines into EVM events.
You may be able to do this
by using UNIX tools to format the lines into a form suitable for input to
evmpost
, using the
-r
option to produce EVM events
on
stdout
instead of posting them.
Alternatively, for a
faster conversion, you can use the EVM channel utility
/usr/share/evm/channels/bin/text2evm
to do the conversion.
This tool currently requires input of the
form:
event-name yyyy/mm/dd hh:mm:ss host user message
If a filter string was supplied, pass the output through
evmshow
, using the
-f
and
-r
options, to restrict the output to that requested in the filter.
Finally, if you want the retrieved events to include data
items contained in the events' templates, you can pipe the output through
the EVM channel utility,
/usr/share/evm/channels/bin/merge_template
.
If your channel's log files are difficult to convert to EVM format -- for example, because each entry is made up of multiple unstructured lines of text, which cannot be parsed easily -- it may be better not to supply a get function, but instead to allow the events to be logged by the EVM logger as they are posted. This consumes more storage space, as the events would be stored in two places, but it may significantly improve retrieval time and programming effort.
If you supply your own get function for the channel, be sure to change
the filter strings in the EVM logger's configuration file so that your events
are not duplicated in the EVM log.
See the
System Administration
manual for information
on how to change the logger's configuration file.
14.8.2 The Details Function
The details function is executed by
evmshow
when
it is invoked with the
-d
option.
Although it is currently
executed with the privileges of the user executing
evmshow
,
this is likely to change in a future release, so it is important that it take
appropriate security precautions.
The function must support the following invocation syntax:
function-name [arguments]
If arguments are specified in the details function line in the channel
configuration file, they are passed directly to the function when it is executed.
The details function must accept a stream of EVM events through
stdin
, and display on
stdout
a stream of text
that describes the contents of each event.
Various forms of
evmshow
will be useful in producing the output, but be careful not to use
the
-d
option because that would result in a recursive
loop.
Unless redirected, messages written to
stderr
will
appear on
evmshow
's
stderr
stream, so
if needed they should be clearly identified as being written by this function.
If your channel does not require special formatting for detailed display,
omit this function from the channel configuration.
By default, the
evmlog
channel function,
/usr/share/evm/channels/evmlog/evmlog_details
, will be used to display the events belonging to any channel that
does not supply its own details function.
If you do need to develop your own
function, you can use this shell script as a model.
14.8.3 The Explain Function
The explain function is executed by
evmshow
when
it is invoked with the
-x
option.
Although it is currently
executed with the privileges of the user executing
evmshow
,
this is likely to change in a future release, so it is important that it take
appropriate security precautions.
The function must support the following
invocation syntax:
function-name event-name [reference]
The explain function is invoked with the name of the event requiring explanation and an optional reference value. If supplied, the reference is the contents of the event's reference data item. If no reference is available, a hyphen will usually be passed for this argument, but the function should also allow the argument to be omitted.
The explain function should use its arguments to produce a formatted
explanation of the event, and write it as lines of text to
stdout
.
If no explanation can be found, an appropriate message should
be written to
stdout
in place of the explanation.
Unless
redirected, messages written to
stderr
will appear on
evmshow
's
stderr
stream, so, if they are needed,
they should be clearly identified as being written by this function.
Your explain function can invoke the
evmlog
explain
function,
/usr/share/evm/channels/evmlog/evmlog_explain
,
provided that:
The events in your channel contain a reference data item of
the form:
cat:
catalog-name[:
set_number], where
catalog-name
is the name of an I18N catalog file containing the explanations
for your channel's events and the optional
set_number
is the number of the message set containing the explanation.
Each explanation in the catalog begins with the name of an
event enclosed in braces, for example,
{myco.myprod.myapp.startup}
.
The message catalog must be located according to normal I18N rules.
To minimize search time, group the explanations into sets and provide the
set numbers in the reference data items of the events.
See
mkcatdefs
(1)
and
gencat
(1)
for the procedures to generate a catalog file.
14.8.4 The Monitor Function
The monitor function is executed by the EVM channel manager.
It is executed
with an
init
argument when the channel manager is started
and each time the channel manager is reconfigured with
evmreload
, and periodically thereafter without the
init
argument.
The execution period is controlled with the
mon_period
channel value.
This function is always executed as root and
must take appropriate security precautions.
The function must support the following invocation syntax:
function-name
[init
]
The presence or absence of the
init
argument can
be used to decide whether the function needs to initialize any work files
that it has to maintain.
If desired, you can pass additional arguments to
the function by including them on the command line in the channel configuration
file, but note that the
init
argument is always passed
as the last argument.
There are no restrictions on the actions that can be taken by the monitor
function, although its job should generally be to check status and post events
if it detects a change of state.
The function is invoked with no
stdout
or
stderr
, so if it is necessary to report
error conditions, they should generally be posted as EVM events, taking care
not to cause an event storm by unnecessarily reporting the same condition
each time the function is invoked.
The following example monitor script initializes itself by counting
the number of lines in a log file and saving the count in a state file.
On
subsequent invocations, it compares the number of lines in the file with the
previous number, extracts each new line with the UNIX
tail
command, and posts it as an EVM event with
evmpost
.
#! /bin/sh INIT=$1 STATE=/tmp/mylog.state LOG=/tmp/mylog EVENT_NAME=myco.admin.mylog.line_added # No log? Create one! if [ ! -f $LOG ] then touch $LOG fi # If we're initializing then save the current logfile # state and exit: if [ "$INIT" != "" ] then # Count the lines in the demolog, and save the count # in the state file: wc -l $LOG | awk '{print $1}' > $STATE exit fi # Find out how many lines there were in the file last time # we checked: OLDCOUNT=`cat $STATE` # How many now? NEWCOUNT=`wc -l $LOG | awk '{print $1}'` if [ $NEWCOUNT > $OLDCOUNT ] then # Save the new line count to the state file: echo $NEWCOUNT > $STATE # What's the difference between the old and new counts? diff=`expr $NEWCOUNT - $OLDCOUNT | awk '{print $1}'` # Post an event for each new line: tail -$diff $LOG | while read LINE do echo 'event { name '${EVENT_NAME} \ ' var {name msg type STRING value "'$LINE'"} }' | evmpost done fi
The cleanup function is executed daily by the EVM channel manager, at the time specified in the channel configuration file, to perform housekeeping actions such as archiving and deleting the channel's log files. This function is always executed as root and must take appropriate security precautions.
The cleanup function is specified as a command line that is executed
exactly as specified, so arguments may be passed to the function through the
command line if desired.
The function is free to take whatever action is appropriate.
It is executed with no
stdout
or
stderr
streams, so any desired status messages should generally be issued in the
form of EVM events by using
evmpost
, instead of being written
to
stderr
.
Nothing should be written to
stdout
.
Write the function so that it has the same effect regardless of the
time of day at which it is run; for example, it might use the
-mtime
option of the
find
command to identify log files
to be archived.
14.8.6 Channel Security
In most cases, channel functions are executed by processes that are children of the EVM daemon, and, as a result, they will be run with full root privileges. Because of this, you must protect your system's integrity by ensuring that:
Functions are placed in a directory that has restricted write privileges.
Functions themselves have restricted write and execute privileges.
Functions do not call other programs that have inappropriate privileges.