From 1d188c8737f97a4882851ccd0150cd09c7f6c1e9 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 13 Nov 2013 14:36:31 -0800 Subject: [PATCH] Create devmapper_wrapper.go --- devmapper/devmapper.go | 296 +++++---------------------- devmapper/devmapper_wrapper.go | 351 +++++++++++++++++++++++++++++++++ 2 files changed, 401 insertions(+), 246 deletions(-) create mode 100644 devmapper/devmapper_wrapper.go diff --git a/devmapper/devmapper.go b/devmapper/devmapper.go index eae42247cf..b4e8118431 100644 --- a/devmapper/devmapper.go +++ b/devmapper/devmapper.go @@ -1,163 +1,11 @@ package devmapper -/* -#cgo LDFLAGS: -L. -ldevmapper -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef LOOP_CTL_GET_FREE -#define LOOP_CTL_GET_FREE 0x4C82 -#endif - -// FIXME: this could easily be rewritten in go -char* attach_loop_device(const char *filename, int *loop_fd_out) -{ - struct loop_info64 loopinfo = {0}; - struct stat st; - char buf[64]; - int i, loop_fd, fd, start_index; - char* loopname; - - - *loop_fd_out = -1; - - start_index = 0; - fd = open("/dev/loop-control", O_RDONLY); - if (fd >= 0) { - start_index = ioctl(fd, LOOP_CTL_GET_FREE); - close(fd); - - if (start_index < 0) - start_index = 0; - } - - fd = open(filename, O_RDWR); - if (fd < 0) { - perror("open"); - return NULL; - } - - loop_fd = -1; - for (i = start_index ; loop_fd < 0 ; i++ ) { - if (sprintf(buf, "/dev/loop%d", i) < 0) { - close(fd); - return NULL; - } - - if (stat(buf, &st)) { - if (!S_ISBLK(st.st_mode)) { - fprintf(stderr, "[error] Loopback device %s is not a block device.\n", buf); - } else if (errno == ENOENT) { - fprintf(stderr, "[error] There are no more loopback device available.\n"); - } else { - fprintf(stderr, "[error] Unkown error trying to stat the loopback device %s (errno: %d).\n", buf, errno); - } - close(fd); - return NULL; - } - - loop_fd = open(buf, O_RDWR); - if (loop_fd < 0 && errno == ENOENT) { - fprintf(stderr, "[error] The loopback device %s does not exists.\n", buf); - close(fd); - return NULL; - } else if (loop_fd < 0) { - fprintf(stderr, "[error] Unkown error openning the loopback device %s. (errno: %d)\n", buf, errno); - continue; - } - - if (ioctl(loop_fd, LOOP_SET_FD, (void *)(size_t)fd) < 0) { - int errsv = errno; - close(loop_fd); - loop_fd = -1; - if (errsv != EBUSY) { - close(fd); - fprintf(stderr, "cannot set up loopback device %s: %s", buf, strerror(errsv)); - return NULL; - } - continue; - } - - close(fd); - - strncpy((char*)loopinfo.lo_file_name, buf, LO_NAME_SIZE); - loopinfo.lo_offset = 0; - loopinfo.lo_flags = LO_FLAGS_AUTOCLEAR; - - if (ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo) < 0) { - perror("ioctl LOOP_SET_STATUS64"); - if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { - perror("ioctl LOOP_CLR_FD"); - } - close(loop_fd); - fprintf (stderr, "cannot set up loopback device info"); - return (NULL); - } - - loopname = strdup(buf); - if (loopname == NULL) { - close(loop_fd); - return (NULL); - } - - *loop_fd_out = loop_fd; - return (loopname); - } - - return (NULL); -} - -static int64_t get_block_size(int fd) -{ - uint64_t size; - - if (ioctl(fd, BLKGETSIZE64, &size) == -1) - return -1; - return ((int64_t)size); -} - -extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str); - -static void -log_cb(int level, const char *file, int line, - int dm_errno_or_class, const char *f, ...) -{ - char buffer[256]; - va_list ap; - - va_start(ap, f); - vsnprintf(buffer, 256, f, ap); - va_end(ap); - - DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer); -} - -static void -log_with_errno_init () -{ - dm_log_with_errno_init(log_cb); -} - -*/ -import "C" - import ( "errors" "fmt" "github.com/dotcloud/docker/utils" "os" "runtime" - "syscall" - "unsafe" ) type DevmapperLogger interface { @@ -198,6 +46,7 @@ var ( ErrTaskSetAddNode = errors.New("dm_task_set_add_node failed") ErrTaskSetRO = errors.New("dm_task_set_ro failed") ErrTaskAddTarget = errors.New("dm_task_add_target failed") + ErrGetInfo = errors.New("dm_task_get_info failed") ErrGetDriverVersion = errors.New("dm_task_get_driver_version failed") ErrAttachLoopbackDevice = errors.New("loopback mounting failed") ErrGetBlockSize = errors.New("Can't get block size") @@ -210,7 +59,7 @@ var ( type ( Task struct { - unmanaged *C.struct_dm_task + unmanaged *CDmTask } Info struct { Exists int @@ -230,167 +79,131 @@ type ( func (t *Task) destroy() { if t != nil { - C.dm_task_destroy(t.unmanaged) + DmTaskDestory(t.unmanaged) runtime.SetFinalizer(t, nil) } } func TaskCreate(tasktype TaskType) *Task { - c_task := C.dm_task_create(C.int(tasktype)) - if c_task == nil { + Ctask := DmTaskCreate(int(tasktype)) + if Ctask == nil { return nil } - task := &Task{unmanaged: c_task} + task := &Task{unmanaged: Ctask} runtime.SetFinalizer(task, (*Task).destroy) return task } func (t *Task) Run() error { - if res := C.dm_task_run(t.unmanaged); res != 1 { + if res := DmTaskRun(t.unmanaged); res != 1 { return ErrTaskRun } return nil } func (t *Task) SetName(name string) error { - c_name := C.CString(name) - defer free(c_name) - - if res := C.dm_task_set_name(t.unmanaged, c_name); res != 1 { + if res := DmTaskSetName(t.unmanaged, name); res != 1 { return ErrTaskSetName } return nil } func (t *Task) SetMessage(message string) error { - c_message := C.CString(message) - defer free(c_message) - - if res := C.dm_task_set_message(t.unmanaged, c_message); res != 1 { + if res := DmTaskSetMessage(t.unmanaged, message); res != 1 { return ErrTaskSetMessage } return nil } func (t *Task) SetSector(sector uint64) error { - if res := C.dm_task_set_sector(t.unmanaged, C.uint64_t(sector)); res != 1 { + if res := DmTaskSetSector(t.unmanaged, sector); res != 1 { return ErrTaskSetAddNode } return nil } -func (t *Task) SetCookie(cookie *uint32, flags uint16) error { - c_cookie := C.uint32_t(*cookie) - if res := C.dm_task_set_cookie(t.unmanaged, &c_cookie, C.uint16_t(flags)); res != 1 { +func (t *Task) SetCookie(cookie *uint, flags uint16) error { + if res := DmTaskSetCookie(t.unmanaged, cookie, flags); res != 1 { return ErrTaskSetAddNode } - *cookie = uint32(c_cookie) return nil } -func (t *Task) SetAddNode(add_node AddNodeType) error { - if res := C.dm_task_set_add_node(t.unmanaged, C.dm_add_node_t(add_node)); res != 1 { +func (t *Task) SetAddNode(addNode AddNodeType) error { + if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 { return ErrTaskSetAddNode } return nil } func (t *Task) SetRo() error { - if res := C.dm_task_set_ro(t.unmanaged); res != 1 { + if res := DmTaskSetRo(t.unmanaged); res != 1 { return ErrTaskSetRO } return nil } -func (t *Task) AddTarget(start uint64, size uint64, ttype string, params string) error { - c_ttype := C.CString(ttype) - defer free(c_ttype) - - c_params := C.CString(params) - defer free(c_params) - - if res := C.dm_task_add_target(t.unmanaged, C.uint64_t(start), C.uint64_t(size), c_ttype, c_params); res != 1 { +func (t *Task) AddTarget(start, size uint64, ttype, params string) error { + if res := DmTaskAddTarget(t.unmanaged, start, size, + ttype, params); res != 1 { return ErrTaskAddTarget } return nil } func (t *Task) GetDriverVersion() (string, error) { - buffer := C.CString(string(make([]byte, 128))) - defer free(buffer) - - if res := C.dm_task_get_driver_version(t.unmanaged, buffer, 128); res != 1 { + var version string + if res := DmTaskGetDriverVersion(t.unmanaged, &version); res != 1 { return "", ErrGetDriverVersion } - return C.GoString(buffer), nil + return version, nil } func (t *Task) GetInfo() (*Info, error) { - c_info := C.struct_dm_info{} - if res := C.dm_task_get_info(t.unmanaged, &c_info); res != 1 { - return nil, ErrGetDriverVersion + info := &Info{} + if res := DmTaskGetInfo(t.unmanaged, info); res != 1 { + return nil, ErrGetInfo } - return &Info{ - Exists: int(c_info.exists), - Suspended: int(c_info.suspended), - LiveTable: int(c_info.live_table), - InactiveTable: int(c_info.inactive_table), - OpenCount: int32(c_info.open_count), - EventNr: uint32(c_info.event_nr), - Major: uint32(c_info.major), - Minor: uint32(c_info.minor), - ReadOnly: int(c_info.read_only), - TargetCount: int32(c_info.target_count), - }, nil + return info, nil } -func (t *Task) GetNextTarget(next uintptr) (uintptr, uint64, uint64, string, string) { - var ( - c_start, c_length C.uint64_t - c_target_type, c_params *C.char - ) +func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64, + length uint64, targetType string, params string) { - nextp := C.dm_get_next_target(t.unmanaged, unsafe.Pointer(next), &c_start, &c_length, &c_target_type, &c_params) - return uintptr(nextp), uint64(c_start), uint64(c_length), C.GoString(c_target_type), C.GoString(c_params) + return DmGetNextTarget(t.unmanaged, next, &start, &length, + &targetType, ¶ms), + start, length, targetType, params } func AttachLoopDevice(filename string) (*os.File, error) { - c_filename := C.CString(filename) - defer free(c_filename) - - var fd C.int - res := C.attach_loop_device(c_filename, &fd) - if res == nil { - if os.Getenv("DEBUG") != "" { - C.perror(C.CString(fmt.Sprintf("[debug] Error attach_loop_device(%s, %d)", filename, int(fd)))) - } + var fd int + res := DmAttachLoopDevice(filename, &fd) + if res == "" { return nil, ErrAttachLoopbackDevice } - defer free(res) - - return os.NewFile(uintptr(fd), C.GoString(res)), nil + return os.NewFile(uintptr(fd), res), nil } func getBlockSize(fd uintptr) int { var size uint64 - if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))); err != 0 { - utils.Debugf("Error ioctl: %s", err) + if err := SysGetBlockSize(fd, &size); err != 0 { + utils.Debugf("Error ioctl (getBlockSize: %s)", err) return -1 } return int(size) } func GetBlockDeviceSize(file *os.File) (uint64, error) { - if size := C.get_block_size(C.int(file.Fd())); size == -1 { + size := DmGetBlockSize(file.Fd()) + if size == -1 { return 0, ErrGetBlockSize - } else { - return uint64(size), nil } + return uint64(size), nil } -func UdevWait(cookie uint32) error { - if res := C.dm_udev_wait(C.uint32_t(cookie)); res != 1 { +func UdevWait(cookie uint) error { + if res := DmUdevWait(cookie); res != 1 { utils.Debugf("Failed to wait on udev cookie %d", cookie) return ErrUdevWait } @@ -398,21 +211,18 @@ func UdevWait(cookie uint32) error { } func LogInitVerbose(level int) { - C.dm_log_init_verbose(C.int(level)) + DmLogInitVerbose(level) } var dmLogger DevmapperLogger = nil func logInit(logger DevmapperLogger) { dmLogger = logger - C.log_with_errno_init() + LogWithErrnoInit() } func SetDevDir(dir string) error { - c_dir := C.CString(dir) - defer free(c_dir) - - if res := C.dm_set_dev_dir(c_dir); res != 1 { + if res := DmSetDevDir(dir); res != 1 { utils.Debugf("Error dm_set_dev_dir") return ErrSetDevDir } @@ -420,13 +230,11 @@ func SetDevDir(dir string) error { } func GetLibraryVersion() (string, error) { - buffer := C.CString(string(make([]byte, 128))) - defer free(buffer) - - if res := C.dm_get_library_version(buffer, 128); res != 1 { + var version string + if res := DmGetLibraryVersion(&version); res != 1 { return "", ErrGetLibraryVersion } - return C.GoString(buffer), nil + return version, nil } // Useful helper for cleanup @@ -445,10 +253,6 @@ func RemoveDevice(name string) error { return nil } -func free(p *C.char) { - C.free(unsafe.Pointer(p)) -} - // This is the programmatic example of "dmsetup create" func createPool(poolName string, dataFile *os.File, metadataFile *os.File) error { task, err := createTask(DeviceCreate, poolName) @@ -466,7 +270,7 @@ func createPool(poolName string, dataFile *os.File, metadataFile *os.File) error return fmt.Errorf("Can't add target") } - var cookie uint32 = 0 + var cookie uint = 0 if err := task.SetCookie(&cookie, 0); err != nil { return fmt.Errorf("Can't set cookie") } @@ -564,7 +368,7 @@ func resumeDevice(name string) error { return err } - var cookie uint32 = 0 + var cookie uint = 0 if err := task.SetCookie(&cookie, 0); err != nil { return fmt.Errorf("Can't set cookie") } @@ -646,7 +450,7 @@ func activateDevice(poolName string, name string, deviceId int, size uint64) err return fmt.Errorf("Can't add node") } - var cookie uint32 = 0 + var cookie uint = 0 if err := task.SetCookie(&cookie, 0); err != nil { return fmt.Errorf("Can't set cookie") } diff --git a/devmapper/devmapper_wrapper.go b/devmapper/devmapper_wrapper.go new file mode 100644 index 0000000000..d6ec7ae7fb --- /dev/null +++ b/devmapper/devmapper_wrapper.go @@ -0,0 +1,351 @@ +package devmapper + +/* +#cgo LDFLAGS: -L. -ldevmapper +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef LOOP_CTL_GET_FREE +#define LOOP_CTL_GET_FREE 0x4C82 +#endif + +// FIXME: this could easily be rewritten in go +char* attach_loop_device(const char *filename, int *loop_fd_out) +{ + struct loop_info64 loopinfo = {0}; + struct stat st; + char buf[64]; + int i, loop_fd, fd, start_index; + char* loopname; + + + *loop_fd_out = -1; + + start_index = 0; + fd = open("/dev/loop-control", O_RDONLY); + if (fd >= 0) { + start_index = ioctl(fd, LOOP_CTL_GET_FREE); + close(fd); + + if (start_index < 0) + start_index = 0; + } + + fd = open(filename, O_RDWR); + if (fd < 0) { + perror("open"); + return NULL; + } + + loop_fd = -1; + for (i = start_index ; loop_fd < 0 ; i++ ) { + if (sprintf(buf, "/dev/loop%d", i) < 0) { + close(fd); + return NULL; + } + + if (stat(buf, &st)) { + if (!S_ISBLK(st.st_mode)) { + fprintf(stderr, "[error] Loopback device %s is not a block device.\n", buf); + } else if (errno == ENOENT) { + fprintf(stderr, "[error] There are no more loopback device available.\n"); + } else { + fprintf(stderr, "[error] Unkown error trying to stat the loopback device %s (errno: %d).\n", buf, errno); + } + close(fd); + return NULL; + } + + loop_fd = open(buf, O_RDWR); + if (loop_fd < 0 && errno == ENOENT) { + fprintf(stderr, "[error] The loopback device %s does not exists.\n", buf); + close(fd); + return NULL; + } else if (loop_fd < 0) { + fprintf(stderr, "[error] Unkown error openning the loopback device %s. (errno: %d)\n", buf, errno); + continue; + } + + if (ioctl(loop_fd, LOOP_SET_FD, (void *)(size_t)fd) < 0) { + int errsv = errno; + close(loop_fd); + loop_fd = -1; + if (errsv != EBUSY) { + close(fd); + fprintf(stderr, "cannot set up loopback device %s: %s", buf, strerror(errsv)); + return NULL; + } + continue; + } + + close(fd); + + strncpy((char*)loopinfo.lo_file_name, buf, LO_NAME_SIZE); + loopinfo.lo_offset = 0; + loopinfo.lo_flags = LO_FLAGS_AUTOCLEAR; + + if (ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo) < 0) { + perror("ioctl LOOP_SET_STATUS64"); + if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { + perror("ioctl LOOP_CLR_FD"); + } + close(loop_fd); + fprintf (stderr, "cannot set up loopback device info"); + return (NULL); + } + + loopname = strdup(buf); + if (loopname == NULL) { + close(loop_fd); + return (NULL); + } + + *loop_fd_out = loop_fd; + return (loopname); + } + + return (NULL); +} + +static int64_t get_block_size(int fd) +{ + uint64_t size; + + if (ioctl(fd, BLKGETSIZE64, &size) == -1) + return -1; + return ((int64_t)size); +} + +extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str); + +static void +log_cb(int level, const char *file, int line, + int dm_errno_or_class, const char *f, ...) +{ + char buffer[256]; + va_list ap; + + va_start(ap, f); + vsnprintf(buffer, 256, f, ap); + va_end(ap); + + DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer); +} + +static void +log_with_errno_init () +{ + dm_log_with_errno_init(log_cb); +} + +*/ +import "C" + +import ( + "syscall" + "unsafe" +) + +type ( + CDmTask C.struct_dm_task +) + +var ( + DmTaskDestory = dmTaskDestroyFct + DmTaskCreate = dmTaskCreateFct + DmTaskRun = dmTaskRunFct + DmTaskSetName = dmTaskSetNameFct + DmTaskSetMessage = dmTaskSetMessageFct + DmTaskSetSector = dmTaskSetSectorFct + DmTaskSetCookie = dmTaskSetCookieFct + DmTaskSetAddNode = dmTaskSetAddNodeFct + DmTaskSetRo = dmTaskSetRoFct + DmTaskAddTarget = dmTaskAddTargetFct + DmTaskGetDriverVersion = dmTaskGetDriverVersionFct + DmTaskGetInfo = dmTaskGetInfoFct + DmGetNextTarget = dmGetNextTargetFct + DmAttachLoopDevice = dmAttachLoopDeviceFct + SysGetBlockSize = sysGetBlockSizeFct + DmGetBlockSize = dmGetBlockSizeFct + DmUdevWait = dmUdevWaitFct + DmLogInitVerbose = dmLogInitVerboseFct + LogWithErrnoInit = logWithErrnoInitFct + DmSetDevDir = dmSetDevDirFct + DmGetLibraryVersion = dmGetLibraryVersionFct +) + +func free(p *C.char) { + C.free(unsafe.Pointer(p)) +} + +func dmTaskDestroyFct(task *CDmTask) { + C.dm_task_destroy((*C.struct_dm_task)(task)) +} + +func dmTaskCreateFct(taskType int) *CDmTask { + return (*CDmTask)(C.dm_task_create(C.int(taskType))) +} + +func dmTaskRunFct(task *CDmTask) int { + return int(C.dm_task_run((*C.struct_dm_task)(task))) +} + +func dmTaskSetNameFct(task *CDmTask, name string) int { + Cname := C.CString(name) + defer free(Cname) + + return int(C.dm_task_set_name((*C.struct_dm_task)(task), + Cname)) +} + +func dmTaskSetMessageFct(task *CDmTask, message string) int { + Cmessage := C.CString(message) + defer free(Cmessage) + + return int(C.dm_task_set_message((*C.struct_dm_task)(task), + Cmessage)) +} + +func dmTaskSetSectorFct(task *CDmTask, sector uint64) int { + return int(C.dm_task_set_sector((*C.struct_dm_task)(task), + C.uint64_t(sector))) +} + +func dmTaskSetCookieFct(task *CDmTask, cookie *uint, flags uint16) int { + cCookie := C.uint32_t(*cookie) + defer func() { + *cookie = uint(cCookie) + }() + return int(C.dm_task_set_cookie((*C.struct_dm_task)(task), &cCookie, + C.uint16_t(flags))) +} + +func dmTaskSetAddNodeFct(task *CDmTask, addNode AddNodeType) int { + return int(C.dm_task_set_add_node((*C.struct_dm_task)(task), + C.dm_add_node_t(addNode))) +} + +func dmTaskSetRoFct(task *CDmTask) int { + return int(C.dm_task_set_ro((*C.struct_dm_task)(task))) +} + +func dmTaskAddTargetFct(task *CDmTask, + start, size uint64, ttype, params string) int { + + Cttype := C.CString(ttype) + defer free(Cttype) + + Cparams := C.CString(params) + defer free(Cparams) + + return int(C.dm_task_add_target((*C.struct_dm_task)(task), + C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) +} + +func dmTaskGetDriverVersionFct(task *CDmTask, version *string) int { + buffer := C.CString(string(make([]byte, 128))) + defer free(buffer) + defer func() { + *version = C.GoString(buffer) + }() + return int(C.dm_task_get_driver_version((*C.struct_dm_task)(task), + buffer, 128)) +} + +func dmTaskGetInfoFct(task *CDmTask, info *Info) int { + Cinfo := C.struct_dm_info{} + defer func() { + info.Exists = int(Cinfo.exists) + info.Suspended = int(Cinfo.suspended) + info.LiveTable = int(Cinfo.live_table) + info.InactiveTable = int(Cinfo.inactive_table) + info.OpenCount = int32(Cinfo.open_count) + info.EventNr = uint32(Cinfo.event_nr) + info.Major = uint32(Cinfo.major) + info.Minor = uint32(Cinfo.minor) + info.ReadOnly = int(Cinfo.read_only) + info.TargetCount = int32(Cinfo.target_count) + }() + return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo)) +} + +func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, + target, params *string) uintptr { + + var ( + Cstart, Clength C.uint64_t + CtargetType, Cparams *C.char + ) + defer func() { + *start = uint64(Cstart) + *length = uint64(Clength) + *target = C.GoString(CtargetType) + *params = C.GoString(Cparams) + }() + nextp := C.dm_get_next_target((*C.struct_dm_task)(task), + unsafe.Pointer(next), &Cstart, &Clength, &CtargetType, &Cparams) + return uintptr(nextp) +} + +func dmAttachLoopDeviceFct(filename string, fd *int) string { + cFilename := C.CString(filename) + defer free(cFilename) + + var cFd C.int + defer func() { + *fd = int(cFd) + }() + + ret := C.attach_loop_device(cFilename, &cFd) + defer free(ret) + return C.GoString(ret) +} + +// sysGetBlockSizeFct retrieves the block size from IOCTL +func sysGetBlockSizeFct(fd uintptr, size *uint64) syscall.Errno { + _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, + uintptr(unsafe.Pointer(&size))) + return err +} + +// dmGetBlockSizeFct retrieves the block size from library call +func dmGetBlockSizeFct(fd uintptr) int64 { + return int64(C.get_block_size(C.int(fd))) +} + +func dmUdevWaitFct(cookie uint) int { + return int(C.dm_udev_wait(C.uint32_t(cookie))) +} + +func dmLogInitVerboseFct(level int) { + C.dm_log_init_verbose(C.int(level)) +} + +func logWithErrnoInitFct() { + C.log_with_errno_init() +} + +func dmSetDevDirFct(dir string) int { + Cdir := C.CString(dir) + defer free(Cdir) + + return int(C.dm_set_dev_dir(Cdir)) +} + +func dmGetLibraryVersionFct(version *string) int { + buffer := C.CString(string(make([]byte, 128))) + defer free(buffer) + defer func() { + *version = C.GoString(buffer) + }() + return int(C.dm_get_library_version(buffer, 128)) +}