Alertra Script Language 1.9

2. Tutorial

This tutorial covers some general topics on writing scripts using the Alertra Scripting Language. For more detail discussion of specific topics, please see the Articles section of the Alertra web site under the heading Scripting: Unlock the Power of Alertra

Note: Some of the sample code blocks have been reformatted to fit comfortably within your web browser. Lines that end with the underline character, "_" continue the same statement on the next line. When writing your own ASL scripts, you must include the entire statement on the same line. There is no line-continuation character in ASL.

2.1. Check a Site

The simplest script to write is one that checks an Internet resource and accepts the default actions when the script fails:




# Check the following URL and 
# take the default action if it fails

dns "www.alertra.com"

tcp $HTTP_PORT

http get,redir "/"

	

The dns call looks up the given host name and stores the IP address for use by the other commands in the script. The tcp call makes a socket connection to port 80 (HTTP_PORT is a predefined constant representing the default connection port for HTTP servers) of the server. The http command actually retrieves the default page from the web server at the selected host. If there is a problem performing any of these tasks, an error is generated and notifications are generated based on the schedule for the script.

We can get a little fancier and check to see if the page has the size we expect and a required key phrase:




# Check a site, make sure the page is the correct

# size and has the given key phrase.

dns "www.alertra.com"

tcp $HTTP_PORT



http get "/login.asp?userid=jsmith&password=pocahontas"

if not "Sample Site Corp" in $CONTENT then begin
	error "Returned page not correct"

end

if not $CONTENT_LENGTH > 1024 then begin
	error "Page not correct size"

end



2.2. HTTP Parameters

We've already seen how you can pass parameters in the URL used to retrieve an HTTP document. Those used the GET method, but you can also use the POST method:




# Pass some parameters to an HTTP form

dns "www.sample.com"

tcp $HTTP_PORT



form userid = "jsmith"

form password = "pocahontas"

http post "/login.asp"



In case you're wondering, the socket connection created by the tcp command doesn't need to be explicitly closed. Any of the other protocol commands (except send and recv) will automatically close the socket.

2.3. Error Handling

If you want, you can change the default error handling used by ASL. Normally, any problem with a command aborts the script immediately and an error notification is sent to all contacts scheduled to be notified for this script.

You can change this by creating your own error handler. The on error command allows you to either install your own handler:




on error goto error_handler



Or you can just ignore errors:




on error goto next



Here is an example of installing and using a custom error handler.




on error goto script_error



# Get the IP address for the site

dns "www.alertra.com"



# Connect to the FTP server

tcp $FTP_PORT



# Request the readme.txt file with the anonymous login ID

# Only get the first 512 bytes of the file

ftp "/pub/readme.txt" "anonymous" "jsmith@virginia.com" 512



# Make sure we got the right file	

if not "Alertra" in $CONTENT then error "Returned file not correct."



goto exit

		

:script_error

	

# Ignore DNS errors

if not $LAST_COMMAND = "dns" then goto send_error

exit "DNS failed, site not checked."



:send_error

on error goto 0

error $LAST_MSG



:exit

	

The first line of our script changes how ASL handles errors. Anytime the error command is called or an internal command generates an error, the script will jump to the ":script_error" label. This allows you to handle script errors in a central location. This script looks at the name of the command that generated the error ($LAST_COMMAND). If the command was a DNS lookup DNS, the script chooses to ignore the error, log the fact that it failed, and exit gracefully.

The final section of the error handler removes the error handler and then re-generates the error so ASL's normal notification mechanism will notify the scheduled contacts of the error.

In your scripts, there really isn't any reason to do this, but just to show that it can be done, lets recode the original example with a totally custom error handler:




on error goto next



dns "www.alertra.com"

if not $LAST_CODE = "0" then goto bad_dns



tcp $HTTP_PORT

if not $LAST_CODE = "0" then wait 20;tcp $HTTP_PORT

if not $LAST_CODE = "0" then wait 20;tcp $HTTP_PORT

if not $LAST_CODE = "0" then goto bad_socket



http get,redir "/"

if not $LAST_CODE = "0" then goto bad_http



goto exit



:bad_dns

on error goto 0

info "(" + $LAST_CODE + ") " + $LAST_DESCRIPTION

error "Unable to resolve DNS entry.  Check primary " + "and secondary DNS servers."



:bad_socket

on error goto 0

info "(" + $LAST_CODE + ") " + $LAST_DESCRIPTION

error "Unable to connect to host. " + "Make sure HTTP server is running on port " + $HTTP_PORT + "."



:bad_http

on error goto 0

info "(" + $LAST_CODE + ") " + $LAST_DESCRIPTION

error "Unable to retrieve " + $LAST_RESOURCE + "."



:exit



This example shows two other features of ASL scripting. While trying to make the socket connection to the server, we attempt the connection 3 times and wait 20 seconds between each attempt. The IF statement allows you to chain commands together with a semi-colon, so we can wait and retry the connection in one statement. You could also use the begin/end construct to accomplish the same thing:




if not $LAST_CODE = "0" then begin

	wait 20

	tcp $HTTP_PORT

end



