// +build !windows,!darwin
-// Package vppapiclient is the default VPP adapter being used for the connection with VPP via shared memory.
-// It is based on the communication with the vppapiclient VPP library written in C via CGO.
package vppapiclient
/*
} reply_header_t;
static void
-govpp_msg_callback (unsigned char *data, int size)
+govpp_msg_callback(unsigned char *data, int size)
{
reply_header_t *header = ((reply_header_t *)data);
go_msg_callback(ntohs(header->msg_id), data, size);
}
static int
-govpp_connect (char *shm)
+govpp_connect(char *shm)
{
return vac_connect("govpp", shm, govpp_msg_callback, 32);
}
static int
-govvp_disconnect()
+govpp_disconnect()
{
return vac_disconnect();
}
)
const (
- // watchedFolder is a folder where vpp's shared memory is supposed to be created.
- // File system events are monitored in this folder.
- watchedFolder = "/dev/shm/"
- // watchedFile is a default name of the file in the watchedFolder. Once the file is present,
- // the vpp is ready to accept a new connection.
- watchedFile = "vpe-api"
+ // shmDir is a directory where shared memory is supposed to be created.
+ shmDir = "/dev/shm/"
+ // vppShmFile is a default name of the file in the shmDir.
+ vppShmFile = "vpe-api"
)
-// vppAPIClientAdapter is the opaque context of the adapter.
-type vppAPIClientAdapter struct {
- shmPrefix string
- callback adapter.MsgCallback
-}
+// global VPP binary API client adapter context
+var client *VppClient
-var vppClient *vppAPIClientAdapter // global vpp API client adapter context
+// VppClient is the default implementation of the VppAPI.
+type VppClient struct {
+ shmPrefix string
+ msgCallback adapter.MsgCallback
+}
-// NewVppAdapter returns a new vpp API client adapter.
-func NewVppAdapter(shmPrefix string) adapter.VppAdapter {
- return &vppAPIClientAdapter{
+// NewVppClient returns a new VPP binary API client.
+func NewVppClient(shmPrefix string) *VppClient {
+ return &VppClient{
shmPrefix: shmPrefix,
}
}
// Connect connects the process to VPP.
-func (a *vppAPIClientAdapter) Connect() error {
- vppClient = a
+func (a *VppClient) Connect() error {
+ if client != nil {
+ return fmt.Errorf("already connected to binary API, disconnect first")
+ }
+
var rc _Ctype_int
if a.shmPrefix == "" {
rc = C.govpp_connect(nil)
rc = C.govpp_connect(shm)
}
if rc != 0 {
- return fmt.Errorf("unable to connect to VPP (error=%d)", rc)
+ return fmt.Errorf("connecting to VPP binary API failed (rc=%v)", rc)
}
+
+ client = a
return nil
}
// Disconnect disconnects the process from VPP.
-func (a *vppAPIClientAdapter) Disconnect() {
- C.govvp_disconnect()
+func (a *VppClient) Disconnect() error {
+ client = nil
+
+ rc := C.govpp_disconnect()
+ if rc != 0 {
+ return fmt.Errorf("disconnecting from VPP binary API failed (rc=%v)", rc)
+ }
+
+ return nil
}
// GetMsgID returns a runtime message ID for the given message name and CRC.
-func (a *vppAPIClientAdapter) GetMsgID(msgName string, msgCrc string) (uint16, error) {
+func (a *VppClient) GetMsgID(msgName string, msgCrc string) (uint16, error) {
nameAndCrc := C.CString(msgName + "_" + msgCrc)
defer C.free(unsafe.Pointer(nameAndCrc))
}
// SendMsg sends a binary-encoded message to VPP.
-func (a *vppAPIClientAdapter) SendMsg(context uint32, data []byte) error {
+func (a *VppClient) SendMsg(context uint32, data []byte) error {
rc := C.govpp_send(C.uint32_t(context), unsafe.Pointer(&data[0]), C.size_t(len(data)))
if rc != 0 {
- return fmt.Errorf("unable to send the message (error=%d)", rc)
+ return fmt.Errorf("unable to send the message (rc=%v)", rc)
}
return nil
}
-// SetMsgCallback sets a callback function that will be called by the adapter whenever a message comes from VPP.
-func (a *vppAPIClientAdapter) SetMsgCallback(cb adapter.MsgCallback) {
- a.callback = cb
+// SetMsgCallback sets a callback function that will be called by the adapter
+// whenever a message comes from VPP.
+func (a *VppClient) SetMsgCallback(cb adapter.MsgCallback) {
+ a.msgCallback = cb
}
// WaitReady blocks until shared memory for sending
// binary api calls is present on the file system.
-func (a *vppAPIClientAdapter) WaitReady() error {
- // Path to the shared memory segment
+func (a *VppClient) WaitReady() error {
var path string
+
+ // join the path to the shared memory segment
if a.shmPrefix == "" {
- path = filepath.Join(watchedFolder, watchedFile)
+ path = filepath.Join(shmDir, vppShmFile)
} else {
- path = filepath.Join(watchedFolder, a.shmPrefix+"-"+watchedFile)
+ path = filepath.Join(shmDir, a.shmPrefix+"-"+vppShmFile)
}
- // Watch folder if file does not exist yet
- if !fileExists(path) {
- watcher, err := fsnotify.NewWatcher()
- if err != nil {
- return err
- }
- defer watcher.Close()
+ // check if file at the path exists
+ if _, err := os.Stat(path); err == nil {
+ // file exists, we are ready
+ return nil
+ } else if !os.IsNotExist(err) {
+ return err
+ }
- if err := watcher.Add(watchedFolder); err != nil {
- return err
- }
+ // file does not exist, start watching folder
+ watcher, err := fsnotify.NewWatcher()
+ if err != nil {
+ return err
+ }
+ defer watcher.Close()
+
+ if err := watcher.Add(shmDir); err != nil {
+ return err
+ }
- for {
- ev := <-watcher.Events
- if ev.Name == path && (ev.Op&fsnotify.Create) == fsnotify.Create {
+ for {
+ ev := <-watcher.Events
+ if ev.Name == path {
+ if (ev.Op & fsnotify.Create) == fsnotify.Create {
+ // file was created, we are ready
break
}
}
return nil
}
-func fileExists(name string) bool {
- if _, err := os.Stat(name); err != nil {
- if os.IsNotExist(err) {
- return false
- }
- }
- return true
-}
-
//export go_msg_callback
func go_msg_callback(msgID C.uint16_t, data unsafe.Pointer, size C.size_t) {
// convert unsafe.Pointer to byte slice
- slice := &reflect.SliceHeader{Data: uintptr(data), Len: int(size), Cap: int(size)}
- byteArr := *(*[]byte)(unsafe.Pointer(slice))
+ sliceHeader := &reflect.SliceHeader{Data: uintptr(data), Len: int(size), Cap: int(size)}
+ byteSlice := *(*[]byte)(unsafe.Pointer(sliceHeader))
- vppClient.callback(uint16(msgID), byteArr)
+ client.msgCallback(uint16(msgID), byteSlice)
}