Thursday | 10 OCT 2024
[ previous ]
[ next ]

Working with D3 from C

Title:
Date: 2022-03-21
Tags:  

Another installment in bringing about the javascript singularity! I want to be able to use Rocket's multivalue database, D3, from node and the first step to do that is figure out how the c bindings work and what they are. This article will be very similar to my intercall article where I lay out the most basic steps you need to take to compile C programs that work with the database.

https://nivethan.dev/devlog/working-with-intercall.html

Luckily the instructions for compiling C programs with D3 are really simple and documented. I'm actually also a big fan of the search and indexing of the site even if it looks dated.

https://www3.rocketsoftware.com/rocketd3/support/documentation/d3nt/92/refman/

With that out of the way, let's start working!

The first step is to get into D3 and run the command addbi which seems to mean add branch index. This will generate a libgmu.a file that we need to link to. I'm not sure if I can bundle libgmu file yet or if each d3 system writes it's own special version of this library.

[root@d3linux ~]# d3

/dev/pts/6: Connected to Virtual Machine 'pick0:LINUX'.


18:45:55  19 Mar 2022
Enter your user id: dm

terminal name: vt100
product name: VT100
terminal width: 79    printer width: 80
depth: 24            depth: 59
lineskip:    0
lf delay:    1
ff delay:    1
back space:  8


[1301] The Speller is off.
master dictionary: dm


3
<<< 18:45:57   --             D               --   19 Mar 2022      >>>
<<<                                                                 >>>
<<< Copyright (c) 1982-2019 Rocket Software, Inc. All rights        >>>
<<< reserved. This work is property of, and embodies trade secrets  >>>
<<< and confidential information proprietary to Rocket Software,    >>>
<<< Inc. It may not be reproduced, copied, used, disclosed,         >>>
<<< transferred adapted or modified without the express written     >>>
<<< approval of Rocket Software, Inc.                               >>>

24 Sep 2019  lx64
:addbi
Compile and archive user built-in branch table.
ar: creating libgmu.a
a - px_user.o
User built-in branch table '/root/px_user.c' rebuilt.
:

Now we have 3 files in /root. We have a libgmu.a library file, a px_user.o file and a px_user.c file. I'm not sure what those files are used for yet but we just need the library file.

Now we can create a new folder for our project and copy the library file and header files that we need to work with d3.

> mkdir d3c-test
> cd d3c-test
> sudo cp /root/libgmu.a ./
> cp /usr/lib/pick/include/CPuser.h ./
> cp /usr/lib/pick/include/CPuser1.h ./

Finally we can write some code!

#include "CPuser.h"
#include <stdio.h>
#include <stdlib.h>

int main() {
    CPSTR *user = _CP_mkstr("dm");
    CPSTR *md = _CP_mkstr("dm");
    CPSTR *machine = _CP_mkstr("pick0");
    int r;

    r = _CP_logon(machine, user, _CP_str_null, md, _CP_str_null, -1, 0);


    if (r < 0)  {
        printf("Cannot log in\n");
        exit(-2);
    }
    _CP_unix_env();

    CPSTR *pfile = _CP_mkstr("MD");
    CPSTR *pitem = _CP_mkstr("TIME");
    CPSTR *buf = _CP_str_null;
    int f = 0;

    r = _CP_open(&f, _CP_str_null, pfile);
    if (r < 0) {
        printf("Failed to read file\n");
        exit(-2);
    }

    r = _CP_read(_CP_READ, &buf, f, pitem, (int*) 0);
    if (r < 0) {
        printf("Cannot read item\n");
    }

    unsigned char *p;
    _CP_TERM(buf);      /* terminate the string for Unix */

    for (p = _CP_SADDR(buf); *p; p++)
        if (*p == 254)  *p = '\n';

    printf("%s\n", _CP_SADDR(buf));

    _CP_close(f);

    _CP_str_free(pfile);
    _CP_str_free(pitem);
    _CP_str_free(buf);

    _CP_logoff();

    return r;
}

This program will log in, read in the MD entry for time, print it to the screen and then log off. This way we will have a working base for future projects.

There seems to be a some differences between this and the intercall C functions but the core ideas all seem to be the same. We open files and get file handlers back, there are functions to open and close sessions, and there are string functions specifically to be used with multivalue. Hopefully it'll be easier this time around to wrap the functions and create the node addon.

Now that we have an example program, it's time to compile it.

> gcc -o test test.c -m64 /usr/lib/pick/gid.o /usr/lib/pick/ld.import.lx64.o -lcgm -lm -lc -lcurl -L/usr/lib/pick -L./ -Wl,--start-group -lcgm -lgm -lgmu -lCP -lCPm -lsqla -lsqln -Wl,--end-group -lTllsApi -lxdmscapi -ld3pyembed -lpam -lssl -lcrypto /usr/lib/pick/d3_validatecert.o -lpthread -ldl
/usr/lib/pick/libcgm.a(pick_init.o): In function `pick_init_pibcheck':
pick_init.c:(.text+0x4e64): warning: the `gets' function is dangerous and should not be used.

We can ignore the warnings for now but we should have an executable called test in our folder now.

> sudo ./test
[sudo] password for nivethan:
VR
39
F
dm,bp, time

Voila! We have a working example of some C code that can talk to D3. There is a fuller example and a Makefile in /usr/lib/pick folder that would also be good to look at but this is the core code.

Bug Report - I ran into an error while compiling this project with the node addon api as that requires a compiler that can compile C++17.

/opt/rh/devtoolset-10/root/usr/libexec/gcc/x86_64-redhat-linux/10/ld: /usr/lib/pick/ld.import.lx64.o: .symtab local symbol at index 3 (>= sh_info of 3)
/opt/rh/devtoolset-10/root/usr/libexec/gcc/x86_64-redhat-linux/10/ld: /usr/lib/pick/ld.import.lx64.o: error adding symbols: bad value

The code however works with gcc 4.8 so I figured it was a gcc version issue. I switched gcc to be 7.3 by installing devtoolset-7 and that did the trick. I'm guessing somewhere along the way something changed in a gcc version. Luckily gcc 7.3 compiles the example code above and the node addon api code.