In this article Barry Mavin, CEO and Chief Software Architect for Recital details how to Build C Extension Libraries to use with Recital.
Overview
It is possible to extend the functionaliy of Recital products using "Extension libraries" that can be written in C. These extension libraries, written using the Recital/SDK API, are dynamically loadable from all Recital 9 products. This includes:
- Recital
- Recital Server
- Recital Web
Building C Extension Libraries
You can create C wrappers for virtually any native operating system function and access these from the Recital 4GL. Unlike traditional APIs which only handle the development of C functions that are callable from the 4GL, the Recital/SDK allows you to build Classes that are accessible from all Recital products. e.g. You could create a GUI framework for Linux that handles VFP system classes!
To deploy your C Extension Libraries, copy them to the following location:
Windows:
\Program Files\Recital\extensions
Linux/Unix:
/opt/recital/extensions
Please see the Recital/SDK API Reference documentation for further details.
Sample code
Listed below is the complete example of a C Extension Library.:
////////////////////////////////////////////////////////////////////////////////
#include "mirage_demo.h"
////////////////////////////////////////////////////////////////////////////////
// Declare your functions and classes below as follows:
//
// Recital Function Name, C Function Name, Type (Function or Class)
//
#define MAX_ELEMENTS 7
static struct API_SHARED_FUNCTION_TABLE api_function_table[MAX_ELEMENTS] = {
{"schar", "fnSamplesCharacter", API_FUNCTION},
{"stype", "fnSamplesType", API_FUNCTION},
{"slog", "fnSamplesLogical", API_FUNCTION},
{"snum", "fnSamplesNumeric", API_FUNCTION},
{"sopen", "fnSamplesOpen", API_FUNCTION},
{"myclass", "clsMyClass", API_CLASS},
{NULL, NULL, -1}
};
////////////////////////////////////////////////////////////////////////////////
// Recital API initialization. This should be in only ONE of your C files
// **IT SHOULD NEVER BE EDITED OR REMOVED**
INIT_API;
///////////////////////////////////////////////////////////////////////
// This is an example of passing a character parameter and returning one.
RECITAL_FUNCTION fnSamplesCharacter(void)
{
char *arg1;
if (!_parse_parameters(PCOUNT, "C", &arg1)) {
ERROR(-1, "Incorrect parameters");
}
_retc(arg1);
}
///////////////////////////////////////////////////////////////////////
// This is an example of passing a numeric parameter and returning one.
RECITAL_FUNCTION fnSamplesNumeric(void)
{
int arg1;
if (!_parse_parameters(PCOUNT, "N", &arg1)) {
ERROR(-1, "Incorrect parameters");
}
_retni(arg1);
}
///////////////////////////////////////////////////////////////////////
// This is an example returns the data type of the parameter passed.
RECITAL_FUNCTION fnSamplesType(void)
{
char result[10];
if (PCOUNT != 1) {
ERROR(-1, "Incorrect parameters");
}
switch (_parinfo(1)) {
case API_CTYPE:
strcpy(result, "Character");
break;
case API_NTYPE:
strcpy(result, "Numeric");
break;
case API_LTYPE:
strcpy(result, "Logical");
break;
case API_DTYPE:
strcpy(result, "Date");
break;
case API_TTYPE:
strcpy(result, "DateTime");
break;
case API_YTYPE:
strcpy(result, "Currency");
break;
case API_ATYPE:
strcpy(result, "Array");
break;
default:
strcpy(result, "Unkown");
break;
}
_retc(result);
}
///////////////////////////////////////////////////////////////////////
// This is an example returns "True" or False.
RECITAL_FUNCTION fnSamplesLogical(void)
{
char result[10];
int arg1;
if (!_parse_parameters(PCOUNT, "L", &arg1)) {
ERROR(-1, "Incorrect parameters");
}
if (arg1) strcpy(result, "True");
else strcpy(result, "False");
_retc(result);
}
///////////////////////////////////////////////////////////////////////
// This example opens a table.
RECITAL_FUNCTION fnSamplesOpen(void)
{
char *arg1;
if (!_parse_parameters(PCOUNT, "C", &arg1)) {
ERROR(-1, "Incorrect parameters");
}
if (_parinfo(1) == API_CTYPE) {
_retni(COMMAND(arg1));
} else {
_retni(-1);
}
}
///////////////////////////////////////////////////////////////////////
// Define the MyClass CLASS using the API macros
///////////////////////////////////////////////////////////////////////
RECITAL_EXPORT int DEFINE_CLASS(clsMyClass)
{
/*-------------------------------------*/
/* Dispatch factory methods and return */
/*-------------------------------------*/
DISPATCH_FACTORY();
/*---------------------------------*/
/* Dispatch constructor and return */
/*---------------------------------*/
DISPATCH_METHOD(clsMyClass, Constructor);
/*--------------------------------*/
/* Dispatch destructor and return */
/*--------------------------------*/
DISPATCH_METHOD(clsMyClass, Destructor);
/*-----------------------------------*/
/* Dispatch DEFINE method and return */
/*-----------------------------------*/
DISPATCH_METHOD(clsMyClass, Define);
/*------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property NumValue */
/* then return. */
/*------------------------------*/
DISPATCH_PROPSET(clsMyClass, NumValue);
DISPATCH_PROPGET(clsMyClass, NumValue);
/*------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property LogValue */
/* then return. */
/*------------------------------*/
DISPATCH_PROPSET(clsMyClass, LogValue);
DISPATCH_PROPGET(clsMyClass, LogValue);
/*-------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property DateValue */
/* then return. */
/*-------------------------------*/
DISPATCH_PROPSET(clsMyClass, DateValue);
DISPATCH_PROPGET(clsMyClass, DateValue);
/*-------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property TimeValue */
/* then return. */
/*-------------------------------*/
DISPATCH_PROPSET(clsMyClass, TimeValue);
DISPATCH_PROPGET(clsMyClass, TimeValue);
/*-------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property CurrValue */
/* then return. */
/*-------------------------------*/
DISPATCH_PROPSET(clsMyClass, CurrValue);
DISPATCH_PROPGET(clsMyClass, CurrValue);
/*-------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property CharValue */
/* then return. */
/*-------------------------------*/
DISPATCH_PROPSET(clsMyClass, CharValue);
DISPATCH_PROPGET(clsMyClass, CharValue);
/*------------------------------*/
/* Dispatch SET or GET PROPERTY */
/* method for property ObjValue */
/* then return. */
/*------------------------------*/
DISPATCH_PROPSET(clsMyClass, ObjValue);
DISPATCH_PROPGET(clsMyClass, ObjValue);
/*-----------------------------------*/
/* If message not found return error */
/*-----------------------------------*/
OBJECT_RETERROR("Unknown message type");
}
////////////////////////////////////////////////////////////////////////////////
// Define METHOD handlers
////////////////////////////////////////////////////////////////////////////////
DEFINE_METHOD(clsMyClass, Constructor)
{
struct example_data *objectDataArea;
/* Allocate memory for objects objectData area */
objectDataArea = (struct example_data *)
malloc(sizeof(struct example_data));
if (objectDataArea == NULL) return(-1);
/* Assign the default property values */
strcpy(objectDataArea->prop_charvalue, "Test API object");
objectDataArea->prop_numvalue = 15.2827;
objectDataArea->prop_logvalue = 'F';
strcpy(objectDataArea->prop_datevalue, DATE_DATE());
strcpy(objectDataArea->prop_timevalue, DATE_DATETIME());
strcpy(objectDataArea->prop_currvalue, "15.2827");
strcpy(objectDataArea->object_name, "APIobject");
objectDataArea->prop_objvalue
= OBJECT_NEW(objectDataArea->object_name, "exception", NULL);
/* Set the object objectData area */
OBJECT_SETDATA((char *)objectDataArea);
return(0);
}
DEFINE_METHOD(clsMyClass, Destructor)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData != NULL) {
if (objectData->prop_objvalue != NULL)
OBJECT_DELETE(objectData->prop_objvalue);
free(objectData);
objectData = NULL;
}
return(0);
}
DEFINE_METHOD(clsMyClass, Define)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
char buffer[512];
int rc;
/* Check the object class */
OBJECT_GETPROPERTY(objectData->prop_objvalue, "class", buffer);
rc = OBJECT_GETARG(buffer, &result);
if (result.errno == 0 && result.type == 'C'
&& strcmp(result.character, "Exception") == 0) {
switch (OBJECT_GETARGC()) {
case 1:
rc = OBJECT_GETPARAMETER(1, &result);
if (result.errno == 0 && result.type == 'C') {
OBJECT_SETARG(buffer, &result);
rc = OBJECT_SETPROPERTY(objectData->prop_objvalue,
"message", buffer);
}
break;
case 2:
rc = OBJECT_GETPARAMETER(2, &result);
if (result.errno == 0 && result.type == 'N') {
OBJECT_SETARG(buffer, &result);
rc = OBJECT_SETPROPERTY(objectData->prop_objvalue,
"errorno", buffer);
}
}
}
result.type = 'L';
result.logical = (rc == 0 ? 'T' : 'F');
OBJECT_RETRESULT(&result);
}
////////////////////////////////////////////////////////////////////////////////
// Define GET property handlers
////////////////////////////////////////////////////////////////////////////////
DEFINE_PROPERTYGET(clsMyClass, NumValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('N', objectData->prop_numvalue);
}
DEFINE_PROPERTYGET(clsMyClass, LogValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('L', objectData->prop_logvalue);
}
DEFINE_PROPERTYGET(clsMyClass, DateValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('D', objectData->prop_datevalue);
}
DEFINE_PROPERTYGET(clsMyClass, TimeValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('T', objectData->prop_timevalue);
}
DEFINE_PROPERTYGET(clsMyClass, CurrValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('Y', objectData->prop_currvalue);
}
DEFINE_PROPERTYGET(clsMyClass, CharValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('C', objectData->prop_charvalue);
}
DEFINE_PROPERTYGET(clsMyClass, ObjValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
if (objectData == NULL) return(-1);
OBJECT_RETPROPERTY('O', objectData->prop_objvalue);
}
////////////////////////////////////////////////////////////////////////////////
// Define SET property handlers
////////////////////////////////////////////////////////////////////////////////
DEFINE_PROPERTYSET(clsMyClass, NumValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
int rc = OBJECT_ERROR;
OBJECT_GETVALUE(&result);
if (result.errno == 0 && result.type == 'N') {
objectData->prop_numvalue = result.number;
rc = OBJECT_SUCCESS;
}
return(rc);
}
DEFINE_PROPERTYSET(clsMyClass, LogValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
int rc = OBJECT_ERROR;
OBJECT_GETVALUE(&result);
if (result.errno == 0 && result.type == 'L') {
objectData->prop_logvalue = result.logical;
rc = OBJECT_SUCCESS;
}
return(rc);
}
DEFINE_PROPERTYSET(clsMyClass, DateValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
int rc = OBJECT_ERROR;
OBJECT_GETVALUE(&result);
if (result.errno == 0 && result.type == 'D') {
strcpy(objectData->prop_datevalue, DATE_DTOS(result.date));
rc = OBJECT_SUCCESS;
}
return(rc);
}
DEFINE_PROPERTYSET(clsMyClass, TimeValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
int rc = OBJECT_ERROR;
OBJECT_GETVALUE(&result);
if (result.errno == 0 && result.type == 'T') {
strcpy(objectData->prop_timevalue, DATE_TTOS(result.datetime));
rc = OBJECT_SUCCESS;
}
return(rc);
}
DEFINE_PROPERTYSET(clsMyClass, CurrValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
int rc = OBJECT_ERROR;
OBJECT_GETVALUE(&result);
if (result.errno == 0 && result.type == 'Y') {
strcpy(objectData->prop_currvalue, CURR_YTOS(result.currency));
rc = OBJECT_SUCCESS;
}
return(rc);
}
DEFINE_PROPERTYSET(clsMyClass, CharValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
struct API_EXPRESSION result;
int rc = OBJECT_ERROR;
OBJECT_GETVALUE(&result);
if (result.errno == 0 && result.type == 'C') {
strcpy(objectData->prop_currvalue, result.character);
rc = OBJECT_SUCCESS;
}
return(rc);
}
DEFINE_PROPERTYSET(clsMyClass, ObjValue)
{
struct example_data *objectData = (struct example_data *)OBJECT_GETDATA();
OBJECT objvalue;
int rc = OBJECT_ERROR;
if (OBJECT_GETTYPE() == 'O') {
objvalue = OBJECT_GETOBJECT();
objectData->prop_objvalue = OBJECT_ASSIGN(objvalue, objectData->object_name);
rc = OBJECT_SUCCESS;
}
return(rc);
} It would appear that gigabit LAN is not! In fact it often runs at the same speed as 100Mbps LAN. Let's look at why exactly.
After configuring your network you can use the ifconfig command to see what speeds the LAN is connected. Even though 1000Mbps is reported by the card, the reality is that the overall throughtput may well be ~100Mpbs. You can try copying a large file using scp to demonstrate this.
As it turns out, in order to use a gigabit LAN you need to use CAT6 cables. CAT5 and CAT5E are not good enough. End result, the ethernet cards throttle back the speed to reduce dropped packets and errors.
You can find a good article here titled Squeeze Your Gigabit NIC for Top Performance. After tuning up the TCP parameters i found that it made no dfifference. The principal reasons behind low gigabit ethernet performance can be summed up as follows.
- Need to use CAT6 cables
- Slow Disk speed
- Limitations of the PCI bus which the gigabit ethernet cards use
You can get an idea about the disk speed using the hdparm command:
Display the disk partitions and choose the main linux partition which has the / filesystem.
# fdisk -l
Then get disk cache and disk read statistics:
# hdparm -Tt /dev/sda0
On my desktop system the sata disk perfomance is a limiting factor. These were the results:
/dev/sda1:
Timing cached reads: 9984 MB in 2.00 seconds = 4996.41 MB/sec
Timing buffered disk reads: 84 MB in 3.13 seconds = 58.49 MB/sec
Well, that equates to a raw disk read speed of 58.49 * 8 = 467Mbps which is half the speed of a gigabit LAN.
So.. NAS storage with lots of memory looks to be the way to go... If you use the right cables!
Recital Web Getting Started
This article talks about the log files available in Recital products and how to enable logging when required.
Overview
When discussing a problem with the Recital Support Team, one of the most common requests is that you enable logging and send them the log files produced. Log files along with error files are designed to provide detailed information about Recital processes and the prevailing environment and can be a fast-track to resolving a problem.
Log Files
There are three main types of log file:
- System Log
- Client/Server Communication Logs
- Custom Logs
System Log
The System log is a system-wide all product log. It tracks all login and logout operations from either Recital or the Recital Server. Logout details include the exit code: 0 for an error-free, 'normal' exit and the error number and message when an error has occurred. It also shows the licenses that have been loaded and any license error codes and messages. The system log filename is recital.log.
Client/Server Communication Logs
The Client/Server communication logs track the requests and responses between the Recital Server and its clients. The log files are as follows:
| Filename | Type | Description |
|---|---|---|
|
dbserver.log |
System-wide |
The Recital Server startup log. This logs any problems with the Recital Server startup. |
|
port.log |
System-wide |
The port listener log. The port listener (or portserver) listens on port 8001 for client connection requests and spawns the appropriate server process. |
|
net.log |
Connection |
The netserver log. The netserver is the Recital Server database and 4GL engine. |
|
rsi.log |
Connection |
The Recital Server Interface (RSI) Gateway log. This logs communication with the Database Gateways to SQL databases. |
|
rec.log |
Connection |
The Recital Database Gateway log. The Recital Database Gateway (or recserver) is the SQL database engine for Recital Gateway data access. |
|
mys.log |
Connection |
The MySQL Database Gateway log. |
|
ora.log |
Connection |
The Oracle Database Gateway log. |
|
inf.log |
Connection |
The Informix Database Gateway log. |
|
ing.log |
Connection |
The Ingres Database Gateway log. |
|
pos.log |
Connection |
The PostgreSQL Database Gateway log. |
|
jdb.log |
Connection |
The JDBC Driver Database Gateway log. |
Custom Logs
The Recital/4GL USERLOG() function can be used to log information to a user-specific log file for debugging or audit trail purposes. For full information on this function, please see the USERLOG() documentation.
Enabling Log Files
For instructions on enabling log files for individual products, please follow these links:
- Recital Server for Windows
- Recital Server for Linux
- Recital Server for UNIX
- Recital Server for OpenVMS
- Recital for Linux
- Recital for UNIX
- Recital for OpenVMS
Enabling Log Files: Recital Server for Windows
To enable the system log file for the Recital Universal Application Server for Windows, include the following command in the UAS\config.db file:
set syslogging on
The Recital Server Manager System Logging tab allows for the viewing and resetting of the System log.
Section
Item
Description
System Logging
DateTime
Date and time stamp of the action.
Name
Login name used by connection
Action
Action logged: Login, Logoff, Errot
Details
Details of action
Buttons
Purge
Allows the log file to be reset
Refresh
Refreshes the display
To set up Client/Server logging, use the Recital Server Manager Settings tab to update the server's Registry entries:
The following Log file settings can be configured:
|
Item |
Description |
|---|---|
|
Log files Directory Path |
Enter the directory in which log files will be created. The default is the UAS\log directory. |
|
Enabled |
Check to enable log file creation. |
|
Versions |
Check to enable log file versioning. |
|
Listener |
Click to view the current port listener log file |
|
Server |
Click to view the current netserver log file |
|
Purge |
Click to purge all log files |
Enabling Log Files: Recital Server for Linux
To enable the system log file for the Recital Server for Linux, include the following command in the conf/config.db file:
set syslogging on
To set up Client/Server logging, the Recital Server can be started with the 'logging' parameter, in which case, all relevant logging will take place.
# service startup logging<
Alternatively, one or more of the following environment variables can be added to the dbserver.conf file or set at the Operating System prompt. The Recital Server must be restarted before environment variable changes will be recognized. Each environment variable should be set to the name of a log file.
Environment Variable
Logs Activity of...
UASLOG_PORT
Port Server (db_rsiserver)
UASLOG_NET
(Net) Server (db_netserver)
UASLOG_ORA
Oracle Server (db_oraserver)
UASLOG_INF
Informix Server (db_infserver)
UASLOG_ING
Ingres Server (db_ingserver)
UASLOG_JDB
JDBC Server (db_jdbserver)
UASLOG_REC
Recital Server (db_recserver)
Extract from recital.conf:
UASLOG_PORT="port.log" ; export UASLOG_PORT UASLOG_NET="net.log" ; export UASLOG_NET UASLOG_ORA="ora.log" ; export UASLOG_ORA UASLOG_INF="inf.log" ; export UASLOG_INF UASLOG_ING="ing.log" ; export UASLOG_ING UASLOG_JDB="jdb.log" ; export UASLOG_JDB UASLOG_REC="rec.log" ; export UASLOG_REC
DB_LOGDIR
If the environment variable DB_LOGDIR is set to an existing directory, all log files will be written to this directory. If not, the log files will be created in the bin directory.
DB_LOGDIR is set in the conf/recital.conf file. By default it is set to the log directory:
DB_LOGDIR=${ROI_ROOT}log/ ; export DB_LOGDIR
DB_LOGVER
If the environment variable DB_LOGVER is greater than 0, version numbers are added to the file names. For example, the activity of the first Net Server process will be logged to net.log, the second to net001.log, the third to net002.log etc. up to the maximum value of DB_LOGVER.
DB_LOGVER is set in the conf/recital.conf file:
DB_LOGVER=10; export DB_LOGVER
Enabling Log Files: Recital Server for UNIX
To enable the system log file for the Recital Server for UNIX, include the following command in the conf/config.db file:
set syslogging on
To set up Client/Server logging, the Recital Server can be started with the 'logging' parameter, in which case, all relevant logging will take place.
# service startup logging
Alternatively, one or more of the following environment variables can be added to the <em>dbserver.conf</em> file or set at the Operating System prompt. The Recital Server must be restarted before environment variable changes will be recognized. Each environment variable should be set to the name of a log file.
Environment Variable
Logs Activity of...
UASLOG_PORT
Port Server (db_rsiserver)
UASLOG_NET
(Net) Server (db_netserver)
UASLOG_ORA
Oracle Server (db_oraserver)
UASLOG_INF
Informix Server (db_infserver)
UASLOG_ING
Ingres Server (db_ingserver)
UASLOG_JDB
JDBC Server (db_jdbserver)
UASLOG_REC
Recital Server (db_recserver)
Extract from recital.conf:
UASLOG_PORT="port.log" ; export UASLOG_PORT UASLOG_NET="net.log" ; export UASLOG_NET UASLOG_ORA="ora.log" ; export UASLOG_ORA UASLOG_INF="inf.log" ; export UASLOG_INF UASLOG_ING="ing.log" ; export UASLOG_ING UASLOG_JDB="jdb.log" ; export UASLOG_JDB UASLOG_REC="rec.log" ; export UASLOG_REC
DB_LOGDIR
If the environment variable DB_LOGDIR is set to an existing directory, all log files will be written to this directory. If not, the log files will be created in the bin directory.
DB_LOGDIR is set in the conf/recital.conf file. By default it is set to the log directory:
DB_LOGDIR=${DB_ROOT}log/ ; export DB_LOGDIR
DB_LOGVER
If the environment variable DB_LOGVER is greater than 0, version numbers are added to the file names. For example, the activity of the first Net Server process will be logged to net.log, the second to net001.log, the third to net002.log etc. up to the maximum value of DB_LOGVER.
DB_LOGVER is set in the conf/recital.conf file:
DB_LOGVER=10; export DB_LOGVER
Enabling Log Files: Recital Universal Application Server for OpenVMS
To enable the system log file for the Recital Universal Application Server for OpenVMS, include the following command in the db_uas:config.db file:
set syslogging on
To set up Client/Server logging, one or more of the following symbols can be added to the <em>db_uas:login.com</em> file. The Recital Server must be restarted before symbol changes will be recognized. Each symbol should be set to the name of a log file.
Symbol
Logs Activity of…
UASLOG_PORT
Port Server (db_rsiserver)
UASLOG_NET
(Net) Server (db_netserver)
UASLOG_ORA
Oracle Server (db_oraserver)
UASLOG_INF
Informix Server (db_infserver)
UASLOG_ING
Ingres Server (db_ingserver)
UASLOG_JDB
JDBC Server (db_jdbserver)
UASLOG_REC
Recital Server (db_recserver)
Extract from db_uas:login.com
$ uaslog_port :== port.log $ uaslog_net :== net.log $ uaslog_ora :== ora.log $ uaslog_inf :== inf.log $ uaslog_ing :== ing.log $ uaslog_jdb :== jdb.log $ uaslog_rec :== rec.log
DB_LOGDIR
If the symbol DB_LOGDIR is set to an existing directory, all log files will be written to this directory. If not, the log files will be created in the UAS directory.
DB_LOGDIR is set in the db_uas:login.com file. By default it is set to the UAS.log] directory:
$db_logdir :== 'db_root'.log] ! system logging directory
DB_LOGVER
If the symbol DB_LOGVER is enabled, version numbers are added to the file names. For example, the activity of the first Net Server process will be logged to net.log, the second to net001.log, the third to net002.log etc.
DB_LOGVER is set in the db_uas:login.com file:
$db_logver :== true ! enable multiple log files
Enabling Log Files: Recital for Linux
To enable the system log file for Recital for Linux, include the following command in the conf/config.db file:
set syslogging on
Enabling Log Files: Recital for UNIX
To enable the system log file for Recital for UNIX, include the following command in the conf/config.db file:
set syslogging on
Enabling Log Files: Recital for OpenVMS
To enable the system log file for Recital for OpenVMS, include the following command in the db_ovd:config.db file:
set syslogging on
In Brief
- Log files provide important information to aid problem resolution, but they are also an overhead, so logging should only be enabled when required, not in normal production operation.
- The System log provides a system-wide view of logins, exits and error codes.
- The System log can be viewed in table format via the SYSLOGGING System Table.
- The System log is enabled using the SET SYSLOGGING ON Recital/4GL command in the conf/config.db file.
- Client/Server logs provide detailed information on client/server requests and responses.
- Client/Server logs are enabled using environment variables, symbols or Registry entries or by specifying the 'logging' parameter when starting the Recital Server.
- The location of log files is determined by the DB_LOGDIR setting.
- Versioning of log files is determined by the DB_LOGVER setting.
We are pleased to announce the release of Recital 10.0.3.
Here is a brief list of features and functionality that you will find in the 10.0.3 release.
- New Commands:
- SET TMPNAMPATH ON|OFF
- REMOVE TABLE - New Functions:
- CURSORGETPROP()
- CURSORSETPROP()
- CURVAL()
- GETFLDSTATE()
- OLDVAL()
- TABLEREVERT()
- TABLEUPDATE()
- SETFLDSTATE() - Enhanced Functions:
- TMPNAM() - additional parameter to specify the return of basename only
- MAILATTACH() - parameter changed from array to filename to allow directory and file extension to be specified - Enhancements:
- DO level increased from 32 to 64. - Fixes:
- Delay exiting Recital after SYS(3) or SYS(2015)
- SET SOFTSEEK issue when search key above first record in index
- Compilation error with REPLACE command after UDF call
- FETCH INTO memvars error
- END TRANSACTION at command prompt error
- ROLLBACK locking error
- Linux ODBC Driver undefined symbol error
- RELEASE variable with same name as variable in calling program issue
- SQLCODE() issue on non-gateway data access
- Issuing two SQLEXEC() calls error
- LASTSEQNO() in workareas > 1 error
- SET RELATION to detail table in workarea 1 issue
- LIST STATUS on empty table delay
- SET AUTOCATALOG alias entries error
- ADD OBJECT in DEFINE CLASS error
- DEACTIVATE WINDOW error
- SORT error
- Other reported bugs
Recital is a dynamic programming language with an embedded high performance database engine particularly well suited for the development and deployment of high transaction throughput applications.
The Recital database engine is not a standalone process with which the application program communicates. Instead, the Recital database is an integral part of any applications developed in Recital.
Recital implements most of the SQL-99 standard for SQL, but also provides lower level navigational data access for performing high transaction throughput. It is the choice of the application developer whether to use SQL, navigational data access, or a combination of both depending upon the type of application being developed.
The Recital database engine, although operating as an embedded database in the user process, multiple users and other background processes may access the same data concurrently. Read accesses are satisfied in parallel. Recital uses automatic record level locking when performing database updates. This provides for a high degree of database concurrency and superior application performance and differentiates the Recital database from other embeddable databases such as sqlite that locks the entire database file during writing.
Key features of the Recital scripting language include:
- High performance database application scripting language
- Modern object-oriented language features
- Easy to learn, easy to use
- Fast, just-in-time compiled
- Loosely-typed
- Garbage collected
- Static arrays, Associative arrays and objects
- Develop desktop or web applications
- Cross-platform support
- Extensive built-in functions
- Superb built-in SQL command integration
- Navigational data access for the most demanding applications
- Scripting language is upward compatible with FoxPRO
Key features of the Recital database include:
- A broad subset of ANSI SQL 99, as well as extensions
- Cross-platform support
- Stored procedures
- Triggers
- Cursors
- Updatable Views
- System Tables
- Query caching
- Sub-SELECTs (i.e. nested SELECTs)
- Embedded database library
- Fault tolerant clustering support
- Chronological data versioning with database timelines
- Optional DES3 encrypted data
- Hot backup
- Client drivers for ODBC, JDBC and .NET
SE Linux is a feature of the Linux kernel that provides mandatory access control. This policy based access control system grants far greater control over the resources on a machine than standard Linux access controls such as permissions.
Many modern Linux distributions are shipping with SELinux enabled by default, Fedora 14 and Rhel 6 both install with it enabled.
When you run Recital Web on a SELinux enabled machine and navigate to the default.rsp page you will see something similar to the screen shot below.

