Iterating over C array in Go - c

I am trying to write bindings to a C library. The code is invoked as follows in C:
#include <stdio.h>
#include <stdlib.h>
#include <libvirt/libvirt.h>
#include <string.h>
int main(int argc, char *argv[])
{
virConnectPtr conn;
conn = virConnectOpen("qemu:///system");
if (conn == NULL) {
fprintf(stderr, "Failed to open connection to qemu:///system\n");
return 1;
}
int nparams = 0;
virNodeCPUStats* params = malloc(sizeof(virNodeCPUStats) * nparams);
memset(params, 0, sizeof(virNodeCPUStats) * nparams);
int cpuNum = VIR_NODE_CPU_STATS_ALL_CPUS;
if (virNodeGetCPUStats(conn, cpuNum, NULL, &nparams, 0) == 0 &&
nparams != 0) {
if ((params = malloc(sizeof(virNodeCPUStats) * nparams)) == NULL)
goto error;
memset(params, 0, sizeof(virNodeCPUStats) * nparams);
if (virNodeGetCPUStats(conn, cpuNum, params, &nparams, 0))
goto error;
}
int err = virNodeGetCPUStats(conn, cpuNum, params, &nparams, 0);
printf("ret code %d\n", err);
printf("%d nparams\n", nparams);
int i = 0;
for(i = 0; i < nparams; i++){
printf("%s value\n", params[i].field);
printf("%llu value\n", params[i].value);
}
fflush(stdout);
error:
virConnectClose(conn);
return 0;
}
As you see, you first call virNodeGetCPUStats to get the nparams (it returns 4 on linux) then you call with it again with proper size virNodeCPUStats array, and it will fill the values and an output like the following is shown:
ret code 0
4 nparams
kernel value
991550000000 value
user value
4830840000000 value
idle value
199124620000000 value
iowait value
695120000000 value
PASS
Upon reading the code for libvirt-go and ListAllStorageVolumes, I tried the implement the following:
func (c *VirConnection) GetNodeCPUStats(flags C.uint) ([]VirNodeCPUStats, error) {
var nparams C.int
var params *C.virNodeCPUStatsPtr
var cpuNum C.int = -1 // change with cVIR_NODE_CPU_STATS_ALL_CPUS
var stats []VirNodeCPUStats
if C.virNodeGetCPUStats(c.ptr, cpuNum, nil, &nparams, 0) == 0 {
retcode := C.virNodeGetCPUStats(c.ptr, cpuNum, (*C.virNodeCPUStats)(unsafe.Pointer(&params)), &nparams, C.uint(0))
fmt.Println("D> Retcode: ", retcode) --> outputs 0, okay
}
fmt.Println("D> params: ", params)
header := reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(params)),
Len: 4,
Cap: 4,
}
slice := *(*[]C.virNodeCPUStatsPtr)(unsafe.Pointer(&header))
for _, ptr := range slice {
stats = append(stats, VirNodeCPUStats{ptr}) //add for first
}
C.free(unsafe.Pointer(params))
return stats, nil
}
However, it gives the following error:
D> params initial: <nil>
D> Successful: virNodeGetCPUStats
D> nparams: 4
D> ret code: 0
D> params: (*libvirt._Ctype_virNodeCPUStatsPtr)(0x6c656e72656b)
D> Header Data: 119182900487531
D> before slice
D> before for
unexpected fault address 0x6c656e72656b
fatal error: fault
[signal 0xb code=0x1 addr=0x6c656e72656b pc=0x4c0c04]
goroutine 87 [running]:
runtime.throw(0x613078, 0x5)
/usr/local/go/src/runtime/panic.go:547 +0x90 fp=0xc820053c80 sp=0xc820053c68
runtime.sigpanic()
/usr/local/go/src/runtime/sigpanic_unix.go:27 +0x2ab fp=0xc820053cd0 sp=0xc820053c80
github.com/rgbkrk/libvirt-go.(*VirConnection).GetNodeCPUStats(0xc820053ef0, 0x0, 0x0, 0x0, 0x0)
/home/tugba/go/src/github.com/rgbkrk/libvirt-go/libvirt.go:879 +0xa84 fp=0xc820053ec0 sp=0xc820053cd0
github.com/rgbkrk/libvirt-go.TestGetCPUStats(0xc8200beea0)
/home/tugba/go/src/github.com/rgbkrk/libvirt-go/libvirt_test.go:833 +0x66 fp=0xc820053f58 sp=0xc820053ec0
testing.tRunner(0xc8200beea0, 0x91acf8)
/usr/local/go/src/testing/testing.go:473 +0x98 fp=0xc820053f90 sp=0xc820053f58
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:1998 +0x1 fp=0xc820053f98 sp=0xc820053f90
created by testing.RunTests
/usr/local/go/src/testing/testing.go:582 +0x892
Am I not slicing the C array properly? What is going on? What is unexpected fault and how can it be at an address? Does that mean I am using a memory I shouldn't be?

