Testing the Code
Now let's test the code and play with some commands. Before testing the code, remember to enable the plug-in LOG level in the LDAP server, from the administrator's console or from the command line as follows:
CODE EXAMPLE 13 Enabling the Plug-In Log Level
<sunone slapd-sunblueprints>~$ ldapmodify -p 10389 -D "cn=Directory Manager" -w manager0 dn: cn=config changetype: modify replace: nsslapd-infolog-area nsslapd-infolog-area: 65536 ^D
Let's try a UNIX command. The language is defined to accept UNIX escape commands such as the following:
! pwd \; ! man cat \;
Many UNIX programs support this useful feature (for example, mail, vi).
The following example executes two commands. Notice that the ! escape symbol is expected at beginning of the line and a terminating '\;' sequence is required. Refer to the triggers.l file and the LEXer specification file for the lexical details of the language.
CODE EXAMPLE 14 Executing Commands
triggers>> !uname -a \; ! man pwd \; . Calling extended operation 4.3.2.1 ------------- Your input ---------------- !uname -a \; ! man pwd \; . ----------------------------------------- ------------- Server output ---------------- Statement #0 Error code: 0 Error msg: Unix shell command successfully executed Linux alfa 2.4.18-3 #1 Thu Apr 18 07:37:53 EDT 2002 i686 unknown Statement #1 Error code: 0 Error msg: Unix shell command successfully executed PWD(1) PWD(1) NOME pwd - stampa il nome della directory di lavoro corrente SINTASSI pwd pwd {--help,--version} DESCRIZIONE Questa documentazione non ? mantenuta da lungo tempo e potrebbe essere inaccurata o incompleta. La documentazione in Texinfo ? ora la fonte autorevole. Questa pagina di manuale documenta la versione GNU di pwd. pwd stampa il nome della directory corrente risolvendolo completamente. Cio?, tutte le componenti del nome stam- pato saranno nomi di directory reali -- nessuna sar? un link simbolico. Si noti che molte shell Unix forniscono un proprio comando pwd interno con funzionalit? simili cosicch? il semplice, interattivo comando pwd di solito eseguito sar? quello della shell e non questo. OPZIONI --help Mostra nello standard output un messaggio d'aiuto ed esce con successo. --version Mostra nello standard output informazioni sulla versione ed esce con successo. FSF GNU Shell Utilities PWD(1) -------------------------------------------- Triggers Client Insert your commands, a line with '.' terminates input
We executed the commands uname -a and man pwd. Both are executed on the server, where Sun Java System Directory Server and the extended operation run. You could use this feature for a minimal remote administration if you like, but our goal was just to show an advanced use of the extended operation. Consider also that this kind of feature could introduce serious security concerns, especially if the remote server runs with root account privileges.
Now let's review the log error file to determine what happened.
CODE EXAMPLE 15 Reviewing the Log File
[20/Oct/2003:01:47:58 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_service_fn - conn=2 op=1 msgId=1 - Entering pb=0x8354d60 [20/Oct/2003:01:47:58 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_service_fn - conn=2 op=1 msgId=1 - Request with OID: 4.3.2.1 Value from client: !uname -a \; ! man pwd \; . [20/Oct/2003:01:47:58 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_parse_client_req - conn=2 op=1 msgId=1 - Entering with params(0x8354d60,0x8118348,0x42b796fc) [20/Oct/2003:01:47:58 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_parse_client_req - conn=2 op=1 msgId=1 - Creating the temporary file for parsing [20/Oct/2003:01:47:58 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_parse_client_req - conn=2 op=1 msgId=1 - Opening the temporary file for parsing [20/Oct/2003:01:47:58 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_parse_client_req - conn=2 op=1 msgId=1 - <<!uname -a \; ! man pwd \; .>> [20/Oct/2003:01:47:58 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_parse_client_req - conn=2 op=1 msgId=1 - Now parsing... [..cut...] [20/Oct/2003:01:47:58 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_apply_statements: - conn=2 op=1 msgId=1 - trg_apply_statements(0x8354d60,0x42b796fc, 0x43d1191c) [...cut..] [20/Oct/2003:01:47:58 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_execute_unixcmd - conn=2 op=1 msgId=1 - Entering (index=0) [20/Oct/2003:01:47:58 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_execute_unixcmd - conn=2 op=1 msgId=1 - Returning [20/Oct/2003:01:47:58 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_execute_unixcmd - conn=2 op=1 msgId=1 - Entering (index=1) [20/Oct/2003:01:47:59 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_execute_unixcmd - conn=2 op=1 msgId=1 - Returning [20/Oct/2003:01:47:59 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_apply_statements: - conn=2 op=1 msgId=1 - Returning 0 [20/Oct/2003:01:47:59 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_service_fn - conn=2 op=1 msgId=1 - Client statements executed [20/Oct/2003:01:47:59 +0200] - INFORMATION - (PID=3018,ThID=13325) trg_service_fn - conn=2 op=1 msgId=1 - OID sent to client: Value sent to client: Statement #0 Error code: 0 Error msg: Unix shell command successfully executed Linux alfa 2.4.18-3 #1 Thu Apr 18 07:37:53 EDT 2002 i686 unknown Statement #1 Error code: 0 Error msg: Unix shell command successfully executed PWD(1) PWD(1) ...
The code traces heavily, and we can see the full stack of calls:
trg_service_fn accepts the extended operation invocation and calls
trg_parse_client_req to parse the client command list, then calls
trg_apply_statements, which in turn, for every user command, issues a call to an execution routine, in this case
trg_execute_unixcmd, which issues popen library call to execute the UNIX command and returns the output to
trg_service_fn, which collects all the output from the execution functions in the extended operation response and
trg_service_fn returns the data to the client
This patterns repeats for any command issued by a user at the client application prompt; what changes is the actual trg_execute_<command> called.
Now, let's create our first trigger.
CODE EXAMPLE 16 Creating a Trigger
TRIGGERS>> create or replace trigger SunBlueprintsBigTrigger on 'ou=testadd,o=sunblueprints' before ldap_add action ignore; . ------------- Server output ---------------- Statement #0 Error code: 0 Error msg: Trigger SunBlueprintsBigTrigger successfully added -------------------------------------------- TRIGGERS>> list all triggers; . ------------- Server output ---------------- Statement #0 Error code: 0 Error msg: 1 triggers found SunBlueprintsBigTrigger --------------------------------------------
The example defines a trigger called SunBlueprintsBigTrigger on the directory entry o=testadd, ou=SunBlueprints to be triggered before the entry is added to the directory information tree (DIT). As the action, we specify IGNORE, which means do nothing or skip the operation.
The list all triggers instruction shows you all the available triggers under ou=triggers,o=sunblueprints, which is where we store them. As a proof of this, perform the following LDAP search:
CODE EXAMPLE 17 Performing an LDAP Search
<sunone slapd-sunblueprints>~$ ldapsearch -p 10389 -D "cd=Trigger Manager" -w manager0 -b "ou=triggers,o=sunblueprints" objectclass=trigger" dn: cn=SunBlueprintsBigTrigger,ou=triggers,o=sunblueprints objectClass: trigger objectClass: top cn: SunBlueprintsBigTrigger on: ou=testadd,o=sunblueprints before: 1 enabled: true explain: create or replace trigger SunBlueprintsBigTrigger on ou=testadd,ou=sun blueprints add action ignore action: 2 actiondn:
The search reports exactly one entry, our trigger. Note the explanation attribute, which stores the whole create instruction, as typed by the administrator. Now create the entry, and see what happens.
CODE EXAMPLE 18 Creating an Entry
<sunone slapd-sunblueprints>~$ ldapmodify -p 10389 -D "cd=Trigger Manager" -w manager0 dn: ou=testadd, o=sunblueprints changetype: add objectclass: organizationalUnit ou: testadd description: Ciao mondo! (how Italians say Hello world!) adding new entry ou=testadd, o=sunblueprints ldap_add: Operations error ldap_add: additional info: Trigger SunBlueprintsBigTrigger forbids the creation of entry ou=testadd,o=sunblueprints
It works; we receive the expected message because of the IGNORE action triggered by SunBlueprintsBigTrigger. Reviewing the log file shows the following:
CODE EXAMPLE 19 Verifying the Log File
[21/Oct/2003:02:05:57 +0200] - INFORMATION - (PID=4017,ThID=25625) triggers_pre_add_fn - conn=2 op=1 msgId=30 - Entering [21/Oct/2003:02:05:57 +0200] - INFORMATION - (PID=4017,ThID=25625) triggers_pre_add_fn - conn=2 op=1 msgId=30 - Looking for triggers... [21/Oct/2003:02:05:57 +0200] - INFORMATION - (PID=4017,ThID=25625) find_trigger - conn=2 op=1 msgId=30 - Entering(0x8352190, 0x47312008, (&(&(on=ou=testadd,o=sunblueprints)(before=1))(enabled=true))) [21/Oct/2003:02:05:57 +0200] - INFORMATION - (PID=4017,ThID=25625) find_trigger - conn=2 op=1 msgId=30 - Getting results [21/Oct/2003:02:05:57 +0200] - INFORMATION - (PID=4017,ThID=25625) find_trigger - conn=2 op=1 msgId=30 - Returning [21/Oct/2003:02:05:57 +0200] - INFORMATION - (PID=4017,ThID=25625) triggers_pre_add_fn - conn=2 op=1 msgId=30 - Found triggers ou=testadd,o=sunblueprints ...
The routine triggers_pre_add_fn is the add pre-operation function of our pre-operation plug-in. It calls find_trigger to search for triggers. When it finds one, it executes. As a counter proof, now let's delete the trigger, and re-add the test entry:
CODE EXAMPLE 20 Deleting the Trigger and Reading the Test Entry
TRIGGERS>> delete trigger SunBlueprintsBigTrigger; . ------------- Server output ---------------- Statement #0 Error code: 0 Error msg: Trigger SunBlueprintsBigTrigger successfully deleted -------------------------------------------- TRIGGERS>> list all triggers; . ------------- Server output ---------------- Statement #0 Error code: 0 Error msg: 1 triggers found trgme --------------------------------------------
Try to add the entry again:
CODE EXAMPLE 21 Adding a Duplicate Entry
<sunone slapd-sunblueprints>~$ ldapmodify -p 10389 -D "cn=Directory Manager" -w manager0 dn: ou=testadd, o=sunblueprints changetype: add objectclass: organizationalUnit ou: testadd description: Ciao mondo! (how Italians say for Hello world!) adding new entry ou=testadd, o=sunblueprints
No problem now, because we deleted the trigger on this entry. Our language provides a construct to temporarily disable a trigger instead of removing it.
To disable a trigger, use the following as an example:
CODE EXAMPLE 22 Disabling a Trigger
TRIGGERS>> disable trigger SunBlueprintsBigTrigger; . ------------- Server output ---------------- Statement #0 Error code: 0 Error msg: Trigger SunBlueprintsBigTrigger successfully disabled --------------------------------------------
If you repeat the LDAP add of the ou=testadd test entry, it will work. It works because the triggers_pre_add_fn function that is called before the internal add calls the find_trigger with a search filter such as the following:
(&(&(on=ou=testadd,o=sunblueprints)(before=0))(enabled=true)))
This filter searches expressly for only enabled triggers.
The LDAP triggers language has many other possibilities. Our code example implements only some of them. We invite you to discover the other features and functions.