Access to OS libraries (e.g. the cryptography library) from YottaDB code.
Chris Munt cmunt@mgateway.com
23 June 2023, MGateway Ltd http://www.mgateway.com
- Current Release: Version: 1.3; Revision 7.
- Release Notes can be found at the end of this document.
Contents
- Overview
- Pre-requisites
- Installing mg_pwind
- Invocation of mg_pwind functions
- Cryptographic functions
- Wait and Signal functions
- Accessing InterSystems databases
- Accessing InterSystems classes
- Accessing InterSystems transactions
- Accessing InterSystems long strings
- For the future
- License
The YottaDB Process Window (mg_pwind) library is an Open Source solution to provide easy access to functionality contained in external shared libraries.
The mg_pwind library provides access to the cryptographic functions commonly used in web application development. To provide this functionality, mg_pwind makes use of the OpenSSL cryptography library: libcrypto.so.
Also included are functions to access data and functionality held in InterSystems databases.
The OpenSSL libraries:
https://www.openssl.org/
These libraries are pre-installed on most Linux systems. In order to build mg_pwind.so the OpenSSL development files will need to be installed. For example:
apt-get install libssl-dev
Working in the /src directory of the distribution, edit the following two files to reflect the layout of your YottaDB installation. The instructions given here assume a standard 'out of the box' installation of YottaDB (version 1.30) deployed in the following location:
/usr/local/lib/yottadb/r130
Assuming that you will install mg_pwind in this directory, first edit the following line in Makefile to reflect this installation location:
MGYDBDIR=/usr/local/lib/yottadb/r130
Now edit the top line of the YottaDB interface file mg_pwind.xc to give the full name and path to the mg_pwind.so library:
/usr/local/lib/yottadb/r130/mg_pwind.so
Finally, build and install the mg_pwind library:
make
make install
You should now see the mg_pwind library and its associated interface file installed in the YottaDB directory:
/usr/local/lib/yottadb/r130/mg_pwind.so
/usr/local/lib/yottadb/r130/mg_pwind.xc
Before invoking mg\pwind functions the following environment variable (ydb_xc_pwind) must be set before starting YottaDB processes:
export ydb_xc_pwind=/usr/local/lib/yottadb/r130/mg_pwind.xc
Of course, modify the path to suit your own installation.
When calling mg_pwind functions be sure to pass output variables by reference (i.e. preceded by a period character).
set status=$&pwind.version(.<version>)
Example:
set status=$&pwind.version(.vers)
write !,"version: ",vers
Some of the mg_pwind functions will return an error variable. Success of the operation is indicated by this variable being returned as an empty string (""). All functions will return a status code which will be returned as zero if the function completes successfully. If the function call is not successful then a non-zero status code will be returned, a M exception will be thrown and the corresponding error message may be retrieved by the error function.
set status=$&pwind.error(.<error>)
Example:
new $ztrap set $ztrap="zgoto "_$zlevel_":error"
set status=$&pwind.sslversion(.vers)
write !,"SSL version: ",vers
quit
error ; error
set status=$&pwind.error(.error)
write !,"error: ",error
quit
Some functions in this section include a mode flag. Set this flag as follows:
- mode=0: The default. Return the raw hash or HMAC value.
- mode=1: Return the hash or HMAC value as B64 encoded.
- mode=2: Return the hash or HMAC value as a string of HEX values.
It will not be necessary to use this function for most systems as the libcrypto.so library is usually readily available from the OS known directories for shared libraries. However, if you need to specify an alternative location for the libcrypto.so library then use this function to specify the name and full path.
set status=$&pwind.cryptlibrary(<cryptlibrary>)
Example:
set status=$&pwind.cryptlibrary("/unusual/location/libcrypto.so")
set status=$&pwind.sslversion(.<version>)
Example:
set status=$&pwind.sslversion(.vers)
write !,"SSL version: ",vers
set status=$&pwind.sha1(<data>, <mode>, .<hash>)
Example:
set status=$&pwind.sha1("my data",2,.hash)
write !,"SHA1 hash: ",hash
set status=$&pwind.sha256(<data>, <mode>, .<hash>)
Example:
set status=$&pwind.sha256("my data",2,.hash)
write !,"SHA256 hash: ",hash
set status=$&pwind.sha512(<data>, <mode>, .<hash>)
Example:
set status=$&pwind.sha512("my data",2,.hash)
write !,"SHA512 hash: ",hash
set status=$&pwind.md5(<data>, <mode>, .<hash>)
Example:
set status=$&pwind.md5("my data",2,.hash)
write !,"MD5 hash: ",hash
set status=$&pwind.hmacsha1(<key>, <data>, <mode>, .<hmac>)
Example:
set status=$&pwind.hmacsha1("my key","my data",2,.hmac)
write !,"SHA1 HMAC: ",hmac
set status=$&pwind.hmacsha256(<key>, <data>, <mode>, .<hmac>)
Example:
set status=$&pwind.hmacsha256("my key","my data",2,.hmac)
write !,"SHA256 HMAC: ",hmac
set status=$&pwind.hmacsha512(<key>, <data>, <mode>, .<hmac>)
Example:
set status=$&pwind.hmacsha512("my key","my data",2,.hmac)
write !,"SHA512 HMAC: ",hmac
set status=$&pwind.hmacmd5(<key>, <data>, <mode>, .<hmac>)
Example:
set status=$&pwind.hmacmd5("my key","my data",2,.hmac)
write !,"MD5 HMAC: ",hmac
set status=$&pwind.encodeb64(<data>, .<b64>)
Example:
set status=$&pwind.encodeb64("my data",.b64)
write !,"B64 encode: ",b64
set status=$&pwind.decodeb64(<b64>, .<data>)
Example:
set status=$&pwind.decodeb64(b64,.data)
write !,"B64 decode: ",data
set status=$&pwind.crc32(<data>, .<crc32>)
Example:
set status=$&pwind.crc32("my data",.crc32)
write !,"CRC32: ",crc32
Wait for a signal (from another process) or a time-out event:
set status=$&pwind.signalwait(.<result>,<timeout>)
timeout is specified in milliseconds. When this function returns (as a result of time-out or receiving a wake-up signal from another process), return will be set to 0 for time-out, 1 for wake-up signal received and a value of -1 indicates an error condition.
Send an interrupt signal to another process:
set status=$&pwind.signal(<process_id>)
process_id is the $Job value of the process to wake up (i.e. the process blocking on a pwind.signalwait() call.
This section describes an experimental approach to accessing data held in InterSystems databases (Cache, Ensemble and IRIS). Two connectivity modes are supported:
- High-performance in-process access to a local InterSystems database using the Cache/IRIS API.
- Network based access to a local or remote InterSystems databases via the network.
The functions described in this section will allow YottaDB programs to send updates to, and receive data from, InterSystems databases. They will also allow YottaDB programs to access the advanced facilities provided by the InterSystems databases and development environments.
set status=$&pwind.dbopen(<dbtype>, <path>, <host>, <port>, <username>, <password>, <namespace>)
- dbtype should be set to either Cache or IRIS as appropriate.
- For API-based connectivity, specify the path and leave host and port empty.
- For Network-based connectivity, leave the path empty and specify the host and port on which the %zmgsi superserver is listening.
Example using the API:
set status=$&pwind.dbopen("Cache","/opt/cache20181/mgr","","","_SYSTEM","SYS","USER")
Example using the network:
set status=$&pwind.dbopen("Cache","","localhost",7041,"_SYSTEM","SYS","USER")
set status=$&pwind.dbclose()
Example:
set status=$&pwind.dbclose()
set status=$&pwind.dbset(<data>, <global>, <key ...>)
Example:
set status=$&pwind.dbset("my data record", "^MyGlobal", "my key")
This is equivalent to:
set ^MyGlobal("my key")="my data record"
set status=$&pwind.dbget(.<data>, <global>, <key ...>)
Example:
set status=$&pwind.dbget(.data, "^MyGlobal", "my key")
This is equivalent to:
set data=$get(^MyGlobal("my key"))
set status=$&pwind.dbkill(<global>, <key ...>)
Example:
set status=$&pwind.dbkill("^MyGlobal", "my key")
This is equivalent to:
kill ^MyGlobal("my key")
set status=$&pwind.dborder(.<nkey>, <global>, <key ...>)
Example:
set status=$&pwind.dborder(.nkey, "^MyGlobal", "")
This is equivalent to:
set nkey=$order(^MyGlobal(""))
set status=$&pwind.dborderdata(.<nkey>, .<data>, <global>, <key ...>)
Example:
set status=$&pwind.dborderdata(.nkey,.data,"^MyGlobal", "")
This is equivalent to:
set data="",nkey=$order(^MyGlobal("")) if nkey'="" set data=$get(^MyGlobal(nkey))
set status=$&pwind.dbprevious(.<pkey>, <global>, <key ...>)
Example:
set status=$&pwind.dbprevious(.pkey, "^MyGlobal", "")
This is equivalent to:
set pkey=$order(^MyGlobal(""),-1)
set status=$&pwind.dbpreviousdata(.<nkey>, .<data>, <global>, <key ...>)
Example:
set status=$&pwind.dbpreviousdata(.nkey,.data,"^MyGlobal", "")
This is equivalent to:
set data="",nkey=$order(^MyGlobal(""),-1) if nkey'="" set data=$get(^MyGlobal(nkey))
set status=$&pwind.dbincrement(.<result>, <increment>, <global>, <key ...>)
Example:
set status=$&pwind.dbincrement(.result,0.5,"^MyGlobal", 1)
This is equivalent to:
set result=$increment(^MyGlobal(1),0.5)
set status=$&pwind.dblock(.<result>, <timeout>, <global>, <key ...>)
- The time-out value should be specified in seconds. Set to -1 for no time-out.
- result is set to 1 to indicate success or 0 for time-out.
Example:
set status=$&pwind.dblock(.result,-1,"^MyGlobal", 1)
This is equivalent to:
Lock +^MyGlobal(1)
set status=$&pwind.dbunlock(<global>, <key ...>)
Example:
set status=$&pwind.undblock("^MyGlobal", 1)
This is equivalent to:
Lock -^MyGlobal(1)
set status=$&pwind.dbfunction(.<result>, <function>, <arguments ...>)
Example:
set status=$&pwind.dbfunction(.result, "function^MyRoutine", "a", "b")
This is equivalent to:
set result=$$function^MyRoutine("a","b")
; Open a new connection to a local Cache database
set status=$&pwind.dbopen("Cache","/opt/cache20181/mgr","","","_SYSTEM","SYS","USER")
; Kill a global
set status=$&pwind.dbkill("^MyGlobal")
; Set up some new records
for n=1:1:10 set status=$&pwind.dbset("record "_n_" ("_$zh_")","^MyGlobal",n)
; Now read them all back (in order)
set key="" for set status=$&pwind.dborder(.key,"^MyGlobal",key) quit:key="" set status=$&pwind.dbget(.data,"^MyGlobal",key) write !,key," = ",data
; Read them all back (in reverse order)
set key="" for set status=$&pwind.dbprevious(.key,"^MyGlobal",key) quit:key="" set status=$&pwind.dbget(.data,"^MyGlobal",key) write !,key," = ",data
; close the database connection
set status=$&pwind.dbclose()
To illustrate these methods, the following simple class will be used:
Class User.customer Extends %Persistent
{
Property number As %Integer;
Property name As %String;
ClassMethod MyClassMethod(x As %Integer) As %Integer
{
// do some work
Quit result
}
Method MyMethod(x As %Integer) As %Integer
{
// do some work
Quit result
}
}
set status=$&pwind.dbclassmethod(.<result>, <class_name>, <method_name>, <arguments ...>)
Example:
set status=$&pwind.dbclassmethod(.result,"User.customer","MyClassMethod",3)
Example (using instance/record #1):
set status=$&pwind.dbclassmethod(.oref,"User.customer","%OpenId", 1)
set status=$&pwind.dbgetproperty(.<data>, <object_reference>, <property_name>)
Example:
set status=$&pwind.dbgetproperty(.name,oref,"name")
set status=$&pwind.dbsetproperty(<data>, <object_reference>, <property_name>)
Example:
set status=$&pwind.dbsetproperty(name,oref,"name")
set status=$&pwind.dbmethod(.<result>, <object_reference>, <method_name>), <arguments ...>)
Example:
set status=$&pwind.dbmethod(.result,oref,"MyMethod",3)
set status=$&pwind.dbcloseinstance(<object_reference>)
Example:
set status=$&pwind.dbcloseinstance(oref)
set status=$&pwind.dbtstart()
This is equivalent to:
TStart
set status=$&pwind.dbtlevel(.<tlevel>)
Example:
set status=$&pwind.dbtlevel(.tlevel)
This is equivalent to:
set tlevel=$TLevel
set status=$&pwind.dbtcommit()
This is equivalent to:
TCommit
set status=$&pwind.dbtrollback()
This is equivalent to:
TRollback
The maximum string length for InterSystems DB Servers is usually 3,641,144 Bytes whereas for YottaDB it is currently 1,048,576 Bytes. The scheme described in this section will allow mg_pwind functions to retrieve (and set) strings up to the maximum size permitted by InterSystems.
set status=$&pwind.dbgetstring(.<data>, <index>, .<chunk_no>)
- data is the next chunk of string data.
- index is the index number for the string. By convention this is -1 for data returned from a mg_pwind function. When a function returns two data items (e.g. dborderdata) then the second data item will have an index of -2. The default value for this parameter is -1.
- chunk_no is the data chunk number.
After the last chunk is returned, subsequent calls to dbgetstring will return an empty string. The dbgetstring function relates only to the previous mg_pwind retrieval operation.
Example (retrieve a large string):
set rc=$&pwind.dbget(.data,"^MyGlobal","very long string")
set dataarray(1)=data
for n=2:1 s rc=$&pwind.dbgetstring(.data) q:data="" set dataarray(n)=data
set status=$&pwind.dbsetstring(<data>, <index>, .<chunk_no>)
- data is the next chunk of string data.
- index is the index number for the string. By convention this is 0 for data and 1->n for global subscripts or arguments to functions.
- chunk_no is the data chunk number.
The dbsetstring function relates only to the next mg_pwind update operation.
Example (set a large string):
for n=1:1 quit:'$data(dataarray(n)) s rc=$&pwind.dbsetstring(dataarray(n),0)
set rc=$&pwind.dbset("","^MyGlobal","very long string")
Example (set a large string as the first argument to a function):
for n=1:1 quit:'$data(dataarray(n)) s rc=$&pwind.dbsetstring(dataarray(n),1)
set rc=$&pwind.dbfunction(.result, "function^MyRoutine", "")
Further functions (and access to other OS libraries) will be added to mg_pwind as required.
Copyright (c) 2018-2023 MGateway Ltd,
Surrey UK.
All rights reserved.
http://www.mgateway.com
Email: cmunt@mgateway.com
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
- Initial Release
- Introduce experimental network I/O layer.
- Introduce experimental access to InterSystems databases.
- Introduce experimental access to InterSystems classes.
- Introduce access to the InterSystems Global Lock command.
- Introduce access to InterSystems Transactions.
- Introduce a function to gracefully close InterSystems Object References.
- set status=$&pwind.dbclosinstance(oref)
- Introduce support for long strings through the mg_pwind interface.
- Maximum string length for YottaDB: 1,048,576 Bytes.
- Maximum string length for InterSystems databases: 3,641,144 Bytes (32,767 Bytes for older systems).
- Introduce a simple wait/signal mechanism to aid communication between YottaDB processes.
- Introduce a scheme for dealing with large strings from InterSystems IRIS and Cache.
- The maximum string length for InterSystems DB Servers is usually 3,641,144 Bytes whereas for YottaDB it is currently 1,048,576 Bytes. This enhancement will allow mg_pwind functions to retrieve (and set) strings up to the maximum size permitted by InterSystems.
- Documentation update.