If you launch the SELinux troubleshooter you will see the following problem.
SELinux is blocking the apache server from accessing the Recital server running on port 8001.

To manage you SELinux policy you must have the policycoreutils package group installed. The policycoreutils contains the policy core utilities that are required for basic operation of a SELinux system.
If you wish to use a GUI tool, you must install the policycoreutils-gui package.
At the command prompt execute the following:
As root
$ yum install policycoreutils
$ semanage port -a -t http_port_t -p tcp 8001
$ service recital restart
$ service httpd restart
We use the semanage command here to allow the http server access to port 8001. Once you have completed the steps detailed above you can go and navigate back to the default.rsp page in your borwser, where you will find the permission denied message is now replaced by the default.rsp page.

SELinux does a great job of restricting services and daemons so rather than simply disabling it, why not work with it!
When it comes to security, every little bit helps...
alias pwd "? default()"
alias cp "copy file "
alias mv "rename "
alias rm "erase "
alias ls "run('ls $0')"
alias ps "run('ps $0')"
alias grep "run('grep $0')"
alias cd "set default to $1"
alias cls "clear screen"
These commands can now be used inside the Recital command window just as you would use them at the linux prompt, including the ability to pipe commands together.
ls -l | grep .prg ps -elf | grep db.exeThe run() function that is used to run the shell command as specified in the alias command will capture output and display it in a text viewer. If you want to run the command and display the contents full screen, then specify true as the third parameter to the run().
run("command", true, true)
| Argument | Description |
|---|---|
| 1 | the command line to run |
| 2 | True if output should be displayed in a text area (default True) |
| 3 | True if the output should be displayed full screen (default False) |
| Macro | Description |
|---|---|
| $0 | the command line following the command name |
| $1..$n | the arguments given to the command |