6.3. T-Script

6.3.1. Introduction

T-Script is a scripting language which primary purpose is to generate text files. It can be useful for processing templates with some additional data retrieved from data sources like SQL databases or text files.

Before compilation ensure that you have in your system packages bison (at least 1.875 version) and flex.

6.3.2. Syntax

T-Script language syntax is based on other popular languages like C or JavaScript with little changes made to make writting templates simpler. Additional commands should be written inside { } parenthesis. Data outside curly brackets will be writed to output file (or ommited if file was not specified). All expressions, variables, commands, function names are case-sensitive. To separate expressions inside parenthesis use semicolon sign.

6.3.2.1. Expressions and operators

  • Literal inside quotes. Supports formating sequences like in C language (\t, \n, \\).

    Example: "some literal"

  • Number.

    Example: 1234

  • Value of variable "var".

    Example: var

  • N-th element of array "var".

    Example: var[n]

  • Subvariable "n" of variable "var".

    Example: var.n

  • Value of expression inside parenthesis.

    Example: ( expression )

  • Null keyword. Prescribe not defined value. Useful for checking that some value was or wasn't defined.

    Example: variable = null

  • Comparisions. Returns logical result of expressions comparision.

    Example:

    expression1 == expression2;
    expression1 != expression2;
    expression1 < expression2;
    expression1 > expression2;
    expression1 <= expression2;
    expression1 >= expression2;

  • Binary operators. Sum and product.

    Example: expression1 | expression2

    Example: expression1 & expression2

  • Logical operators.

    Example: expression1 || expression2

    Example: expression1 && expression2

    Example: ! expression

  • Strings concatenation. When both expression values haven't numeric type treats expressions as strings and performs string concatenation.

    Example: expression1 + expression2

  • Arithmetic operators. Evaluates to result of arithmetic operation on two expression values.

    Example:

    expression1 + expression2;
    expression1 - expression2;
    expression1 * expression2;
    expression1 / expression2;
    expression1 % expression2;

  • Unary increment/decrement operators.

    Example: expression++

    Example: expression--

    Example: ++expression

    Example: --expression

  • Bit shift operators.

    Example: expr1 >> expr2

    Example: expr1 << expr2

  • String comparision with regular expression. Evaluates to 1 if expression expr1 value match with regular expression expr2, else 0.

    Example: expr1 =~ expr2

6.3.2.2. Comments

  • C-style comment.

    Example: /* this is a comment - it can be also multiline */

