6.1. Structure of Listener Modules#

Listener modules can be implemented using the High-level Listener modules API or the Low-level Listener module.

New in version 4.2: New implementations should be based on the newer high-level API, which is available since UCS 4.2 erratum 311.

Each listener module must declare several string constants. They are required by the Univention Directory Listener to handle each module.

your_module.name: str#


This name is used to uniquely identify the module. It defaults to the filename containing this listener module without the .py suffix. The name is used to keep track of the module state in /var/lib/univention-directory-listener/handlers/.

Return type:


For description, see name.

your_module.description: str#


A short description. It is currently unused and displayed nowhere.

Return type:


For description, see description.

your_module.filter: str#


Defines a LDAP filter which is used to match the objects the listener is interested in. This filter is similar to the LDAP search filter as defined in RFC 2254, but more restricted:

  • it is case sensitive

  • it only supports equal matches

Return type:


For description, see filter.

your_module.ldap_filter: str#

(high-level API)

For description, see filter.

your_module.attributes: List[str]#


A Python list of LDAP attribute names which further narrows down the condition under which the listener module gets called. By default the module is called on all attribute changes of objects matching the filter. If the list is specified, the module is only invoked when at least one of the listed attributes is changed.

Return type:


For description, see attributes.

your_module.modrdn: str#

(low-level API, optional)

Setting this variable to the string 1 changes the signature of the function handler(). It receives an additional forth argument, which specifies the LDAP operation triggering the change.

your_module.handle_every_delete: bool#

(low-level API, optional)

The Listener uses its cache to keep track of objects, especially their previous values and which modules handles which objects. The Univention Configuration Registry Variable listener/cache/filter can be used to prevent certain objects from being stored in the cache. But then the Listener no longer knows which module must be called when such an object is deleted. Setting this variable to True will make the Listener call the function handler() of this module whenever any object is deleted. The function then must use other means to determine itself if the deleted object is of the appropriate type as old will be empty dict.

your_module.priority: float#


This variable can be used to explicitly overwrite the default order in which the modules are executed. By default modules are executed in random order. replication.py defaults to 0.0 as it must be executed first, all other modules default to 50.0.

Return type:


For description, see priority.

6.1.1. Handle LDAP objects#

For handling changes to matching LDAP objects a handler must be implemented. This function is called for different events:

  • when the object is first created.

  • when attributes of an existing object are changed.

  • when the object is moved to a different location within the LDAP tree.

  • when the object is finally removed.

  • when a LDAP schema change happens.

The low-level API requires writing a single function handler() to handle all those cases. The high-level API already splits this into separate methods create(), modify() and remove(), which are easier to overwrite.

6.1.2. Initialize and clean#

Each module gets initialized once by calling its function initialize(). This state of each module is tracked in a file below /var/lib/univention-directory-listener/handlers/.

Return type:



The function initialize() is called once when the Univention Directory Listener loads the module for the first time. This is recorded persistently in the file /var/lib/univention-directory-listener/name, where name equals the value from the header.

If for whatever reason the listener module should be reset and re-run for all matching objects, the state can be reset by running the following command:

$ univention-directory-listener-ctrl resync $name

In that case the function initialize() will be called again.

Return type:



The function clean() is only called when the Univention Directory Listener is initialized for the first time or is forced to re-initialize from scratch using the option -g, -i, or -P. The function should purge all previously generated files and return the module into a clean state.

Afterwards the module will be re-initialized by calling the function initialize().

6.1.3. Suspend and resume#

For efficiency reasons the API provides two functions, which resumes and suspends modules when no transactions are processed for 15 seconds. All modules start in the state suspended. Before a suspended modules is called to handle a change, the function prerun() is called for that module. If no transactions happen within a time span of 15 seconds the Listener suspends all active modules by calling the function postrun(). This mechanism is most often used to batch changes by collecting multiple changes and applying them only on suspend.

Return type:



For optimization the Univention Directory Listener does not keep open an LDAP connection all time. Instead the connection is opened once at the beginning of a change and closed only if no new change arrives within 15 seconds. The opening is signaled by the invocation of the function prerun() and the closing by postrun().

The function postrun() is most often used to restart services, as restarting a service takes some time and makes the service unavailable during that time. It’s best practice to use the handler() only to process the stream of changes, set UCR variables or generate new configuration files. Restarting associated services should be delayed to the postrun() function.



For description, see prerun().


The function postrun() is only called, when no change happens for 15 seconds. This is not on a per-module basis, but globally. In an ever changing system, where the stream of changes never pauses for 15 seconds, the functions may never be called!