Related

Iterate C array struct in Go

package main
//#cgo LDFLAGS: -ldl -lp11
//#include <stdlib.h>
//#include <libp11.h>
import "C"
import (
"log"
"unsafe"
"fmt"
//"runtime"
//"reflect"
)
func main(){
var ctx *C.PKCS11_CTX
ctx = C.PKCS11_CTX_new()
//log.Println(ctx)
cModulePath := C.CString("/home/procondutor/libaetpkss.so.3.0.3930")
defer C.free(unsafe.Pointer(cModulePath))
rc := C.PKCS11_CTX_load(ctx, cModulePath)
if rc < 0 {
log.Println("1 erro rc menor que 0")
return
}
var slots, slot *C.PKCS11_SLOT
var keys *C.PKCS11_KEY
var nslots, nkeys uint
p := unsafe.Pointer(&nslots)
n := unsafe.Pointer(&nkeys)
//log.Println(nkeys)
rc = C.PKCS11_enumerate_slots(ctx, &slots, (*C.uint)(p))
if rc < 0 {
log.Println("2 erro rc menor que 0")
return
}
slot = C.PKCS11_find_token(ctx, slots, C.uint(nslots));
s := C.GoString(slot.manufacturer)
fmt.Printf("%+v\n", s)
cPin := C.CString("2903")
defer C.free(unsafe.Pointer(cPin))
rc = C.PKCS11_login(slot, 0, cPin);
if rc < 0 {
log.Println("3 erro rc menor que 0")
return
}
rc = C.PKCS11_enumerate_keys(slot.token, &keys, (*C.uint)(n));
if rc < 0 {
log.Println("4 erro rc menor que 0")
return
}
My problem is in the PKCS11_enumerate_keys function, in C this function returns a list according to the code below:
static void list_keys(const char *title, const PKCS11_KEY *keys,
const unsigned int nkeys) {
unsigned int i;
printf("\n%s:\n", title);
for (i = 0; i < nkeys; i++)
printf(" * %s key: %s\n",
keys[i].isPrivate ? "Private" : "Public", keys[i].label);
}
But in Go it only returns the first object.
If I try to iterate in Go, I get the following error:
invalid operation: cannot index keys (variable of type *_Ctype_struct_PKCS11_key_st)
I finally got it with the code below:
list_keys := unsafe.Slice(keys, nkeys)
for i := 0; i < int(nkeys); i++ {
z:= C.GoString(slice[i].label)
fmt.Println(z)
}

BPF tail call not called

In the following code BPF program tail_prog is not getting tail called from main_prog:
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
struct bpf_map_def SEC("maps") jump_table = {
.type = BPF_MAP_TYPE_PROG_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(__u32),
.max_entries = 8,
};
SEC("xdp")
int main_prog(struct xdp_md *ctx) {
bpf_printk("Making tail call");
bpf_tail_call(ctx, &jump_table, 0);
return XDP_PASS;
}
SEC("xdp_1")
int tail_prog(struct xdp_md *ctx) {
bpf_printk("Inside tail call");
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
I observe only the print in main_prog is printed.
I'm loading the BPF programs using Cilium's eBPF Go package. Here's the code for loading the programs and maps:
type BpfObjects struct {
MainProg *ebpf.Program `ebpf:"main_prog"`
TailProg *ebpf.Program `ebpf:"tail_prog"`
JumpTable *ebpf.Map `ebpf:"jump_table"`
}
var objects BpfObjects
spec, err := ebpf.LoadCollectionSpec("prog.o")
if err != nil {
log.Fatalln("ebpf.LoadCollectionSpec", err)
}
if err := spec.LoadAndAssign(&objects, nil); err != nil {
log.Fatalln("ebpf.LoadAndAssign", err)
}
objects.JumpTable.Update(0, objects.TailProg.FD(), ebpf.UpdateAny)
According to this, the jump table has be initialized from user space, which is what I think the last line above is supposed to do. However, I don't see any difference whether that line is there or not.
I wasn't looking at the error returned from the Update function: Update can't marshal key: encoding int: binary.Write: invalid type int. Therefore, the program array map was not updated. I changed to the following:
err = objects.JumpTable.Update(uint32(0), uint32(objects.CopyHttpHostnameProg.FD()), ebpf.UpdateAny)
if err != nil {
println("Update", err.Error())
}
If you pass 0 as the key, the size of the key is 8 bytes, which is why you have to do uint32(0), which matches the map's definition. Now the tail calls succeed.

libxml2 get xsd validation errors

I'm using xmlTextReader to process large xml files. Now i need to validate the instance against an xsd schema. The api from libxml2 is a little bit confusing, how this is done.
With my approach, im getting the validation errors in the schemaParseErrorHandler function, but without any line numbers or column numbers.
How can i get these informations?
#include <stdio.h>
#include <libxml/xmlreader.h>
#include <libxml/encoding.h>
#include <libxml/xmlwriter.h>
static void schemaParseErrorHandler(void *arg, xmlErrorPtr error)
{
fprintf( stderr, "Error at line %d, column %d\n%s",
error->line, error->int2, error->message);
*((bool*)arg) = true;
}
int main( int argc, char **argv )
{
xmlInitParser();
xmlSchemaPtr schema = NULL;
xmlSchemaParserCtxtPtr schema_parser_ctxt = NULL;
int has_schema_errors = 0;
int ret = -1;
xmlSchemaValidCtxtPtr valid_ctxt = NULL;
if ((schema_parser_ctxt = xmlSchemaNewParserCtxt("example.xsd")))
{
schema = xmlSchemaParse(schema_parser_ctxt);
xmlSchemaFreeParserCtxt(schema_parser_ctxt);
if (schema)
{
valid_ctxt = xmlSchemaNewValidCtxt(schema);
}
}
xmlTextReaderPtr reader = NULL;
reader = xmlReaderForFile(filename, RPCXmlStream::STD_ENCODING, 0);
if (reader != NULL)
{
if (valid_ctxt)
{
xmlTextReaderSchemaValidateCtxt(reader, valid_ctxt, 0);
xmlSchemaSetValidStructuredErrors(valid_ctxt, schemaParseErrorHandler, &has_schema_errors);
}
ret = xmlTextReaderRead(reader);
while (ret == 1 && !has_schema_errors)
{
//... procesing informations
ret = xmlTextReaderRead(reader);
}
}
if (ret != 0)
{
xmlErrorPtr err = xmlGetLastError();
TRACE("%s: failed to parse in line %d, col %d. Error %d: %s\n",
err->file,
err->line,
err->int2,
err->code,
err->message);
}
xmlFreeTextReader(reader);
xmlCleanupParser();
return 0;
}
Another try was to use the function
xmlTextReaderSchemaValidate(reader, "example.xsd");
instead of creating an xmlSchemaNewValidCtxt, but than the programm is crashing on the first call to xmlTextReaderRead.
So how is validation done right, so that the error informations includes line and column numbers?
So, your questions got me thinking and when I looked in the libxml2 documentation,
Structure xmlError
struct _xmlError {
int domain : What part of the library raised this er
int code : The error code, e.g. an xmlParserError
char * message : human-readable informative error messag
xmlErrorLevel level : how consequent is the error
char * file : the filename
int line : the line number if available
char * str1 : extra string information
char * str2 : extra string information
char * str3 : extra string information
int int1 : extra number information
int int2 : error column # or 0 if N/A (todo: renam
void * ctxt : the parser context if available
void * node : the node in the tree
}
where we can clearly see that the xmlErrorPtr which is returned by the function xmlGetLastError() clearly contains information about the filename and the line number and the column number.
char * file : the filename
int line : the line number if available
...
int int2 : error column
So to test if this was possible or not, here is the code that I used (basically your code with minor changes to make it run on my system):
#include <stdio.h>
#include <stdbool.h>
#include <libxml/xmlreader.h>
#include <libxml/encoding.h>
#include <libxml/xmlwriter.h>
static void schemaParseErrorHandler(void *arg, xmlErrorPtr error)
{
fprintf(stderr, "Error at line %d, column %d\n%s", error->line, error->int2, error->message);
*((bool*)arg) = true;
}
int main( int argc, char **argv )
{
xmlInitParser();
xmlSchemaPtr schema = NULL;
xmlSchemaParserCtxtPtr schema_parser_ctxt = NULL;
int has_schema_errors = 0;
int ret = -1;
xmlSchemaValidCtxtPtr valid_ctxt = NULL;
if ((schema_parser_ctxt = xmlSchemaNewParserCtxt("/home/junglefox/shiporder.xsd")))
{
schema = xmlSchemaParse(schema_parser_ctxt);
xmlSchemaFreeParserCtxt(schema_parser_ctxt);
if (schema)
{
valid_ctxt = xmlSchemaNewValidCtxt(schema);
}
}
xmlTextReaderPtr reader = NULL;
const char* filename = "/home/junglefox/shiporder.xml";
reader = xmlReaderForFile(filename, /*RPCXmlStream::STD_ENCODING,*/ NULL, 0);
if (reader != NULL)
{
if (valid_ctxt)
{
xmlTextReaderSchemaValidateCtxt(reader, valid_ctxt, 0);
xmlSchemaSetValidStructuredErrors(valid_ctxt, schemaParseErrorHandler, &has_schema_errors);
}
ret = xmlTextReaderRead(reader);
while (ret == 1 && !has_schema_errors)
{
//... procesing informations
ret = xmlTextReaderRead(reader);
}
}
if (ret != 0)
{
xmlErrorPtr err = xmlGetLastError();
fprintf(stdout, "%s: failed to parse in line %d, col %d. Error %d: %s\n",
err->file,
err->line,
err->int2,
err->code,
err->message);
}
xmlFreeTextReader(reader);
xmlCleanupParser();
return 0;
}
where, the shiporder.xml and shiporder.xsd used in that program were copied from the url and saved locally.
I compiled and ran the code like this:
junglefox#ubuntu:~$ gcc -o test_xsd main.c -I/usr/include/libxml2/ -lxml2 -L/usr/lib/x86_64-linux-gnu/
junglefox#ubuntu:~$ ./test_xsd
junglefox#ubuntu:~$
The output this time was nothing. As it should be as there were no errors.
If however now I make an intentional error in the shiporder.xml file, as shown below:
Here is the partial-snippet from the buggy shiporder.xml:
<?xml version="1.0" encoding="UTF-8"?>
...
<item>
<title>Hide your heart</title>
<quantity>1</quantity>
price>9.90</price>
</item>
</shiporder>
Notice the missing < before price!
Now I run the program again,
junglefox#ubuntu:~$ ./test_xsd
Error at line 22, column 0
Element 'item': Character content other than whitespace is not allowed because the content type is 'element-only'.
which answers your question(s):
With my approach, im getting the validation errors in the schemaParseErrorHandler function, but without any line numbers or column numbers. How can i get these informations?
and,
So how is validation done right, so that the error informations includes line and column numbers?
as the output clearly shows the line number 22 and column 0, where there was an unexpected empty space due to the missing <.

eBPF, track values longer than stack size?

I'm extending a program which take arguments of traced function and print it. Everything works fine with numeric arguments and short strings. But it's not clear how to handle with long string that longer than stack size in eBPF (which is limited with 512 byte).
In example below, string is limited by 80 bytes, of course it can be increased up to 512, but how to track more longer strings?
Example of C program with traced function which called "ameba"
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
char * ameba(char * s1);
int main(void) {
printf("%s\n", ameba("verylonglongstring...1111111111111111111111111111111111111111111111111111111111111111111111111111"));
}
char * ameba(char * s1) {
char *s;
s = (char *) malloc(128);
sleep(1);
snprintf(s, 128, "ameba: %s", s1);
return s;
}
Example of Go code
package main
import "C"
import (
"bytes"
"encoding/binary"
"fmt"
"os"
"os/signal"
"unsafe"
bpf "github.com/iovisor/gobpf/bcc"
)
const source string = `
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct ameba_event_t {
u32 pid;
char comm[TASK_COMM_LEN];
char arg1[80];
} __attribute__((packed));
BPF_PERF_OUTPUT(ameba_events);
int get_input_args(struct pt_regs *ctx) {
struct ameba_event_t event = {};
if (!PT_REGS_PARM1(ctx))
return 0;
event.pid = bpf_get_current_pid_tgid();
bpf_get_current_comm(&event.comm, sizeof(event.comm));
bpf_probe_read(&event.arg1, sizeof(event.arg1), (void *)PT_REGS_PARM1(ctx));
ameba_events.perf_submit(ctx, &event, sizeof(event));
return 0;
}
`
type amebaEvent struct {
Pid uint32
Comm [16]byte
Arg1 [80]byte
}
func main() {
m := bpf.NewModule(source, []string{})
defer m.Close()
amebaUprobe, err := m.LoadUprobe("get_input_args")
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to load get_input_args: %s\n", err)
os.Exit(1)
}
err = m.AttachUprobe("/home/lesovsky/Git/sandbox/ameba", "ameba", amebaUprobe, -1)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to attach input_args: %s\n", err)
os.Exit(1)
}
table := bpf.NewTable(m.TableId("ameba_events"), m)
channel := make(chan []byte)
perfMap, err := bpf.InitPerfMap(table, channel)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to init perf map: %s\n", err)
os.Exit(1)
}
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, os.Kill)
fmt.Printf("%10s\t%s\t%s\n", "PID", "COMMAND", "ARG1")
go func() {
var event amebaEvent
for {
data := <-channel
err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &event)
if err != nil {
fmt.Printf("failed to decode received data: %s\n", err)
continue
}
comm := (*C.char)(unsafe.Pointer(&event.Comm))
query := (*C.char)(unsafe.Pointer(&event.Query))
fmt.Printf("%10d\t%s\t%s\n", event.Pid, C.GoString(comm), C.GoString(query))
}
}()
perfMap.Start()
<-sig
perfMap.Stop()
}
I read about BPF_MAP_TYPE_PERCPU_ARRAY could help in such case, but it's not clear for me how to use it.
EDIT: Go program has been modified to using "C" and "unsafe".
I read about BPF_MAP_TYPE_PERCPU_ARRAY could help in such case, but it's not clear for me how to use it.
You're right. Peeps usually rely on per-cpu arrays to overcome stack size limitations. The following implements that sort of solution, whereby I'm using a per-cpu array to store struct ameba_event_t instead of storing it on the stack.
I think you're going to need Linux v4.18 to be able to do this (you need commit d71962f). I haven't tested the code as I don't have the right setup at hand, but can later if you run into any issue.
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct ameba_event_t {
u32 pid;
char comm[TASK_COMM_LEN];
char arg1[512];
} __attribute__((packed));
BPF_PERF_OUTPUT(ameba_events);
BPF_PERCPU_ARRAY(ameba_struct, struct ameba_event_t, 1);
int get_input_args(struct pt_regs *ctx) {
int zero = 0;
if (!PT_REGS_PARM1(ctx))
return 0;
struct ameba_event_t* event = ameba_struct.lookup(&zero);
if (!event)
return 0;
event->pid = bpf_get_current_pid_tgid();
bpf_get_current_comm(&event->comm, sizeof(event->comm));
bpf_probe_read(&event->arg1, sizeof(event->arg1), (void *)PT_REGS_PARM1(ctx));
ameba_events.perf_submit(ctx, event, sizeof(*event));
return 0;
}

