blob: 8fda6435bc52623c39ed9e94ebb659fb2cb02fe3 [file] [log] [blame]
/*
*------------------------------------------------------------------
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*------------------------------------------------------------------
*/
package main
import (
"bufio"
"flag"
"fmt"
"os"
"strings"
"sync"
"github.com/pkg/profile"
"memif"
)
func Disconnected(i *memif.Interface) error {
fmt.Println("Disconnected: ", i.GetName())
data, ok := i.GetPrivateData().(*interfaceData)
if !ok {
return fmt.Errorf("Invalid private data")
}
close(data.quitChan) // stop polling
close(data.errChan)
data.wg.Wait() // wait until polling stops, then continue disconnect
return nil
}
func Connected(i *memif.Interface) error {
fmt.Println("Connected: ", i.GetName())
data, ok := i.GetPrivateData().(*interfaceData)
if !ok {
return fmt.Errorf("Invalid private data")
}
data.errChan = make(chan error, 1)
data.quitChan = make(chan struct{}, 1)
data.wg.Add(1)
go func(errChan chan<- error, quitChan <-chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
// allocate packet buffer
pkt := make([]byte, 2048)
// get rx queue
rxq0, err := i.GetRxQueue(0)
if err != nil {
errChan <- err
return
}
// get tx queue
txq0, err := i.GetTxQueue(0)
if err != nil {
errChan <- err
return
}
for {
select {
case <-quitChan: // channel closed
return
default:
// read packet from shared memory
pktLen, err := rxq0.ReadPacket(pkt)
if pktLen > 0 {
// write packet to shared memory
txq0.WritePacket(pkt[:pktLen])
} else if err != nil {
errChan <- err
return
}
}
}
}(data.errChan, data.quitChan, &data.wg)
return nil
}
type interfaceData struct {
errChan chan error
quitChan chan struct{}
wg sync.WaitGroup
}
func interractiveHelp() {
fmt.Println("help - print this help")
fmt.Println("start - start connecting loop")
fmt.Println("show - print interface details")
fmt.Println("exit - exit the application")
}
func main() {
cpuprof := flag.String("cpuprof", "", "cpu profiling output file")
memprof := flag.String("memprof", "", "mem profiling output file")
role := flag.String("role", "slave", "interface role")
name := flag.String("name", "gomemif", "interface name")
socketName := flag.String("socket", "", "control socket filename")
flag.Parse()
if *cpuprof != "" {
defer profile.Start(profile.CPUProfile, profile.ProfilePath(*cpuprof)).Stop()
}
if *memprof != "" {
defer profile.Start(profile.MemProfile, profile.ProfilePath(*memprof)).Stop()
}
memifErrChan := make(chan error)
exitChan := make(chan struct{})
var isMaster bool
switch *role {
case "slave":
isMaster = false
case "master":
isMaster = true
default:
fmt.Println("Invalid role")
return
}
fmt.Println("GoMemif: Responder")
fmt.Println("-----------------------")
socket, err := memif.NewSocket("gomemif_example", *socketName)
if err != nil {
fmt.Println("Failed to create socket: ", err)
return
}
data := interfaceData{}
args := &memif.Arguments{
IsMaster: isMaster,
ConnectedFunc: Connected,
DisconnectedFunc: Disconnected,
PrivateData: &data,
Name: *name,
}
i, err := socket.NewInterface(args)
if err != nil {
fmt.Println("Failed to create interface on socket %s: %s", socket.GetFilename(), err)
goto exit
}
// slave attempts to connect to control socket
// to handle control communication call socket.StartPolling()
if !i.IsMaster() {
fmt.Println(args.Name, ": Connecting to control socket...")
for !i.IsConnecting() {
err = i.RequestConnection()
if err != nil {
/* TODO: check for ECONNREFUSED errno
* if error is ECONNREFUSED it may simply mean that master
* interface is not up yet, use i.RequestConnection()
*/
fmt.Println("Faild to connect: ", err)
goto exit
}
}
}
go func(exitChan chan<- struct{}) {
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("gomemif# ")
text, _ := reader.ReadString('\n')
// convert CRLF to LF
text = strings.Replace(text, "\n", "", -1)
switch text {
case "help":
interractiveHelp()
case "start":
// start polling for events on this socket
socket.StartPolling(memifErrChan)
case "show":
fmt.Println("remote: ", i.GetRemoteName())
fmt.Println("peer: ", i.GetPeerName())
case "exit":
err = socket.StopPolling()
if err != nil {
fmt.Println("Failed to stop polling: ", err)
}
close(exitChan)
return
default:
fmt.Println("Unknown input")
}
}
}(exitChan)
for {
select {
case <-exitChan:
goto exit
case err, ok := <-memifErrChan:
if ok {
fmt.Println(err)
}
case err, ok := <-data.errChan:
if ok {
fmt.Println(err)
}
default:
continue
}
}
exit:
socket.Delete()
close(memifErrChan)
}