LMS - LAN Management System 1.11-git
Prev | Chapter 6. LMS Daemon | Next

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

Prev | Home | Next
Modules | Up | For curious