The Callin Interface
InterSystems IRIS® offers a Callin interface you can use from within C programs to execute InterSystems IRIS commands and evaluate ObjectScript expressions.
The Callin interface permits a wide variety of applications. For example, you can use it to make ObjectScript available from an integrated menu or GUI. If you gather information from an external device, such as an Automatic Teller Machine or piece of laboratory equipment, the Callin interface lets you store this data in an InterSystems IRIS database. Although InterSystems IRIS currently supports only C and C++ programs, any language that uses the calling standard for that platform can invoke the Callin functions.
See Using the Callin Functions for a quick review of Callin functions. For detailed reference material on each Callin function, see the Callin Function Reference.
Setup
The Callin development environment should include the following options:
-
The Development installation package
Your system should include the components installed by the Development installation option. Existing instances of InterSystems IRIS can be updated by running the installer again:
-
In Windows, select the Setup Type: Development option during installation.
-
In UNIX® and related operating systems, select the 1) Development - Install InterSystems IRIS server and all language bindings option during installation (see “Standard InterSystems IRIS Installation Procedure” in the UNIX® and Linux section of the Installation Guide).
-
-
The %Service_CallIn service
If InterSystems IRIS has been installed with security option 2 (normal), open the Management Portal and go to System Administration > Security > Services, select %Service_CallIn, and make sure the Service Enabled box is checked.
If you installed InterSystems IRIS with security option 1 (minimal) it should already be checked.
The iris-callin.h Header File
The iris-callin.h header file defines prototypes for these functions, which allows your C compiler to test for valid parameter data types when you call these functions within your program. You can add this file to the list of #include statements in your C program:
#include "iris-callin.h"
The iris-callin.h file also contains definitions of parameter values you use in your calls, and includes various #defines that may be of use. These include operating-system–specific values, error codes, and values that determine how InterSystems IRIS behaves.
You can translate the distributed header file, iris-callin.h. However, iris-callin.h is subject to change and you must track any changes if you create a translated version of this file. InterSystems Worldwide Support Center does not handle calls about unsupported languages.
Most Callin functions return values of type int, where the return value does not exceed the capacity of a 16-bit integer. Returned values can be IRIS_SUCCESS, an InterSystems IRIS error, or a Callin interface error.
There are two types of errors:
-
InterSystems IRIS errors — The return value of an InterSystems IRIS error is a positive integer.
-
Interface errors — The return value of an interface error is 0 or a negative integer.
iris-callin.h defines symbols for all system and interface errors, including IRIS_SUCCESS (0) and IRIS_FAILURE (-1). You can translate InterSystems IRIS errors (positive integers) by making a call to the Callin function IrisErrxlate.
8-bit and Unicode String Handling
InterSystems Callin functions that operate on strings have both 8-bit and Unicode versions. These functions use a suffix character to indicate the type of string that they handle:
-
Names with an “A” suffix or no suffix at all (for example,IrisEvalA or IrisPopExStr) are versions for 8-bit character strings.
-
Names with a “W” suffix (for example,IrisEvalW or IrisPopExStrW) are versions for Unicode character strings on platforms that use 2–byte Unicode characters.
-
Names with an “H” suffix (for example,IrisEvalH or IrisPopExStrH) are versions for Unicode character strings on platforms that use 4–byte Unicode characters.
For best performance, use the kind of string native to your installed version of InterSystems IRIS.
8-bit String Data Types
InterSystems IRIS supports the following data types that use local 8-bit string encoding:
-
IRIS_ASTR — counted string of 8-bit characters
-
IRIS_ASTRP — Pointer to an 8-bit counted string
The type definition for these is:
#define IRIS_MAXSTRLEN 32767
typedef struct {
unsigned short len;
Callin_char_t str[IRIS_MAXSTRLEN];
} IRIS_ASTR, *IRIS_ASTRP;
The IRIS_ASTR and IRIS_ASTRP structures contain two elements:
-
len — An integer. When used as input, this element specifies the actual length of the string whose value is supplied in the str element. When used as output, this element specifies the maximum allowable length for the str element; upon return, this is replaced by the actual length of str.
-
str — A input or output string.
IRIS_MAXSTRLEN is the maximum length of a string that is accepted or returned. A parameter string need not be of length IRIS_MAXSTRLEN nor does that much space have to be allocated in the program.
2–byte Unicode Data Types
InterSystems IRIS supports the following Unicode-related data types on platforms that use 2–byte Unicode characters:
-
IRISWSTR — Unicode counted string
-
IRISWSTRP — Pointer to Unicode counted string
The type definition for these is:
typedef struct {
unsigned short len;
unsigned short str[IRIS_MAXSTRLEN];
} IRISWSTR, *IRISWSTRP;
The IRISWSTR and IRISWSTRP structures contain two elements:
-
len — An integer. When used as input, this element specifies the actual length of the string whose value is supplied in the str element. When used as output, this element specifies the maximum allowable length for the str element; upon return, this is replaced by the actual length of str.
-
str — A input or output string.
IRIS_MAXSTRLEN is the maximum length of a string that is accepted or returned. A parameter string need not be of length IRIS_MAXSTRLEN nor does that much space have to be allocated in the program.
On Unicode-enabled versions of InterSystems IRIS, there is also the data type IRIS_WSTRING, which represents the native string type on 2–byte platforms. IrisType returns this type. Also, IrisConvert can specify IRIS_WSTRING as the data type for the return value; if this type is requested, the result is passed back as a counted Unicode string in a IRISWSTR buffer.
4–byte Unicode Data Types
InterSystems IRIS supports the following Unicode-related data types on platforms that use 4–byte Unicode characters:
-
IRISHSTR — Extended Unicode counted string
-
IRISHSTRP — Pointer to Extended Unicode counted string
The type definition for these is:
typedef struct {
unsigned int len;
wchar_t str[IRIS_MAXSTRLEN];
} IRISHSTR, *IRISHSTRP;
The IRISHSTR and IRISHSTRP structures contain two elements:
-
len — An integer. When used as input, this element specifies the actual length of the string whose value is supplied in the str element. When used as output, this element specifies the maximum allowable length for the str element; upon return, this is replaced by the actual length of str.
-
str — A input or output string.
IRIS_MAXSTRLEN is the maximum length of a string that is accepted or returned. A parameter string need not be of length IRIS_MAXSTRLEN nor does that much space have to be allocated in the program.
On Unicode-enabled versions of InterSystems IRIS, there is also the data type IRIS_HSTRING, which represents the native string type on 4–byte platforms. IrisType returns this type. Also, IrisConvert can specify IRIS_HSTRING as the data type for the return value; if this type is requested, the result is passed back as a counted Unicode string in a IRISHSTR buffer.
Because Unicode-enabled InterSystems IRIS uses only 2-byte characters, these strings are converted to UTF-16 when coming into InterSystems IRIS and from UTF-16 to 4-byte Unicode when going out from InterSystems IRIS. The $W family of functions (for example, $WASCII() and $WCHAR()) can be used in InterSystems IRIS code to work with these strings.
System-neutral Symbol Definitions
The allowed inputs and outputs of some functions vary depending on whether they are running on an 8-bit system or a Unicode system. For many of the “A” (ASCII) functions, the arguments are defined as accepting a IRISSTR, IRIS_STR, IRISSTRP, or IRIS_STRP type. These symbol definitions (without the “A” , “W”, or “H”) can conditionally be associated with either the 8-bit or Unicode names, depending on whether the symbols IRIS_UNICODE and IRIS_WCHART are defined at compile time. This way, you can write source code with neutral symbols that works with either local 8-bit or Unicode encodings.
The following excerpt from iris-callin.h illustrates the concept:
#if defined(IRIS_UNICODE) /* Unicode character strings */
#define IRISSTR IRISWSTR
#define IRIS_STR IRISWSTR
#define IRISSTRP IRISWSTRP
#define IRIS_STRP IRISWSTRP
#define IRIS_STRING IRIS_WSTRING
#elif defined(IRIS_WCHART) /* wchar_t character strings */
#define IRISSTR IRISHSTR
#define IRIS_STR IRISHSTR
#define IRISSTRP IRISHSTRP
#define IRIS_STRP IRISHSTRP
#define IRIS_STRING IRIS_HSTRING
#else /* 8-bit character strings */
#define IRISSTR IRIS_ASTR
#define IRIS_STR IRIS_ASTR
#define IRISSTRP IRIS_ASTRP
#define IRIS_STRP IRIS_ASTRP
#define IRIS_STRING IRIS_ASTRING
#endif
Using InterSystems Security Functions
Two functions are provided for working with InterSystems IRIS passwords:
-
IrisSecureStart — Similar to IrisStart, but with additional parameters for password authentication. The IrisStart function is now deprecated. If used, it will behave as if IrisSecureStart has been called with NULL for Username, Password, and ExeName. You cannot use IrisStart if you need to use some form of password authentication.
-
IrisChangePassword — This function will change the user's password if they are using InterSystems authentication (it is not valid for LDAP/DELEGATED/Kerberos etc.). It must be called before a Callin session is initialized.
There are IrisSecureStart and IrisChangePassword functions for ASCII "A", Unicode "W", and Unicode "H" installs. The new functions either narrow, widen or "use as is" the passed in parameters, store them in the new Callin data area, then eventually call the IrisStart entry point.
IrisStart and IrisSecureStart pin and pout parameters can be passed as NULL, which indicates that the platform's default input and output device should be used.
Using Callin with Multithreading
InterSystems IRIS has been enhanced so that Callin can be used by threaded programs running under some versions of Windows and UNIX® (see “Other Supported Features” in the InterSystems Supported Platforms document for this release for a list). A threaded application must link against libirisdbt.so or irisdbt.lib.
A program can spawn multiple threads (pthreads in a UNIX® environment) and each thread can establish a separate connection to InterSystems IRIS by calling IrisSecureStart. Threads may not share a single connection to InterSystems IRIS; each thread which wants to use InterSystems IRIS must call IrisSecureStart. If a thread attempts to use a Callin function and it has not called IrisSecureStart, a IRIS_NOCON error is returned.
If IrisSecureStart is being used to specify credentials as part of the login, each thread must call IrisSecureStart and provide the correct username/password for the connection, since credentials are not shared between the threads. There is a performance penalty within InterSystems IRIS using threads because of the extra code the C compiler has to generate to access thread local storage (which uses direct memory references in non-threaded builds).
Threads and UNIX® Signal Handling
On UNIX®, InterSystems IRIS uses a number of signals. If your application uses the same signals, you should be aware of how InterSystems IRIS deals with them. All signals have a default action specified by the OS. Applications may choose to leave the default action, or can choose to handle or ignore the signal. If the signal is handled, the application may further select which threads will block the signal and which threads will receive the signal. Some signals cannot be blocked, ignored, or handled. Since the default action for many signals is to halt the process, leaving the default action in place is not an option. The following signals cannot be caught or ignored, and terminate the process:
SIGNAL | DISPOSITION |
---|---|
SIGKILL | terminate process immediately |
SIGSTOP | stop process for later resumption |
The actions that an application establishes for each signal are process-wide. Whether or not the signal can be delivered to each thread is thread-specific. Each thread may specify how it will deal with signals, independently of other threads. One thread may block all signals, while another thread may allow all signals to be sent to that thread. What happens when a signal is sent to the thread depends on the process-wide handling established for that signal.
Signal Processing
InterSystems IRIS integrates with application signal handling by saving application handlers and signal masks, then restoring them at the appropriate time. Signals are processed in the following ways:
InterSystems IRIS installs its own signal handler for all generated signals. It saves the current (application) signal handler. If the thread catches a generated signal, the signal handler disconnects the thread from InterSystems IRIS, calls the applications signal handling function (if any), then does pthread_exit.
Since signal handlers are process-wide, threads not connected to InterSystems IRIS will also go into the signal handler. If InterSystems IRIS detects that the thread is not connected, it calls the application handler and then does pthread_exit.
InterSystems IRIS establishes signal handlers for all synchronous signals, and unblocks these signals for each thread when the thread connects to InterSystems IRIS (see “Synchronous Signals” for details).
InterSystems IRIS handles all asynchronous signals that would terminate the process (see “Asynchronous Signals” for details).
The system saves the signal state when the first thread connects to it. When the last thread disconnects, InterSystems IRIS restores the signal state for every signal that it has handled.
The thread signal mask is saved on connect, and restored when the thread disconnects.
Synchronous Signals
Synchronous signals are generated by the application itself (for example, SIGSEGV). InterSystems IRIS establishes signal handlers for all synchronous signals, and unblocks these signals for each thread when it connects to InterSystems IRIS.
Synchronous signals are caught by the thread that generated the signal. If the application has not specified a handler for a signal it has generated (for example, SIGSEGV), or if the thread has blocked the signal, then the OS will halt the entire process. If the thread enters the signal handler, that thread may exit cleanly (via pthread_exit) with no impact to any other thread. If a thread attempts to return from the handler, the OS will halt the entire process. The following signals cause thread termination:
SIGNAL | DISPOSITION |
---|---|
SIGABRT | process abort signal |
SIGBUS | bus error |
SIGEMT | EMT instruction |
SIGFPE | floating point exception |
SIGILL | illegal instruction |
SIGSEGV | access violation |
SIGSYS | bad argument to system call |
SIGTRAP | trace trap |
SIGXCPU | CPU time limit exceeded (setrlimit) |
Asynchronous signals
Asynchronous signals are generated outside the application (for example, SIGALRM, SIGINT, and SIGTERM). InterSystems IRIS handles all asynchronous signals that would terminate the process.
Asynchronous signals may be caught by any thread that has not blocked the signal. The system chooses which thread to use. Any signal whose default action is to cause the process to exit must be handled, with at least one thread eligible to receive it, or else it must be specifically ignored.
The application must establish a signal handler for those signals it wants to handle, and must start a thread that does not block those signals. That thread will then be the only one eligible to receive the signal and handle it. Both the handler and the eligible thread must exist before the application makes its first call to IrisStart. On the first call to IrisStart, the following actions are performed for all asynchronous signals that would terminate the process:
-
InterSystems IRIS looks for a handler for these signals. If a handler is found, InterSystems IRIS leaves it in place. Otherwise, it sets the signal to SIG_IGN (ignore the signal).
-
InterSystems IRIS blocks all of these signals for connected threads, whether or not a signal has a handler. Thus, if there is a handler, only a thread that is not connected to InterSystems IRIS can catch the signal.
The following signals are affected by this process:
SIGNAL | DISPOSITION |
---|---|
SIGALRM | timer |
SIGCHLD | blocked by threads |
SIGDANGER | ignore if unhandled |
SIGHUP | ignore if unhandled |
SIGINT | ignore if unhandled |
SIGPIPE | ignore if unhandled |
SIGQUIT | ignore if unhandled |
SIGTERM | If SIGTERM is unhandled, InterSystems IRIS will handle it. On receipt of a SIGTERM signal, the InterSystems IRIS handler will disconnect all threads and no new connections will be permitted. Handlers for SIGTERM are not stacked. |
SIGUSR1 | inter-process communication |
SIGUSR2 | inter-process communication |
SIGVTALRM | virtual timer |
SIGXFSZ | InterSystems IRIS asynchronous thread rundown |
Callin Programming Tips
Topics in this section include:
Tips for All Callin Programs
Your external program must follow certain rules to avoid corrupting InterSystems IRIS data structures, which can cause a system hang.
-
Limits on the number of open files
Your program must ensure that it does not open so many files that it prevents InterSystems IRIS from opening the number of databases or other files it expects to be able to. Normally, InterSystems IRIS looks up the user's open file quota and reserves a certain number of files for opening databases, allocating the rest for the Open command. Depending on the quota, InterSystems IRIS expects to have between 6 and 30 InterSystems IRIS database files open simultaneously, and from 0 to 36 files open with the Open command.
-
Maximum Directory Length for Callin Applications
The directory containing any Callin application must have a full path that uses fewer than 232 characters. For example, if an application is in the C:\IrisApps\Accounting\AccountsPayable\ directory, this has 40 characters in it and is therefore valid.
-
Call IrisEnd after IrisStart before halting
If your connection was established by a call to IrisStart, then you must call IrisEnd when you are done with the connection. You can make as many Callin function calls in between as you wish.
You must call IrisEnd even if the connection was broken. The connection can be broken by a call to IrisAbort with the RESJOB parameter.
IrisEnd performs cleanup operations which are necessary to prepare for another call to IrisStart. Calling IrisStart again without calling IrisEnd (assuming a broken connection) will return the code IRIS_CONBROKEN.
-
Wait until ObjectScript is done before exiting
If you are going to exit your program, you must be certain ObjectScript has completed any outstanding request. Use the Callin function IrisContext to determine whether you are within ObjectScript. This call is particularly important in exit handlers and Ctrl-C or Ctrl-Y handlers. If IrisContext returns a non-zero value, you can invoke IrisAbort.
-
Maintaining Margins in Callin Sessions
While you can set the margin within a Callin session, the margin setting is only maintained for the rest of the current command line. If a program (as with direct mode) includes the line:
:Use 0:10 Write x
the margin of 10 is established for the duration of the command line.
Certain calls affect the command line and therefore its margin. These are the calls are annotated as "calls into InterSystems IRIS" in the function descriptions.
-
Avoid signal handling when using IrisStart()
IrisStart sets handlers for various signals, which may conflict with signal handlers set by the calling application.
Tips for Windows
These tips apply only to Windows.
-
Limitations on building Callin applications using the iris shared library (irisdb.dll)
When Callin applications are built using the shared library irisdb.dll, users who have large global buffer pools may see the Callin fail to initialize (in IrisStart) with an error:
<InterSystems IRIS Startup Error: Mapping shared memory (203)>
The explanation for this lies in the behavior of system DLLs loading in Windows. Applications coded in the Win 32 API or with the Microsoft Foundation Classes (the chief libraries that support Microsoft Visual C++ development) need to have the OS load the DLLs for that Windows code as soon as they initialize. These DLLs get loaded from the top of virtual storage (higher addresses), reducing the amount of space left for the heap. On most systems, there are also a number of other DLLs (for example, DLLs supporting the display graphics) that load automatically with each Windows process at locations well above the bottom of the virtual storage. These DLLs have a tendency to request a specific address space, most commonly 0X10000000 (256MB), chopping off a few hundred megabytes of contiguous memory at the bottom of virtual memory. The result may be that there is insufficient virtual memory space in the Callin executable in which to map the InterSystems IRIS shared memory segment.
Tips for UNIX® and Linux
These tips apply only to UNIX® and Linux.
-
Do not disable interrupt delivery on UNIX®
UNIX® uses interrupts. Do not prevent delivery of interrupts.
-
Avoid using reserved signals
On UNIX®, InterSystems IRIS uses a number of signals. If possible, application programs linked with InterSystems IRIS should avoid using the following reserved signals:
SIGABRT SIGDANGER SIGILL SIGQUIT SIGTERM SIGVTALRM SIGALRM SIGEMT SIGINT SIGSTOP SIGTRAP SIGXCPU SIGBUS SIGFPE SIGKILL SIGSEGV SIGUSR1 SIGXFSZ SIGCHLD SIGHUP SIGPIPE SIGSYS SIGUSR2 If your application uses these signals, you should be aware of how InterSystems IRIS deals with them. See Threads and UNIX® Signal Handling for details.
Setting Permissions for Callin Executables on UNIX®
InterSystems IRIS executables, files, and resources such as shared memory and operating system messages, are owned by a user selected at installation time (the installation owner) and a group with a default name of irisusr (you can choose a different name at installation time). These files and resources are only accessible to processes that either have this user ID or belong to this group. Otherwise, attempting to connect to InterSystems IRIS results in protection errors from the operating system (usually specifying that access is denied); this occurs prior to establishing any connection with InterSystems IRIS.
A Callin program can only run if its effective group ID is irisusr. To meet this condition, one of the following must be true:
-
The program is run by a user in the irisusr group (or an alternate run-as group if it was changed from irisusr to something else).
-
The program sets its effective user or group by manipulating its uid or gid file permissions (using the UNIX® chgrp and chmod commands).