support for shm prefixes
[govpp.git] / adapter / vppapiclient / vppapiclient_adapter.go
index 82c820e..5a05a58 100644 (file)
@@ -49,9 +49,9 @@ govpp_msg_callback (unsigned char *data, int size)
 }
 
 static int
-govpp_connect()
+govpp_connect (char *shm)
 {
-    return vac_connect("govpp", NULL, govpp_msg_callback, 32);
+    return vac_connect("govpp", shm, govpp_msg_callback, 32);
 }
 
 static int
@@ -77,30 +77,49 @@ govpp_get_msg_index(char *name_and_crc)
 import "C"
 
 import (
-       "errors"
        "fmt"
+       "os"
        "reflect"
        "unsafe"
 
-       "gerrit.fd.io/r/govpp.git/adapter"
+       "git.fd.io/govpp.git/adapter"
+       "github.com/fsnotify/fsnotify"
+)
+
+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"
 )
 
 // vppAPIClientAdapter is the opaque context of the adapter.
 type vppAPIClientAdapter struct {
-       callback func(context uint32, msgId uint16, data []byte)
+       shmPrefix string
+       callback  func(context uint32, msgId uint16, data []byte)
 }
 
 var vppClient *vppAPIClientAdapter // global vpp API client adapter context
 
 // NewVppAdapter returns a new vpp API client adapter.
-func NewVppAdapter() adapter.VppAdapter {
-       return &vppAPIClientAdapter{}
+func NewVppAdapter(shmPrefix string) adapter.VppAdapter {
+       return &vppAPIClientAdapter{
+               shmPrefix: shmPrefix,
+       }
 }
 
 // Connect connects the process to VPP.
 func (a *vppAPIClientAdapter) Connect() error {
        vppClient = a
-       rc := C.govpp_connect()
+       var rc _Ctype_int
+       if a.shmPrefix == "" {
+               rc = C.govpp_connect(nil)
+       } else {
+               shm := C.CString(a.shmPrefix)
+               rc = C.govpp_connect(shm)
+       }
        if rc != 0 {
                return fmt.Errorf("unable to connect to VPP (error=%d)", rc)
        }
@@ -114,12 +133,12 @@ func (a *vppAPIClientAdapter) Disconnect() {
 
 // GetMsgID returns a runtime message ID for the given message name and CRC.
 func (a *vppAPIClientAdapter) GetMsgID(msgName string, msgCrc string) (uint16, error) {
-       nameAndCrc := C.CString(fmt.Sprintf("%s_%s", msgName, msgCrc))
+       nameAndCrc := C.CString(msgName + "_" + msgCrc)
        defer C.free(unsafe.Pointer(nameAndCrc))
 
        msgID := uint16(C.govpp_get_msg_index(nameAndCrc))
        if msgID == ^uint16(0) {
-               return msgID, errors.New("unkonwn message")
+               return msgID, fmt.Errorf("unknown message: %v (crc: %v)", msgName, msgCrc)
        }
 
        return msgID, nil
@@ -139,6 +158,48 @@ func (a *vppAPIClientAdapter) SetMsgCallback(cb func(context uint32, msgID uint1
        a.callback = cb
 }
 
+// WaitReady blocks until shared memory for sending
+// binary api calls is present on the file system.
+func (a *vppAPIClientAdapter) WaitReady() error {
+       watcher, err := fsnotify.NewWatcher()
+       if err != nil {
+               return err
+       }
+       defer watcher.Close()
+
+       err = watcher.Add(watchedFolder)
+       if err != nil {
+               return err
+       }
+       // Path to the shared memory segment with prefix, if set
+       var path string
+       if a.shmPrefix == "" {
+               path = watchedFolder + watchedFile
+       } else {
+               path = watchedFolder + a.shmPrefix + "-" + watchedFile
+       }
+       if fileExists(path) {
+               return nil
+       }
+
+       for {
+               ev := <-watcher.Events
+               if ev.Name == path && (ev.Op&fsnotify.Create) == fsnotify.Create {
+                       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, context C.uint32_t, data unsafe.Pointer, size C.size_t) {
        // convert unsafe.Pointer to byte slice
@@ -146,4 +207,4 @@ func go_msg_callback(msgID C.uint16_t, context C.uint32_t, data unsafe.Pointer,
        byteArr := *(*[]byte)(unsafe.Pointer(slice))
 
        vppClient.callback(uint32(context), uint16(msgID), byteArr)
-}
+}
\ No newline at end of file