6.3.2.3. Commands

  • Assignment. Assigns expression value to specified variable.

    Example: variable = expression

  • Conditional statement. Executes statement only if expression is true. Second form executes statement1 if expression is true or statement2 if expression is false.

    Example:

    if ( expression ) statement /if
    if ( expression ) statement1 else statement2 /if

    Text between command blocks is treated as a command so the following example is correct:

    Some text 
    {if (a==1)} 
    a equals 1 
    {else} 
    a doesn't equal 1 
    {/if} 
    You can put backslash between command block and end of line to eat \n symbol and keep normal text flow. For example:
    Some text 
    {if (a==1)}\ 
    a equals 1 
    {else}\ 
    a doesn't equal 1 
    {/if}\

  • Iterative loop. Executes first expr1 as loop initialization command. Then executes statement while expr2 is true. At the end of each iteration expr2 is evaluated.

    Example: for ( expr1 ; expr2 ; expr3 ) statement /for

  • Construct foreach. This simply gives an easy way to iterate over arrays. foreach works only on arrays, and will issue an error when you try to use it on a variable with a different data type. It loops over the array given by array. On each loop, the value of the current element is assigned to value and the internal array pointer is advanced by one (so on the next loop, you'll be looking at the next element).

    Example: foreach ( value in array ) statement /foreach

  • While loop. Executes statement(s) repeatedly, as long as the expression evaluates to true. The value of the expression is checked each time at the beginning of the loop, so even if this value changes during the execution of the nested statement(s), execution will not stop until the end of the iteration.

    Example: while ( expression ) statement /while

  • break. This command ends execution of the current loop structure.

    Example:

    {for (i = 0; i < 10; i++)}\
    {if (i == 5)}{break}{/if}\
    : {i}
    {/for}\

  • continue. continue is used within looping structures to skip the rest of the current loop iteration and continue execution at the condition evaluation and then the beginning of the next iteration.

    Example:

    {for (i = 0; i < 10; i++)}\
    {if (i == 5)}{continue}{/if}\
    : {i}
    {/for}\

  • exit. This command terminates the current script.

    Example:

    {if ( var > 0 )
    exit;
    /if}

6.3.2.4. Functions

Functions have two calling forms: with brackets ({function(var)}) and without brackets ({function {var}}).

  • string(number)

    Converts numeric variable to string value.

    Example: string(var)

  • number(string)

    Converts string variable to numeric value. For arrays returns number of items in array.

    Example: number(var)

  • typeof(variable)

    Type checking. Returns type name of variable, e.g. string, number, array, null.

    Example: typeof(var)

In script above functions can be used like that:
{x = 5}x = {x}
{var = "3"}var = {var}
x + var = {x + var}
x + var = {number(var) + x}
x + var = {string(x) + var}
x is type of {typeof(x)}
var is type of {typeof(var)}

6.3.3. Extensions

Extensions are tscript library supplements. That are functions and predefined variables (constants), wich can be used in scripts.

6.3.3.1. Exec

Shell commands execution is possible with exec(). You can run many commands separated by semicolons in one function call.

  • exec(commands)

    Shell commands execution.

    Example: exec("rm -f /")

6.3.3.2. String

String consists basic functions for strings operations.

  • trim(string)

    Deleting of whitespace signs from the beginning and the end of string.

    Example: trim(" aaa ")

  • len(string)

    String length (strlen() in C).

    Przykład: length = len(string)

  • replace(pattern, replacement, string)

    This function scans string for matches to pattern, then replaces the matched text with replacement. Pattern can be a regular expression.

    Example: replace(":", "-", mac)

    Example: replace("[a-z]", "-", "Text")

  • explode(separator, string)

    Returns an array of strings, each of which is a substring of string formed by splitting it on boundaries formed by separator. Separator can be a string or POSIX's regural expression.

    Example: explode(":", "aaa:bbb:ccc")

    Example: explode("[ ]+", "aaa bbb ccc")

6.3.3.3. Sysinfo

Extension with name Sysinfo consist functions for retriving data from system.

  • date([format_string])

    Current date and time formated according to the format specification. Default format is '%Y/%m/%d'. Conversion specifiers are introduced by a '%' character You can read about all of them in `man strftime`.

    Returned object consist predefined subvariables year, month, day, hour, minute, second.

    Example:

    {date("%s") /* returns current unix timestamp */}
    {t = date()}
    {t.month /* returns current month number */}

  • systype

    System type constant. Returns "unix" or "win32" according to system where program's working.

    Example:

    {if (systype == "unix")}\
    {exec echo executing command}\
    {else}\
    no shell
    {/if}\

6.3.3.4. File

This extension is destined for basic file operations.

  • file(filename)

    Script output redirection. Redirects script output to file. Data will be appended to specified file.

    Example: {file file_name} commands {/file}

  • fileexists(filename)

    If file exists returns 1, else 0.

    Example:

    {if fileexists(file)}{deletefile(file)}{/if}

  • deletefile(filename)

    File deletion.

    Example: deletefile("/tmp/file.txt")

  • readfile(filename)

    Creates array where every element is separated line of file.

    Example: readfile("/tmp/file.txt")

  • getfile(filename)

    Returns file contents.

    Example: getfile("/tmp/file.txt")

  • listdir(directory)

    Returns list of files (and subdirectories) in array. Every element contains subvariable 'size' with file size in bytes.

    Example: listdir("/home/alec")

Listing below presents example script with use of all file functions.
{list = listdir("/home/alec/lms/doc")}
{for (x = 0; x < number(list); x++) }\
{list[x]}--{list[x].size}
{/for}\
{file "/home/alec/file.txt"}
Line 1
Line 2
{/file}\
{f = readfile /home/alec/file.txt}\
{for (i = 0; i < number(f); i++) }\
line {i}: {f[i]}\
{/for}\
{f = getfile /home/alec/file.txt}\
{f}
{deletefile /home/alec/file.txt}\

6.3.3.5. Syslog

Extension with name Syslog consist function for sending messages to system logger. Also includes logs priority (level) definitions.

  • syslog(string [, level])

    Sends message specified by string. Second, optional argument specify logs priority. Default priority is LOG_INFO (see man 3 syslog).

    Example:

    syslog("message", LOG_ERR);
    syslog("message");

6.3.3.6. Net

In this extension are included two functions (with lowercase names) destined to IP addresses and subnet masks operations.

  • mask2prefix(mask_string)

    Returns number of bits in subnet mask.

    Example: mask2prefix("255.255.255.0")

  • ip2long(address_string)

    Changes octal IP address to long number.

    Example: ip2long("192.168.0.1")

  • long2ip(number)

    Changes long number to IP address octal format (xxx.xxx.xxx.xxx).

    Example: long2ip(variable)

  • broadcast(address_string, mask_string)

    Calculates broadcast address from specified IP address and mask (any mask format).

    Example: broadcast("192.168.0.1", "255.255.255.0")

6.3.3.7. SQL

SQL extension implements functions for database operations. Allows to run SQL commands.

  • SQL commands: SELECT, INSERT, DELETE, UPDATE, CREATE, DROP.

    Example:

    {SELECT * FROM table}
    {INSERT INTO table VALUES(1)}
    {DELETE FROM table}
    {UPDATE table SET column=1}
    {CREATE TABLE foo (bar integer) }
    {DROP TABLE foo}

  • rows(sql_query)

    Returns SQL result rows count. Use it for non-select commands.

    Example: rows("SELECT * FROM table")

  • escape(string)

    Escapes a string for use within an SQL commands.

    Example: SELECT * FROM table WHERE name={escape(variable)}

6.3.3.8. Constants

Extension closely connected with LMS. Makes possible to create scripts without of LMS's database structure knowledge. Contains predefined constants, which consists data from database. Query defined in program is executed when constant is used first time. Constants names are uppercase. Each constant is an array with rows indexed starting from zero, and each row consist subvariables accessible by name (lowercase).

  • CUSTOMERS - customers list:

    id - customer ID
    lastname - customer lastname
    name - customer name
    status - status
    address - address
    zip - postal code
    city - city
    email - email address
    ten - tax exempt number
    ssn - security serial number
    regon - business registration number
    icn - identity card number
    rbe - register of business entities number
    info - additional information
    message - warning contents
    warning - warning status (status of all customer's nodes summary)
    access - accessibility status (status of all customer's nodes summary)
    balance - customer's balance

  • NODES - nodes list (and network devices addresses):

    id - node ID
    owner - customer name and lastname
    ownerid - customer ID ('0' for devices)
    name - node (device's address) name
    access - status: connected/disconnected (1/0)
    warning - warnings status: enabled/disabled (1/0)
    netdev - device ID, to which is connected
    lastonline - last activity timestamp
    info - additional information
    message - warning message contents
    mac - MAC address
    passwd - password
    ip - IP address
    ip_pub - public IP address
    linktype - connection type (0-cable, 1-air)
    port - device's port to which node is connected
    chkmac - MAC checking (0-disabled, 1-enable)
    halfduplex - duplex mode (0-full, 1-half)

  • NETWORKS - networks list:

    id - network ID
    name - network name
    address - IP address
    mask - subnet mask (xxx.xxx.xxx.xxx)
    prefix - number of bits in mask
    size - network size (number of addresses)
    interface - interface name
    gateway - gateway address
    dns - primary DNS server
    dns2 - secondary DNS server
    wins - WINS server
    domain - domain name
    dhcpstart - start of DHCP range
    dhcpend - end of DHCP range

6.3.4. Example Scripts

Let's begin from simple script creating file /etc/hosts with list of computers (and devices) IPs and names list.

Example 6-1. Parser: Creating /etc/hosts file

{result = SELECT name, inet_ntoa(ipaddr) AS ip FROM nodes}\
127.0.0.1	localhost
{for (r=0; r<number(result); r++)}\
{result[r].name}{"\t"}{result[r].ip}
{/for}\

How to create debtors list? It's easy with use of predefined constants.

Example 6-2. Parser: Debtors list

{
for (r=0; r<number(CUSTOMERS); r++)
    if (CUSTOMERS[r].balance < 0)
}\
{CUSTOMERS[r].lastname} {CUSTOMERS[r].name}{"\t"}{CUSTOMERS[r].balance}
{
    /if
/for
}\

List of ethernet hosts desriptions for iptraf. Here the format of hardware address is specific. It must be writed without separators.

Example 6-3. Parser: Ethernet hosts descriptions for iptraf.

{list = SELECT LOWER(mac) AS mac, UPPER(name) AS name, inet_ntoa(ipaddr) AS ip from nodes}\
{for(i=0; i<number(list); i++)}\
{replace(":","",list[i].mac)}:{list[i].name} {list[i].ip}
{/for}

In next example we're create file with hosts ethernet hardware addresses to IP addresses mappings, used by arp program. Hosts with no access gets dummy MACs.

Example 6-4. Parser: Ethers file for arp

{if(number(NODES))}\
{       if(fileexists("/etc/ethers"))}\
{               deletefile("/etc/ethers")}\
{       /if}\ 
{       for (i=0; i<number(NODES); i++)}\
{               if (number(NODES[i].access))}\
{                       nodes[i].mac}{"\t"}{NODES[i].ip}
{               else}\
{                      }00:00:00:00:00:00{"\t"}{NODES[i].ip}
{               /if}\
{      /for}\
{/if}\

Next example is longer. Here we are using especially 'exec'. Script sends e-mails to customers with balance less than specified limit.

Example 6-5. Parser: Notify module replacement

{limit = 0}
{dt = date()}
{customers = SELECT customers.id AS id, email, pin, name, lastname,
        SUM((type * -2 +7) * cash.value) AS balance
        FROM customers
        LEFT JOIN cash ON customers.id = cash.customerid AND (cash.type = 3 OR cash.type = 4)
        WHERE deleted = 0 AND email!=''
        GROUP BY customers.id, name, lastname, email, pin
        HAVING SUM((type * -2 +7) * cash.value) < {limit}}

{for(i=0; i<number(customers); i++)}

    {exec echo "NOTE: This message has been generated automatically.

We kindly remind that you have debt on your internet service provide account
for the amount of $ {customers[i].balance}.

If you have already regulated your subscription fees for current month, that
is {dt.month} {dt.year}, please just skip this message.

If you think this message was sent to you in error, please contact our
customer service representative.

All information about payments could be also found at:
http://bigpro.com/myAccount/

If you want to regulate your account status, please contact our accountant:

Aldfert Rule
phone: 0-509031337
e-mail: alde@staff.bigpro.com

PS. Last 10 operations on your account has been attached below for your
convenience.
--------------+--------------+-----------------------------
     Date     |    Value     |           Comment
--------------+--------------+-----------------------------" > /tmp/mail}

    {last10 = SELECT comment, time, CASE WHEN type=4 THEN value*-1 ELSE value END AS value
            FROM cash WHERE customerid = {customers[i].id}
            ORDER BY time DESC LIMIT 10}
    
    {for(j=0; j<number(last10); j++)}
    
        {exec echo "{last10[j].time}|{"\t"}{last10[j].value}|{"\t"}{last10[j].comment}" >> /tmp/mail}
    
    {/for}

    {exec mail -s "Liabilities information" -r lms@domain.tld {customers[i].email} < /tmp/mail}

{/for}

Next more complicated example is the traffic module replacement. Reads text file with stats from firewall counters and writes data to LMS's stats database.

Example 6-6. Parser: Traffic stats.

{
log = "/var/log/traffic.log";
nodes = SELECT id, INET_NTOA(ipaddr) AS ip, INET_NTOA(ipaddr_pub) AS ip_pub FROM nodes;
if(! fileexists(log))
    exit;
/if;
lines = readfile(log);
n = number(nodes);
for (i=0; i<number(lines); i++)
    line = explode("[[:blank:]]+", lines[i]); /* file format: IP upload download */
    if ( number(line) == 3  && (line[1] > 0 || line[2] > 0) )
        for (x=0; x<n; x++)
            if (nodes[x].ip == line[0] || nodes[x].ip_pub == line[0] )
                id = nodes[x].id;
                break;
            /if;
        /for;
        if (x < n)
            INSERT INTO stats (nodeid, dt, download, upload) VALUES ({id}, %NOW%, {line[2]}, {line[
        /if;
     /if;
/for;