2.4. Saving Data

You can save the contents of variables for use the next time your script is called. This can be used to determine when things change. For instance, if you want to know that the IP address the DNS server returns for your site changes you could do something like this:




dns "www.alertra.com"

if $SAVE_IP = "" then set SAVE_IP = $LAST_IP

if not $SAVE_IP = $LAST_IP then goto new_ip



:check

tcp $HTTP_PORT

http get "/"



goto exit



:new_ip

warning "IP address used to be " + $LAST_IP + " but is now " + $SAVE_IP

goto check



:exit

set SAVE_IP = $LAST_IP

save SAVE_IP



In this script, we check the site as normal. If ASL is unable to connect to the web server, an error notification will be sent to the scheduled contacts. However, we also check to see if the IP address has changed. After we retrieve the IP address from the DNS server, we compare it to what the IP address was last time the script ran. To do that we have to work a little magic.

If you ask for the contents of a variable and it doesn't exist, ASL returns an empty string. So our first IF checks to see if we have a valid SAVE_IP. If we don't then we set the SAVE_IP to the value of the current IP. This keeps the next IF statement from firing the first time we run the script. The last bit of magic is taken care of in the ":exit" section. The save command stores the contents of the variable. The next time the script is run, the variable will already exist which is what we want.

This final script demonstrates how you can detect if there have been changes made to your site.




dns "www.alertra.com"

tcp $HTTP_PORT



http get "/index.html"

if $SAVE_DIGEST = "" then digest SAVE_DIGEST = $CONTENT

digest HASH = $CONTENT

if not $HASH = $SAVE_DIGEST then goto possible_hacker



goto exit



:possible_hacker

error "Page has been modified."



:exit

set SAVE_DIGEST = $HASH

save SAVE_DIGEST



The digest command creates a digest of the string it is given. The digest is like a fingerprint and it would be very difficult for someone to change the contents of a file in such a way that it would have different content but generate the same digest.

2.5. Low Level Functions

Low level functions can be used to interact with any socket based service that uses a text based protocol.




dns "www.alertra.com"

tcp 5602



send "HELO\n"

recv 

if $CONTENT = "Widget Server/1.0" then goto WidgetServer



error "Server did not return proper version."



:widgetserver



recv "login:"

send "jsmith\r\n"

recv "password:"

send "pocahontas\r\n"



recv "READY.\r\n"

goto exit



:exit



The commands send and recv are low level functions that allow you to send and receive character data (and to a limited extent binary data) on an open socket. The send command is fairly straight forward; when called it sends the data provided to the server. You can include \r and/or \n to signifiy the carriage-return and new-line characters respectively.

The recv command acts a little differently depending on how it is called. In the first call to recv, the socket is read until no more data is available and that data is placed in the $CONTENT variable. If no data is received before the default timeout, an error is generated. Since we don't have an error handler installed, the script would be aborted.

In the later recv calls we tell recv the text we expect to receive. Instead of storing the response in a variable, the string you pass is compared to the data read from the socket. If the data read doesn't end with the string passed, an error is generated. Notice that in the receive string you can also pass \r and \n to signify the carrige-return and line-feed characters.

2.6. Looping

FOR-loop

The for loop can be used to iterate over a fixed number of items:




for INTERATOR = START_INDEX to END_INDEX begin

	...

end



The for loop initializes ITERATOR to the value of START_INDEX. By default ITERATOR will then be incrememnted by 1 each time through the loop until it reaches END_INDEX.


for REC = 1 to $MYSQL_ROW_COUNT begin

	mysql cursor next

	if $MYSQL_COLUMN_status = "A" then begin

		info $MYSQL_COLUMN_0 + ", " + $MYSQL_COLUMN_email + ", Active"

	else

		info $MYSQL_COLUMN_0 + ", " + $MYSQL_COLUMN_email + ", Not Active"

	end

end

In this example, the loop iterates the over the rows in a MySQL resultset. The variable REC will be set to the current index of the for loop through each iteration. In the example above, the loop will start at 1 and increment REC by 1 until REC is equal to the value of the variable $MYSQL_ROW_COUNT. The default increment is 1, but you can control this by using the step modifier. With step you can increment by any integer value as well as use negative values to run the loop backwards:


for I = 10 to 1 step -1 begin

	info $I

end

Would result in the log containing the text:


10

9

8

7

6

5

4

3

2

1

If you only have one statement to execute in the loop, like the previous example, you could rewrite it like this:




for I = 10 to 1 step -1 info $I



WHILE-loop

The while loop allows a script to execute the same block of commands an indefinate number of times (note: ASL scripts are not allowed to execute more than 5,000 commands; a script that exceeds this limit will be killed by the system).




while EXPRESSION begin

	...

end



As long as the EXPRESSION remains true (1 is true in ASL while 0 is false), the loop will continue to execute. Once the EXPRESSION becomes false (0), then execution of the script will continue with the next statement after the while loop's end statement.




set N = 0

while $N <= 10 loop begin



	info "The value is " + $N

	set N = $N + 1

	

end



This example loops until the value of $N is greater than 10.


Alertra Script Language: Language Reference