14    Posting and Receiving EVM Events

This chapter describes the user-level programming interface to the Tru64 UNIX Event Manager (EVM). The principal issues addressed are as follows:

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:

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:

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:

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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. EVM passes the merged event to all processes that have subscribed to receive the event.

  6. 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:

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:

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:

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:

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 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:

  1. Decide on a family name for a set of related events. (See Section 14.5.1.1 for details.)

  2. 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.)

  3. 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.)

  4. 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.)

  5. 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.)

  6. 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:

Do not post events for the following types of occurrences:

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:

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:

The template should generally include:

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:

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:

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:

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:

14.7.4    Signal Handling

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:

  1. 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()

  2. 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:

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:

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]
 }

  1. 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]

  2. 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]

  3. 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]

  4. When you have finished with the event, free the storage space used by the event. [Return to example]

14.7.10.2    Using Variable-Length Argument Lists

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:

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]
 }

  1. 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]

  2. 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]

  3. When you have finished with the event, free the storage space used by the event. [Return to example]

14.7.10.3    Adding and Retrieving Variables

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:

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]
 }

  1. 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]

  2. 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]

  3. 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]

  4. When you have finished with the event, free the storage space used by the event. [Return to example]

14.7.10.4    Posting Events

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:

Example 14-5:  Posting Events

 #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);
 }

  1. 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]

  2. Create an event and post it. [Return to example]

  3. 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]

14.7.10.5    Reading and Writing Events

You will need to use the EVM read and write functions if you are writing a program that performs any of the following operations:

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:

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);
 }

  1. Create an event containing a name. [Return to example]

  2. 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]

  3. 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]

  4. 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]

  5. 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]

  6. Free the space used by the event. [Return to example]

14.7.10.6    Subscribing for Event Notification

A program that subscribes for receiving event notifications must perform the following operations:

Example 14-7 waits for events to arrive and displays each incoming event on stdout.

The example introduces the following functions:

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;
     }
 }

  1. Use EvmConnCreate() to establish a connection to the EVM daemon, this time specifying a listening connection. The following arguments appear in this code segment:

    [Return to example]

  2. 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]

  3. 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]

  4. 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:

    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]

  5. 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]

  6. 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]

  7. 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]

14.7.10.7    Handling Multiple I/O Sources

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:

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;
      }
  }

  1. Use EvmConnFdGet() to find the file descriptor assigned to the connection. [Return to example]

  2. 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]

  3. 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]

  4. 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]

  5. The event callback function is identical to the one used in the previous example. [Return to example]

14.7.10.8    Using Filter Evaluators

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:

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;
     }
 }

  1. 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]

  2. 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]

  3. 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]

  4. 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]

  5. 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]

  6. 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]

14.7.10.9    Matching Event Names

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:

The following function is related to EvmEventNameMatch:

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);
         }
     }
 
 }

  1. 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]

14.7.10.10    Dealing with Missed Events

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:

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:

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;
      }
 
 }

  1. 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]

  2. This example subscribes for notification of all events. [Return to example]

  3. 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]

  4. 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]

14.8    Adding an Event Channel to EVM

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:

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:

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:

  1. 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.

  2. 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

  3. 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.

  4. 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 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

14.8.5    The Cleanup Function

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: