Thursday | 28 MAR 2024
[ previous ]
[ next ]

Working with InterCall

Title:
Date: 2022-02-03
Tags:  

Intercall is a C library to talk to Universe and UniData through the RPC port. This library lets you be able to do things like get records from a database, get values from a record, execute TCL commands and run subroutines.

Using this library, I was able to create a node addon to let javascript communicate directly to a Universe database and run subroutines. This page will have basically everything I used. The documentation for the InterCall library is pretty good but it is a PDF and requires reading of the manual. I'm writing this post in the hopes of giving someone an easier time and a better starting grasp of how to use Intercall. Ultimately however you'll probably still need to refer back to the manual for things (but that's the fun of these things!)

You can find the manual and the example from this post in the following repo:

https://github.com/Krowemoh/intercall

This is the heavily stripped down version to get to the smallest working example. The example provided by rocket has a makefile and some extra stuff that will likely be helpful if you want to go deeper into things.

The intercall examples, header files and library can be found under /usr/uv/unishared.load/icsdk.

The core things we need to make a C program talk to universe are 3 things, we need intcall.h, u2String.h and libuvic.a.

Once we have those 3 files, we can begin working on talking to universe.

The command to compile the program is:

❯ gcc test.c libuvic.a -ldl

This will create an a.out file that we can run.

This is the contents of test.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "intcall.h"

int main(char *argc, char *argv[]) {
    char *server_name = "localhost";
    char *user_name = "username";
    char *password = "password";
    char *account = "/path/to/account";

    long code;
    long session_id = ic_universe_session(server_name, user_name, password, account, &code, NULL);

    if (code != 0) {
        fprintf(stderr, "Failed to open session. Code = %ld\n", code);

    } else {
        char subname[4] = "NIVT";
        long subname_len = 4;
        long sub_status = 0;
        long numargs = 3;

        ICSTRING arg1, arg2, arg3;

        arg1.len = 0;

        char *text = "Nivethan";
        long size = strlen(text);
        arg2.len = size;
        arg2.text = ic_calloc(&size);
        memcpy(arg2.text, text, size);

        arg3.len = 0;

        ic_subcall(subname, &subname_len, &sub_status, &numargs, &arg1, &arg2, &arg3);

        if (sub_status == 0) {
            printf("Calling: %s\n", subname);
            printf("Arg 1: %s\n", arg1.text);;

        } else {
            fprintf(stderr, "Failed to complete subroutine. Code = %ld\n", sub_status);
        }

        ic_free(arg2.text);

        ic_quit(&code);
        if (code != 0) {
            fprintf(stderr, "Failed to close session. Code = %ld\n", code);
        }
    }

    exit(0);
}

The first thing we need to do is set up a session to universe and this is done through the ic_session function. We pass in 4 variables, the host, username, password and account. In return, we will get back a status code of if the session was set up and a session id.

Now we can set up our subroutine variables such as the name, the length of the subroutine and any arguments we need to pass in.

Passing arguments involves creating variables of type ICSTRING which is a struct made up of a length and a text. We also use the intercall provided functions to allocate memory and when we go to free this memory we need to use the intercall provided functions to free it.

Intercall will dynamically resize memory depending on what Universe sends back. In the example subroutine, argument 1 is left blank as that's where I want a result to be set. Argument 2 is my name. Argument 3 is just a placeholder.

Once we have our subroutine variables ready, the next step is to call ic_subcall which will take in the subroutine name and a variable number of arguments and will then run that subroutine on the universe machine we currently have our session with.

On the other side I have a very simple subroutine called NIVT that is catalogued.

SUBROUTINE NIVT(ARG1, ARG2, ARG3)
ARG1 = "Hello, " : ARG2 : "!"

Once the subroutine runs, we will get back a status that we can check to see if our the subroutine call was successful.

Once we are done we can free any memory we allocated and quit the universe session.

This is a very simple program to run a subroutine using the intercall library and it works pretty well.