Hi there I've been using Nim's foreign function interface to interface with C libs on linux with much joy for some time. I haven't run into any issues until now. I have a utmp object defined in <utmp.h>. I have a simple app that just prints out currently logged in users - similar output to say what the w command does.
type
utmpx* = object
ut_type*:ut_type # Type of record
ut_pid*:cint # PID of login process
ut_line*:array[UT_LINESIZE,char] # Device name of tty - "/dev/"
ut_id*:array[4,char] # Terminal name suffix or inittab(5) ID
ut_user*:array[UT_NAMESIZE,char] # Username
ut_host*:array[UT_HOSTSIZE,char] # Hostname for remote login, or kernel version for run-level messages
ut_session*:cint # Session ID (getsid(2))
ut_tv*:tv # Time entry was made */
ut_addr_v6*:array[4, uint32] # Internet address of remote host; IPv4 address uses just ut_addr_v6[0]
The issue appears when trying to read the ip address. The C equivalent is like so
const char *addr_string;
char buffer[INET6_ADDRSTRLEN];
if (ut->ut_addr_v6[1] || ut->ut_addr_v6[2] || ut->ut_addr_v6[3])
addr_string = inet_ntop(AF_INET6, &(ut->ut_addr_v6), buffer, sizeof(buffer));
else
addr_string = inet_ntop(AF_INET, &(ut->ut_addr_v6), buffer, sizeof(buffer));
printf("ip is %s\n",addr_string);
The nim version returns the wrong IP addresses - struggling to figure out why the difference?
var
buff: array[0..255, char]
ip:cstring
if ut.ut_addr_v6[1] == 0 and ut.ut_addr_v6[2] == 0 and ut.ut_addr_v6[3] == 0:
let ip = inet_ntop(AF_INET,ut.ut_addr_v6.addr[0].addr, buff[0].addr, buff.sizeof.int32)
else:
let ip = inet_ntop(AF_INET6, ut.ut_addr_v6.addr[0].addr, buff[0].addr, buff.sizeof.int32)
2. I tried ut.ut_addr_v6[0].addr as well. ie with the line comming down to (in my hosts case)
ip = inet_ntop(AF_INET,ut.ut_addr_v6[0].addr, buff[0].addr,buff.sizeof.int32)
Still no joy
Indeed "ut.ut_addr_v6.addr[0].addr" looks very strange, have not really an idea what it is and why it may compile.
ut.ut_addr_v6[0].addr
makes more sense, it is the address of the first byte of that array. Maybe C lib expects a pointer to this address, so you may try
addr(ut.ut_addr_v6[0].addr)
Should be easy if you understand C and have complete working C code.
Caution: Whenever you use addr() you have to ensure that the object from which address is taken remains alive as long as the address is used. For arrays located on the stack, this is not always be true.
You would think with a working C example would be a walk in the park. Still no joy. At the risk of breaking forum ettique I have a tiny who.nim that iterates over all logged in users and prints to console. The code also calls the c code that reads the IP address correctly as well as the nim version
https://send.firefox.com/download/6c74113752cf4a9b/#OBxTC24p8aKfVpC0Qg7eDA
I'm not sure but after I see in your wutilc
char buffer[INET6_ADDRSTRLEN];
char *get_from(...) {
\\....
return buffer;
}
I guess it's the problem with global variable; you should change the c api too
void *get_from(struct utmpx *ut, char* outaddr) {
}
and call it in nim
# ....
var addrbuf = alloc0(INET6_ADDRSTRLEN)
getfrom(u, addrbuf)
# ...
dealloc(addrbuf)
Judging from the c example you posted, your sizes are wrong. On my system utmpx is defined something like
utmpx* = object
ut_type*:ut_type # Type of record
ut_pid*:cint # PID of login process
ut_line*:array[UT_LINESIZE,char] # Device name of tty - "/dev/"
ut_id*:array[4,char] # Terminal name suffix or inittab(5) ID
ut_user*:array[UT_NAMESIZE,char] # Username
ut_host*:array[UT_HOSTSIZE,char] # Hostname for remote login, or kernel version for run-level messages
ut_exit*:cint # Exit status
ut_session*:cint # Session ID (getsid(2))
ut_tv*:tv # Time entry was made */
ut_addr_v6*:array[4,int32] # Internet address of remote host; IPv4 address uses just ut_addr_v6[0] */
reserved:array[20, char]
And you're calling inet_ntop with the wrong size arguments. Something like
const
UT_LINESIZE = 32
UT_NAMESIZE = 32
UT_HOSTSIZE = 256
INET6_ADDRSTRLEN = 46
proc getfromIP(ut:ptr utmpx):cstring=
var buff: array[INET6_ADDRSTRLEN, char]
result = inet_ntop(AF_INET,ut.ut_addr_v6[0].addr, buff[0].addr, INET6_ADDRSTRLEN)
should work.