1 // Copyright (c) 2017 Cisco and/or its affiliates.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 // +build !windows,!darwin,!novpp
20 #cgo CFLAGS: -DPNG_DEBUG=1
21 #cgo LDFLAGS: -lvppapiclient
23 #include "vppapiclient_wrapper.h"
36 "github.com/fsnotify/fsnotify"
38 "git.fd.io/govpp.git/adapter"
42 // MaxWaitReady defines maximum duration before waiting for shared memory
44 MaxWaitReady = time.Second * 10
48 // shmDir is a directory where shared memory is supposed to be created.
50 // vppShmFile is a default name of the file in the shmDir.
51 vppShmFile = "vpe-api"
54 // global VPP binary API client, library vppapiclient only supports
55 // single connection at a time
56 var globalVppClient unsafe.Pointer
58 // stubVppClient is the default implementation of the VppAPI.
59 type vppClient struct {
61 msgCallback adapter.MsgCallback
65 // NewVppClient returns a new VPP binary API client.
66 func NewVppClient(shmPrefix string) adapter.VppAPI {
67 return NewVppClientWithInputQueueSize(shmPrefix, 32)
70 // NewVppClientWithInputQueueSize returns a new VPP binary API client with a custom input queue size.
71 func NewVppClientWithInputQueueSize(shmPrefix string, inputQueueSize uint16) adapter.VppAPI {
74 inputQueueSize: inputQueueSize,
78 // Connect connects the process to VPP.
79 func (a *vppClient) Connect() error {
80 h := (*vppClient)(atomic.LoadPointer(&globalVppClient))
82 return fmt.Errorf("already connected to binary API, disconnect first")
85 rxQlen := C.int(a.inputQueueSize)
87 if a.shmPrefix == "" {
88 rc = C.govpp_connect(nil, rxQlen)
90 shm := C.CString(a.shmPrefix)
91 rc = C.govpp_connect(shm, rxQlen)
94 return fmt.Errorf("connecting to VPP binary API failed (rc=%v)", rc)
97 atomic.StorePointer(&globalVppClient, unsafe.Pointer(a))
101 // Disconnect disconnects the process from VPP.
102 func (a *vppClient) Disconnect() error {
103 atomic.StorePointer(&globalVppClient, nil)
104 rc := C.govpp_disconnect()
106 return fmt.Errorf("disconnecting from VPP binary API failed (rc=%v)", rc)
111 // GetMsgID returns a runtime message ID for the given message name and CRC.
112 func (a *vppClient) GetMsgID(msgName string, msgCrc string) (uint16, error) {
113 nameAndCrc := C.CString(msgName + "_" + msgCrc)
114 defer C.free(unsafe.Pointer(nameAndCrc))
116 msgID := uint16(C.govpp_get_msg_index(nameAndCrc))
117 if msgID == ^uint16(0) {
118 // VPP does not know this message
119 return msgID, &adapter.UnknownMsgError{msgName, msgCrc}
125 // SendMsg sends a binary-encoded message to VPP.
126 func (a *vppClient) SendMsg(context uint32, data []byte) error {
127 rc := C.govpp_send(C.uint32_t(context), unsafe.Pointer(&data[0]), C.size_t(len(data)))
129 return fmt.Errorf("unable to send the message (rc=%v)", rc)
134 // SetMsgCallback sets a callback function that will be called by the adapter
135 // whenever a message comes from VPP.
136 func (a *vppClient) SetMsgCallback(cb adapter.MsgCallback) {
140 // WaitReady blocks until shared memory for sending
141 // binary api calls is present on the file system.
142 func (a *vppClient) WaitReady() error {
143 // join the path to the shared memory segment
145 if a.shmPrefix == "" {
146 path = filepath.Join(shmDir, vppShmFile)
148 path = filepath.Join(shmDir, a.shmPrefix+"-"+vppShmFile)
151 // check if file already exists
152 if _, err := os.Stat(path); err == nil {
153 return nil // file exists, we are ready
154 } else if !os.IsNotExist(err) {
155 return err // some other error occurred
158 // file does not exist, start watching folder
159 watcher, err := fsnotify.NewWatcher()
163 defer watcher.Close()
165 // start watching directory
166 if err := watcher.Add(shmDir); err != nil {
170 timeout := time.NewTimer(MaxWaitReady)
174 return fmt.Errorf("timeout waiting (%s) for shm file: %s", MaxWaitReady, path)
176 case e := <-watcher.Errors:
179 case ev := <-watcher.Events:
180 if ev.Name == path && (ev.Op&fsnotify.Create) == fsnotify.Create {
181 // file created, we are ready
188 //export go_msg_callback
189 func go_msg_callback(msgID C.uint16_t, data unsafe.Pointer, size C.size_t) {
190 h := (*vppClient)(atomic.LoadPointer(&globalVppClient))
194 // convert unsafe.Pointer to byte slice
195 sliceHeader := &reflect.SliceHeader{Data: uintptr(data), Len: int(size), Cap: int(size)}
196 byteSlice := *(*[]byte)(unsafe.Pointer(sliceHeader))
197 h.msgCallback(uint16(msgID), byteSlice)