C: How to access value returned by Net-SNMP GET

I apologize for the naive question, Iam new to Net-SNMP. I have tried using this simple SNMP demo app given in Net-SNMP website.
This code performs a SNMP-GET and manipulates the response to check if the value returned is a ASN_OCTET_STRING, and if yes, access the string using vars->val.string and assigned to a character pointer sp.
But Iam unable to figure out how to access this value if the type is anything other than ASN_OCTET_STRING. For example how do I take this value and, say, assign it to a variable if it is of type 'ASN_INTEGER' or 'ASN_OBJECT_ID'.
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <string.h>
#define DEMO_USE_SNMP_VERSION_3
#ifdef DEMO_USE_SNMP_VERSION_3
const char *our_v3_passphrase = "MD5Password";
#endif
int main(int argc, char ** argv)
{
netsnmp_session session, *ss;
netsnmp_pdu *pdu;
netsnmp_pdu *response;
oid anOID[MAX_OID_LEN];
size_t anOID_len;
netsnmp_variable_list *vars;
int status;
int count=1;
init_snmp("snmpdemoapp");
snmp_sess_init( &session );
session.peername = strdup("localhost:161");
#ifdef DEMO_USE_SNMP_VERSION_3
session.version=SNMP_VERSION_3;
session.securityName = strdup("user2");
session.securityNameLen = strlen(session.securityName);
session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
session.securityAuthProto = usmHMACMD5AuthProtocol;
session.securityAuthProtoLen = sizeof(usmHMACMD5AuthProtocol)/sizeof(oid);
session.securityAuthKeyLen = USM_AUTH_KU_LEN;
if (generate_Ku(session.securityAuthProto,
session.securityAuthProtoLen,
(u_char *) our_v3_passphrase, strlen(our_v3_passphrase),
session.securityAuthKey,
&session.securityAuthKeyLen) != SNMPERR_SUCCESS) {
snmp_perror(argv[0]);
snmp_log(LOG_ERR,
"Error generating Ku from authentication pass phrase. \n");
exit(1);
}
#else /* we'll use the insecure (but simplier) SNMPv1 */
session.version = SNMP_VERSION_1;
session.community = "demopublic";
session.community_len = strlen(session.community);
#endif /* SNMPv1 */
SOCK_STARTUP;
ss = snmp_open(&session);
if (!ss) {
snmp_sess_perror("ack", &session);
SOCK_CLEANUP;
exit(1);
}
pdu = snmp_pdu_create(SNMP_MSG_GET);
anOID_len = MAX_OID_LEN;
if (!snmp_parse_oid("ip.21.1.8.xx.xx.xx.xx", anOID, &anOID_len)) {
snmp_perror("ip.21.1.8.xx.xx.xx.xx");
SOCK_CLEANUP;
exit(1);
}
snmp_add_null_var(pdu, anOID, anOID_len);
status = snmp_synch_response(ss, pdu, &response);
if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) {
for(vars = response->variables; vars; vars = vars->next_variable)
print_variable(vars->name, vars->name_length, vars);
/* manipuate the information ourselves */
for(vars = response->variables; vars; vars = vars->next_variable) {
if (vars->type == ASN_OCTET_STR) {
char *sp = (char *)malloc(1 + vars->val_len);
memcpy(sp, vars->val.string, vars->val_len);
sp[vars->val_len] = '\0';
printf("value #%d is a string: %s\n", count++, sp); //Here sp now has the string - But this doesnt work when the string is for eg."HOST-RESOURCES-MIB::hrSWInstalledDate.1953 = STRING: 0-1-1,0:0:0.0"
free(sp);
}
else if(vars->type == ASN_INTEGER) {
printf("value is an Integer\n");
int ObjVal;
// How do I get the Integer value and assign it to 'ObjVal'
}
else if(vars->type == ASN_OBJECT_ID) {
printf("value is an OID\n");
// How do I get the OID and assign it to some variable
}
else if(vars->type == ASN_TIMETICKS) {
printf("value is in Timeticks\n");
// How do I get the Timeticks and assign it to some variable for further processing
}
}
} else {
if (status == STAT_SUCCESS)
fprintf(stderr, "Error in packet\nReason: %s\n",
snmp_errstring(response->errstat));
else if (status == STAT_TIMEOUT)
fprintf(stderr, "Timeout: No response from %s.\n",
session.peername);
else
snmp_sess_perror("snmpdemoapp", ss);
}
if (response)
snmp_free_pdu(response);
snmp_close(ss);
SOCK_CLEANUP;
return (0);
}
Tried vars->val.integer or vars->val.object_id, but that doesnot contain the value. What am I missing here?
My another question is, even when it is of type ASN_OCTET_STRING, when the GET reply is something like this,
HOST-RESOURCES-MIB::hrSWInstalledDate.1953 = STRING: 0-1-1,0:0:0.0
then vars->val.string doesnt have "0-1-1,0:0:0.0" as string.
Basically my question is How does the value get stored in the response structure from which I can retrieve the values?
Thanks in Advance!!
P.S: Makefile link from Net-SNMP website.
Edit1:
For Integers, i can read using *vars->val->string as pointed out by immibis. Any Ideas about how to access other datatypes?
As you can see in /usr/include/net-snmp/types.h file or similar on your system, net-snmp vars->val has the following union type:
typedef union {
long *integer;
u_char *string;
oid *objid;
u_char *bitstring;
struct counter64 *counter64;
#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
float *floatVal;
double *doubleVal;
/*
* t_union *unionVal;
*/
#endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
} netsnmp_vardata;
also *vars has val_len field, where the length of data stored.
So you can access integer as *vars->val.integer, string as pointer to u_char vars->val.string with vars->val_len chars, oid as pointer to oid vars->val.objid with vars->val_len/sizeof(oid) oid elements and so on.

Resources