<commit_before>package main

import (
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"log"
	"os"
	"sort"
	"strings"

	"github.com/kisielk/gotool"
)

var exitCode int

func main() {
	// FIXME check flag.NArgs
	paths := gotool.ImportPaths([]string{os.Args[1]})
	cwd, err := os.Getwd()
	if err != nil {
		// XXX
		log.Fatal(err)
	}
	for _, path := range paths {
		pkg, err := build.Import(path, cwd, build.FindOnly)
		if err != nil {
			// XXX
			log.Fatal(err)
		}
		fset := token.NewFileSet()
		pkgs, err := parser.ParseDir(fset, pkg.Dir, nil, 0)
		if err != nil {
			// XXX
			log.Fatal(err)
		}
		for _, pkg := range pkgs {
			doPackage(fset, pkg)
		}

	}
	os.Exit(exitCode)
}

type Package struct {
	p    *ast.Package
	fset *token.FileSet
	decl map[string]ast.Node
	used map[string]bool
}

func doPackage(fset *token.FileSet, pkg *ast.Package) {
	p := &Package{
		p:    pkg,
		fset: fset,
		decl: make(map[string]ast.Node),
		used: make(map[string]bool),
	}
	for _, file := range pkg.Files {
		for _, decl := range file.Decls {
			switch n := decl.(type) {
			case *ast.GenDecl:
				// var, const, types
				for _, spec := range n.Specs {
					switch s := spec.(type) {
					case *ast.ValueSpec:
						// constants and variables.
						for _, name := range s.Names {
							p.decl[name.Name] = n
						}
					case *ast.TypeSpec:
						// type definitions.
						p.decl[s.Name.Name] = n
					}
				}
			case *ast.FuncDecl:
				// function declarations
				// TODO(remy): do methods
				if n.Recv == nil {
					p.decl[n.Name.Name] = n
				}
			}
		}
	}
	// init() and _ are always used
	p.used["init"] = true
	p.used["_"] = true
	for name, node := range p.decl {
		if !ast.IsExported(name) {
			continue
		}
		if pkg.Name != "main" {
			// exported identifiers in non-main are used
			p.used[name] = true
			continue
		}
		// test and benchmark functions in tests are used
		file := fset.Position(node.Pos()).Filename
		if strings.HasSuffix(file, "_test.go") &&
			(strings.HasPrefix(name, "Test") || strings.HasPrefix(name, "Benchmark")) {
			p.used[name] = true
		}
	}
	if pkg.Name == "main" {
		// in main programs, main() is called.
		p.used["main"] = true
	}
	for _, file := range pkg.Files {
		// walk file looking for used nodes.
		ast.Walk(p, file)
	}
	// reports.
	var reports Reports
	for name, node := range p.decl {
		if !p.used[name] {
			pos := node.Pos()
			if node, ok := node.(*ast.GenDecl); ok && node.Lparen.IsValid() {
				for _, spec := range node.Specs {
					switch spec := spec.(type) {
					case *ast.ValueSpec:
						for _, s := range spec.Names {
							if s.Name == name {
								pos = s.NamePos
								break
							}
						}
					case *ast.TypeSpec:
						pos = spec.Name.Pos()
					}
				}
			}
			reports = append(reports, Report{pos, name})
		}
	}
	sort.Sort(reports)
	for _, report := range reports {
		fmt.Printf("%s: %s is unused\n", fset.Position(report.pos), report.name)
	}
}

type Report struct {
	pos  token.Pos
	name string
}
type Reports []Report

func (l Reports) Len() int           { return len(l) }
func (l Reports) Less(i, j int) bool { return l[i].pos < l[j].pos }
func (l Reports) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }

// Visits files for used nodes.
func (p *Package) Visit(node ast.Node) ast.Visitor {
	u := usedWalker(*p) // hopefully p fields are references.
	switch n := node.(type) {
	// don't walk whole file, but only:
	case *ast.ValueSpec:
		// - variable initializers
		for _, value := range n.Values {
			ast.Walk(&u, value)
		}
		// variable types.
		if n.Type != nil {
			ast.Walk(&u, n.Type)
		}
	case *ast.BlockStmt:
		// - function bodies
		for _, stmt := range n.List {
			ast.Walk(&u, stmt)
		}
	case *ast.FuncDecl:
		// - function signatures
		ast.Walk(&u, n.Type)
	case *ast.TypeSpec:
		// - type declarations
		ast.Walk(&u, n.Type)
	}
	return p
}

type usedWalker Package

// Walks through the AST marking used identifiers.
func (p *usedWalker) Visit(node ast.Node) ast.Visitor {
	// just be stupid and mark all *ast.Ident
	switch n := node.(type) {
	case *ast.Ident:
		p.used[n.Name] = true
	}
	return p
}
<commit_msg>Introduce flags to selectively disable checks<commit_after>package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"log"
	"os"
	"sort"
	"strings"

	"github.com/kisielk/gotool"
)

var exitCode int

var (
	fTypes     bool
	fVariables bool
	fFunctions bool
)

func init() {
	flag.BoolVar(&fFunctions, "f", true, "Report unused functions")
	flag.BoolVar(&fTypes, "t", true, "Report unused types")
	flag.BoolVar(&fVariables, "v", true, "Report unused constants and variables")
}

func main() {
	flag.Parse()
	// FIXME check flag.NArgs
	paths := gotool.ImportPaths([]string{flag.Arg(0)})
	cwd, err := os.Getwd()
	if err != nil {
		// XXX
		log.Fatal(err)
	}
	for _, path := range paths {
		pkg, err := build.Import(path, cwd, build.FindOnly)
		if err != nil {
			// XXX
			log.Fatal(err)
		}
		fset := token.NewFileSet()
		pkgs, err := parser.ParseDir(fset, pkg.Dir, nil, 0)
		if err != nil {
			// XXX
			log.Fatal(err)
		}
		for _, pkg := range pkgs {
			doPackage(fset, pkg)
		}

	}
	os.Exit(exitCode)
}

type Package struct {
	p    *ast.Package
	fset *token.FileSet
	decl map[string]ast.Node
	used map[string]bool
}

func doPackage(fset *token.FileSet, pkg *ast.Package) {
	p := &Package{
		p:    pkg,
		fset: fset,
		decl: make(map[string]ast.Node),
		used: make(map[string]bool),
	}
	for _, file := range pkg.Files {
		for _, decl := range file.Decls {
			switch n := decl.(type) {
			case *ast.GenDecl:
				// var, const, types
				for _, spec := range n.Specs {
					switch s := spec.(type) {
					case *ast.ValueSpec:
						// constants and variables.
						if fVariables {
							for _, name := range s.Names {
								p.decl[name.Name] = n
							}
						}
					case *ast.TypeSpec:
						// type definitions.
						if fTypes {
							p.decl[s.Name.Name] = n
						}
					}
				}
			case *ast.FuncDecl:
				// function declarations
				// TODO(remy): do methods
				if fFunctions {
					if n.Recv == nil {
						p.decl[n.Name.Name] = n
					}
				}
			}
		}
	}
	// init() and _ are always used
	p.used["init"] = true
	p.used["_"] = true
	for name, node := range p.decl {
		if !ast.IsExported(name) {
			continue
		}
		if pkg.Name != "main" {
			// exported identifiers in non-main are used
			p.used[name] = true
			continue
		}
		// test and benchmark functions in tests are used
		file := fset.Position(node.Pos()).Filename
		if strings.HasSuffix(file, "_test.go") &&
			(strings.HasPrefix(name, "Test") || strings.HasPrefix(name, "Benchmark")) {
			p.used[name] = true
		}
	}
	if pkg.Name == "main" {
		// in main programs, main() is called.
		p.used["main"] = true
	}
	for _, file := range pkg.Files {
		// walk file looking for used nodes.
		ast.Walk(p, file)
	}
	// reports.
	var reports Reports
	for name, node := range p.decl {
		if !p.used[name] {
			pos := node.Pos()
			if node, ok := node.(*ast.GenDecl); ok && node.Lparen.IsValid() {
				for _, spec := range node.Specs {
					switch spec := spec.(type) {
					case *ast.ValueSpec:
						for _, s := range spec.Names {
							if s.Name == name {
								pos = s.NamePos
								break
							}
						}
					case *ast.TypeSpec:
						pos = spec.Name.Pos()
					}
				}
			}
			reports = append(reports, Report{pos, name})
		}
	}
	sort.Sort(reports)
	for _, report := range reports {
		fmt.Printf("%s: %s is unused\n", fset.Position(report.pos), report.name)
	}
}

type Report struct {
	pos  token.Pos
	name string
}
type Reports []Report

func (l Reports) Len() int           { return len(l) }
func (l Reports) Less(i, j int) bool { return l[i].pos < l[j].pos }
func (l Reports) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }

// Visits files for used nodes.
func (p *Package) Visit(node ast.Node) ast.Visitor {
	u := usedWalker(*p) // hopefully p fields are references.
	switch n := node.(type) {
	// don't walk whole file, but only:
	case *ast.ValueSpec:
		// - variable initializers
		for _, value := range n.Values {
			ast.Walk(&u, value)
		}
		// variable types.
		if n.Type != nil {
			ast.Walk(&u, n.Type)
		}
	case *ast.BlockStmt:
		// - function bodies
		for _, stmt := range n.List {
			ast.Walk(&u, stmt)
		}
	case *ast.FuncDecl:
		// - function signatures
		ast.Walk(&u, n.Type)
	case *ast.TypeSpec:
		// - type declarations
		ast.Walk(&u, n.Type)
	}
	return p
}

type usedWalker Package

// Walks through the AST marking used identifiers.
func (p *usedWalker) Visit(node ast.Node) ast.Visitor {
	// just be stupid and mark all *ast.Ident
	switch n := node.(type) {
	case *ast.Ident:
		p.used[n.Name] = true
	}
	return p
}
<|endoftext|>


<commit_before>/*-
 * Copyright (c) 2012-2016 Caoimhe Chaos <caoimhechaos@protonmail.com>,
 *                         Ancient Solutions. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions  of source code must retain  the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions  in   binary  form  must   reproduce  the  above
 *    copyright  notice, this  list  of conditions  and the  following
 *    disclaimer in the  documentation and/or other materials provided
 *    with the distribution.
 *
 * THIS  SOFTWARE IS  PROVIDED BY  ANCIENT SOLUTIONS  AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO,  THE IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS
 * FOR A  PARTICULAR PURPOSE  ARE DISCLAIMED.  IN  NO EVENT  SHALL THE
 * FOUNDATION  OR CONTRIBUTORS  BE  LIABLE FOR  ANY DIRECT,  INDIRECT,
 * INCIDENTAL,   SPECIAL,    EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
 * (INCLUDING, BUT NOT LIMITED  TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE,  DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT  LIABILITY,  OR  TORT  (INCLUDING NEGLIGENCE  OR  OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"flag"
	"io/ioutil"
	"log"
	"net"

	"code.google.com/p/goprotobuf/proto"
	"github.com/caoimhechaos/geocolo"
	"google.golang.org/grpc"
)

func main() {
	var service *geocolo.GeoProximityService
	var server *grpc.Server
	var config *geocolo.GeoProximityServiceConfig
	var configpath string
	var listen_net, listen_ip string
	var listener net.Listener
	var bdata []byte
	var err error

	flag.StringVar(&configpath, "config", "",
		"Path to the geocolo service configuration")
	flag.StringVar(&listen_net, "listen-proto", "tcp",
		"Protocol type to listen on (e.g. tcp)")
	flag.StringVar(&listen_ip, "listen-addr", "[::]:1234",
		"IP address to listen on")
	flag.Parse()

	config = new(geocolo.GeoProximityServiceConfig)
	bdata, err = ioutil.ReadFile(configpath)
	if err != nil {
		log.Fatal("Error reading ", configpath, ": ", err)
	}

	err = proto.UnmarshalText(string(bdata), config)
	if err != nil {
		var err2 error = proto.Unmarshal(bdata, config)
		if err != nil {
			log.Print("Error parsing ", configpath, " as text: ",
				err)
			log.Fatal("Error parsing ", configpath, ": ", err2)
		}
	}

	service, err = geocolo.NewGeoProximityService(config)
	if err != nil {
		log.Fatal("Error creating GeoProximityService: ", err)
	}

	if config.ServiceCertificate != nil && config.ServiceKey != nil {
		var cert tls.Certificate
		var tls_config *tls.Config
		var root *x509.CertPool = x509.NewCertPool()
		var cacert *x509.Certificate
		var cablock *pem.Block
		var cadata []byte

		cert, err = tls.LoadX509KeyPair(config.GetServiceCertificate(),
			config.GetServiceKey())
		if err != nil {
			log.Fatal("Error loading X.509 key pair from ",
				config.GetServiceCertificate(), " and ",
				config.GetServiceKey(), ": ", err)
		}

		cadata, err = ioutil.ReadFile(config.GetCaCertificate())
		if err != nil {
			log.Fatal("Error reading CA certificate from ",
				config.GetCaCertificate(), ": ", err)
		}

		cablock, _ = pem.Decode(cadata)
		cacert, err = x509.ParseCertificate(cablock.Bytes)
		if err != nil {
			log.Fatal("Error parsing X.509 certificate ",
				config.GetCaCertificate(), ": ", err)
		}
		root.AddCert(cacert)

		tls_config = &tls.Config{
			Certificates: []tls.Certificate{cert},
			ClientAuth:   tls.RequireAndVerifyClientCert,
			MinVersion:   tls.VersionTLS12,
			RootCAs:      root,
		}
		listener, err = tls.Listen(listen_net, listen_ip, tls_config)
		if err != nil {
			log.Fatal("Error listening on ", listen_ip, ": ", err)
		}
	} else {
		listener, err = net.Listen(listen_net, listen_ip)
		if err != nil {
			log.Fatal("Error listening on ", listen_ip, ": ", err)
		}
	}

	server = grpc.NewServer()
	geocolo.RegisterGeoProximityServiceServer(server, service)
	server.Serve(listener)
}
<commit_msg>Check err2 after parsing the protobuf as binary.<commit_after>/*-
 * Copyright (c) 2012-2016 Caoimhe Chaos <caoimhechaos@protonmail.com>,
 *                         Ancient Solutions. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions  of source code must retain  the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions  in   binary  form  must   reproduce  the  above
 *    copyright  notice, this  list  of conditions  and the  following
 *    disclaimer in the  documentation and/or other materials provided
 *    with the distribution.
 *
 * THIS  SOFTWARE IS  PROVIDED BY  ANCIENT SOLUTIONS  AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO,  THE IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS
 * FOR A  PARTICULAR PURPOSE  ARE DISCLAIMED.  IN  NO EVENT  SHALL THE
 * FOUNDATION  OR CONTRIBUTORS  BE  LIABLE FOR  ANY DIRECT,  INDIRECT,
 * INCIDENTAL,   SPECIAL,    EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
 * (INCLUDING, BUT NOT LIMITED  TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE,  DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT  LIABILITY,  OR  TORT  (INCLUDING NEGLIGENCE  OR  OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"flag"
	"io/ioutil"
	"log"
	"net"

	"code.google.com/p/goprotobuf/proto"
	"github.com/caoimhechaos/geocolo"
	"google.golang.org/grpc"
)

func main() {
	var service *geocolo.GeoProximityService
	var server *grpc.Server
	var config *geocolo.GeoProximityServiceConfig
	var configpath string
	var listen_net, listen_ip string
	var listener net.Listener
	var bdata []byte
	var err error

	flag.StringVar(&configpath, "config", "",
		"Path to the geocolo service configuration")
	flag.StringVar(&listen_net, "listen-proto", "tcp",
		"Protocol type to listen on (e.g. tcp)")
	flag.StringVar(&listen_ip, "listen-addr", "[::]:1234",
		"IP address to listen on")
	flag.Parse()

	config = new(geocolo.GeoProximityServiceConfig)
	bdata, err = ioutil.ReadFile(configpath)
	if err != nil {
		log.Fatal("Error reading ", configpath, ": ", err)
	}

	err = proto.UnmarshalText(string(bdata), config)
	if err != nil {
		var err2 error = proto.Unmarshal(bdata, config)
		if err2 != nil {
			log.Print("Error parsing ", configpath, " as text: ",
				err)
			log.Fatal("Error parsing ", configpath, ": ", err2)
		}
	}

	service, err = geocolo.NewGeoProximityService(config)
	if err != nil {
		log.Fatal("Error creating GeoProximityService: ", err)
	}

	if config.ServiceCertificate != nil && config.ServiceKey != nil {
		var cert tls.Certificate
		var tls_config *tls.Config
		var root *x509.CertPool = x509.NewCertPool()
		var cacert *x509.Certificate
		var cablock *pem.Block
		var cadata []byte

		cert, err = tls.LoadX509KeyPair(config.GetServiceCertificate(),
			config.GetServiceKey())
		if err != nil {
			log.Fatal("Error loading X.509 key pair from ",
				config.GetServiceCertificate(), " and ",
				config.GetServiceKey(), ": ", err)
		}

		cadata, err = ioutil.ReadFile(config.GetCaCertificate())
		if err != nil {
			log.Fatal("Error reading CA certificate from ",
				config.GetCaCertificate(), ": ", err)
		}

		cablock, _ = pem.Decode(cadata)
		cacert, err = x509.ParseCertificate(cablock.Bytes)
		if err != nil {
			log.Fatal("Error parsing X.509 certificate ",
				config.GetCaCertificate(), ": ", err)
		}
		root.AddCert(cacert)

		tls_config = &tls.Config{
			Certificates: []tls.Certificate{cert},
			ClientAuth:   tls.RequireAndVerifyClientCert,
			MinVersion:   tls.VersionTLS12,
			RootCAs:      root,
		}
		listener, err = tls.Listen(listen_net, listen_ip, tls_config)
		if err != nil {
			log.Fatal("Error listening on ", listen_ip, ": ", err)
		}
	} else {
		listener, err = net.Listen(listen_net, listen_ip)
		if err != nil {
			log.Fatal("Error listening on ", listen_ip, ": ", err)
		}
	}

	server = grpc.NewServer()
	geocolo.RegisterGeoProximityServiceServer(server, service)
	server.Serve(listener)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/dmnlk/gomadare"
	"github.com/dmnlk/stringUtils"
	"github.com/rem7/goprowl"
)

var (
	CONSUMER_KEY        string
	CONSUMER_KEY_SECRET string
	ACCESS_TOKEN        string
	ACCESS_TOKEN_SECRET string
	PROWL_API_KEY       string
	PROWL goprowl.Goprowl
)

func main() {
	err := configureToken()
	if err != nil {
		fmt.Println(err)
		return
	}
	err = PROWL.RegisterKey(PROWL_API_KEY)
	if err != nil {
		fmt.Println(err)
		return
	}

	client := gomadare.NewClient(CONSUMER_KEY, CONSUMER_KEY_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

	client.GetUserStream(nil, func(s gomadare.Status, e gomadare.Event) {
		if &s != nil {

		}
		if &e != nil {
			sendEventToProwl(e)
		}
	})
}

func configureToken() error {
	CONSUMER_KEY = os.Getenv("CONSUMER_KEY")
	CONSUMER_KEY_SECRET = os.Getenv("CONSUMER_KEY_SECRET")
	ACCESS_TOKEN = os.Getenv("ACCESS_TOKEN")
	ACCESS_TOKEN_SECRET = os.Getenv("ACCESS_TOKEN_SECRET")
	PROWL_API_KEY = os.Getenv("PROWL_API_KEY")
	if ng := stringUtils.IsAnyEmpty(CONSUMER_KEY, CONSUMER_KEY_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET, PROWL_API_KEY); ng {
		return fmt.Errorf("some key invalid")
	}

	return nil
}

func sendEventToProwl(e gomadare.Event) {

	n := &goprowl.Notification{
		Application: "Twitter",
		Description:  e.Event + " " + e.TargetObject.Text,
		Event:       e.Event + " " + e.Source.Name,
		Priority:    "1",
	}

	PROWL.Push(n)

}
<commit_msg>sample reply method<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/dmnlk/gomadare"
	"github.com/dmnlk/stringUtils"
	"github.com/rem7/goprowl"
	"github.com/k0kubun/pp"
)

var (
	CONSUMER_KEY        string
	CONSUMER_KEY_SECRET string
	ACCESS_TOKEN        string
	ACCESS_TOKEN_SECRET string
	PROWL_API_KEY       string
	PROWL goprowl.Goprowl
)

func main() {
	err := configureToken()
	if err != nil {
		fmt.Println(err)
		return
	}
	err = PROWL.RegisterKey(PROWL_API_KEY)
	if err != nil {
		fmt.Println(err)
		return
	}

	client := gomadare.NewClient(CONSUMER_KEY, CONSUMER_KEY_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
	fmt.Println("aa")
	client.GetUserStream(nil, func(s gomadare.Status, e gomadare.Event) {
		if &s != nil {

		}
		if &e != nil {
			sendEventToProwl(e)
		}
	})
}

func configureToken() error {
	CONSUMER_KEY = os.Getenv("CONSUMER_KEY")
	CONSUMER_KEY_SECRET = os.Getenv("CONSUMER_KEY_SECRET")
	ACCESS_TOKEN = os.Getenv("ACCESS_TOKEN")
	ACCESS_TOKEN_SECRET = os.Getenv("ACCESS_TOKEN_SECRET")
	PROWL_API_KEY = os.Getenv("PROWL_API_KEY")
	if ng := stringUtils.IsAnyEmpty(CONSUMER_KEY, CONSUMER_KEY_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET, PROWL_API_KEY); ng {
		return fmt.Errorf("some key invalid")
	}

	return nil
}

func sendEventToProwl(e gomadare.Event) {

	n := &goprowl.Notification{
		Application: "Twitter",
		Description:  e.Event + " " + e.TargetObject.Text,
		Event:       e.Event + " " + e.Source.Name,
		Priority:    "1",
	}

	PROWL.Push(n)

}

func sendReplyAndRetweetToProwl(s gomadare.Status) {
	if s.Entities.UserMentions != nil{
		pp.Print(s.Entities.UserMentions)
	}
}
<|endoftext|>


<commit_before>package chef

import (
	"encoding/json"
	"fmt"
	"strings"
)

// chef.Node represents the relevant parameters of a Chef node
type Node struct {
	Name        string   `json:"name"`
	Environment string   `json:"chef_environment"`
	JSONClass   string   `json:"json_class"`
	RunList     []string `json:"run_list"`
	ChefType    string   `json:"chef_type"`
	Info        struct {
		Languages map[string]interface{} `json:"languages"`
		Kernel    struct {
			Name    string                            `json:"name"`
			Release string                            `json:"release"`
			Version string                            `json:"version"`
			Machine string                            `json:"machine"`
			Modules map[string]map[string]interface{} `json:"modules"`
		} `json:"kernel"`
		OS        string `json:"os"`
		OSVersion string `json:"os_version"`
		Hostname  string `json:"hostname"`
		FQDN      string `json:"fqdn"`
		Domain    string `json:"domain"`
		Network   struct {
			Interfaces map[string]struct {
				Type          string `json:"type"`
				Encapsulation string `json:"encapsulation"`
				Addresses     map[string]struct {
					Family    string `json:"family"`
					Broadcast string `json:"broadcast"`
					Netmast   string `json:"netmast"`
				} `json:"addresses"`
				Routes []struct {
					Destination string `json:"destination"`
					Family      string `json:"family"`
					Metric      string `json:"metric"`
				} `json:"routes"`
				State string            `json:"state"`
				Flags []string          `json:"flags"`
				MTU   string            `json:"mtu"`
				Arp   map[string]string `json:"arp"`
			} `json:"interfaces"`
			DefaultInterface string `json:"default_interface"`
			DefaultGateway   string `json:"default_gateway"`
		} `json:"network"`
		IPAddress       string                       `json:"ipaddress"`
		MACAddress      string                       `json:"macaddress"`
		ChefPackages    map[string]map[string]string `json:"chef_packages"`
		Keys            map[string]map[string]string `json:"keys"`
		Platform        string                       `json:"platform"`
		PlatformVersion string                       `json:"platform_version"`
		PlatformFamily  string                       `json:"platform_family"`
		CPU             map[string]interface{}       `json:"cpu"`
		Filesystem      map[string]struct {
			KBSize       interface{} `json:"ks_size"`
			KBUsed       interface{} `json:"ks_used"`
			KBavailable  interface{} `json:"ks_available"`
			PercentUsed  interface{} `json:"percent_used"`
			Mount        string      `json:"mount"`
			FSType       string      `json:"fs_type"`
			MountOptions []string    `json:"mount_options"`
		} `json:"filesystem"`
		Memory          map[string]interface{} `json:"memory"`
		UptimeSeconds   int                    `json:"uptime_seconds"`
		Uptime          string                 `json:"uptime"`
		IdletimeSeconds int                    `json:"idletime_seconds"`
		Idletime        string                 `json:"idletime"`
		BlockDevice     map[string]interface{} `json:"block_device"`
		Recipes         []string               `json:"recipes"`
		Roles           []string               `json:"roles"`
	} `json:"automatic"`
	Normal  map[string]interface{} `json:"normal"`
	Default map[string]interface{} `json:"default"`
}

// chef.GetNodes returns a map of nodes names to the nodes's RESTful URL as well
// as an error indicating if the request was successful or not.
//
// Usage:
//
//     nodes, err := chef.GetNodes()
//     if err != nil {
//         fmt.Println(err)
//         os.Exit(1)
//     }
//     // do what you please with the "node" variable which is a map of
//     // node names to node URLs
//     for node := range nodes {
//         fmt.Println(node)
//      }
func (chef *Chef) GetNodes() (map[string]string, error) {
	resp, err := chef.Get("nodes")
	if err != nil {
		return nil, err
	}
	body, err := responseBody(resp)
	if err != nil {
		return nil, err
	}

	nodes := map[string]string{}
	json.Unmarshal(body, &nodes)

	return nodes, nil
}

// chef.GetNode accepts a string which represents the name of a Chef role and
// returns a chef.Environment type representing that role as well as a bool
// indicating whether or not the role was found and an error indicating if the
// request failed or not.
//
// Note that if the request is successful but no such client existed, the error
// return value will be nil but the bool will be false.
//
// Usage:
//
//     node, ok, err := chef.GetNode("neo4j.example.com")
//     if err != nil {
//         fmt.Println(err)
//         os.Exit(1)
//     }
//     if !ok {
//         fmt.Println("Couldn't find that node!")
//     } else {
//         // do what you please with the "node" variable which is of the
//         // *Chef.Node type
//         fmt.Printf("%#v\n", node)
//     }
func (chef *Chef) GetNode(name string) (*Node, bool, error) {
	resp, err := chef.Get(fmt.Sprintf("nodes/%s", name))
	if err != nil {
		return nil, false, err
	}
	body, err := responseBody(resp)
	if err != nil {
		if strings.Contains(err.Error(), "404") {
			return nil, false, nil
		}
		return nil, false, err
	}

	node := new(Node)
	json.Unmarshal(body, node)

	return node, true, nil
}
<commit_msg>Adding a field to get the override attributes as well<commit_after>package chef

import (
	"encoding/json"
	"fmt"
	"strings"
)

// chef.Node represents the relevant parameters of a Chef node
type Node struct {
	Name        string   `json:"name"`
	Environment string   `json:"chef_environment"`
	JSONClass   string   `json:"json_class"`
	RunList     []string `json:"run_list"`
	ChefType    string   `json:"chef_type"`
	Info        struct {
		Languages map[string]interface{} `json:"languages"`
		Kernel    struct {
			Name    string                            `json:"name"`
			Release string                            `json:"release"`
			Version string                            `json:"version"`
			Machine string                            `json:"machine"`
			Modules map[string]map[string]interface{} `json:"modules"`
		} `json:"kernel"`
		OS        string `json:"os"`
		OSVersion string `json:"os_version"`
		Hostname  string `json:"hostname"`
		FQDN      string `json:"fqdn"`
		Domain    string `json:"domain"`
		Network   struct {
			Interfaces map[string]struct {
				Type          string `json:"type"`
				Encapsulation string `json:"encapsulation"`
				Addresses     map[string]struct {
					Family    string `json:"family"`
					Broadcast string `json:"broadcast"`
					Netmast   string `json:"netmast"`
				} `json:"addresses"`
				Routes []struct {
					Destination string `json:"destination"`
					Family      string `json:"family"`
					Metric      string `json:"metric"`
				} `json:"routes"`
				State string            `json:"state"`
				Flags []string          `json:"flags"`
				MTU   string            `json:"mtu"`
				Arp   map[string]string `json:"arp"`
			} `json:"interfaces"`
			DefaultInterface string `json:"default_interface"`
			DefaultGateway   string `json:"default_gateway"`
		} `json:"network"`
		IPAddress       string                       `json:"ipaddress"`
		MACAddress      string                       `json:"macaddress"`
		ChefPackages    map[string]map[string]string `json:"chef_packages"`
		Keys            map[string]map[string]string `json:"keys"`
		Platform        string                       `json:"platform"`
		PlatformVersion string                       `json:"platform_version"`
		PlatformFamily  string                       `json:"platform_family"`
		CPU             map[string]interface{}       `json:"cpu"`
		Filesystem      map[string]struct {
			KBSize       interface{} `json:"ks_size"`
			KBUsed       interface{} `json:"ks_used"`
			KBavailable  interface{} `json:"ks_available"`
			PercentUsed  interface{} `json:"percent_used"`
			Mount        string      `json:"mount"`
			FSType       string      `json:"fs_type"`
			MountOptions []string    `json:"mount_options"`
		} `json:"filesystem"`
		Memory          map[string]interface{} `json:"memory"`
		UptimeSeconds   int                    `json:"uptime_seconds"`
		Uptime          string                 `json:"uptime"`
		IdletimeSeconds int                    `json:"idletime_seconds"`
		Idletime        string                 `json:"idletime"`
		BlockDevice     map[string]interface{} `json:"block_device"`
		Recipes         []string               `json:"recipes"`
		Roles           []string               `json:"roles"`
	} `json:"automatic"`
	Default  map[string]interface{} `json:"default"`
	Normal   map[string]interface{} `json:"normal"`
	Override map[string]interface{} `json:"override"`
}

// chef.GetNodes returns a map of nodes names to the nodes's RESTful URL as well
// as an error indicating if the request was successful or not.
//
// Usage:
//
//     nodes, err := chef.GetNodes()
//     if err != nil {
//         fmt.Println(err)
//         os.Exit(1)
//     }
//     // do what you please with the "node" variable which is a map of
//     // node names to node URLs
//     for node := range nodes {
//         fmt.Println(node)
//      }
func (chef *Chef) GetNodes() (map[string]string, error) {
	resp, err := chef.Get("nodes")
	if err != nil {
		return nil, err
	}
	body, err := responseBody(resp)
	if err != nil {
		return nil, err
	}

	nodes := map[string]string{}
	json.Unmarshal(body, &nodes)

	return nodes, nil
}

// chef.GetNode accepts a string which represents the name of a Chef role and
// returns a chef.Environment type representing that role as well as a bool
// indicating whether or not the role was found and an error indicating if the
// request failed or not.
//
// Note that if the request is successful but no such client existed, the error
// return value will be nil but the bool will be false.
//
// Usage:
//
//     node, ok, err := chef.GetNode("neo4j.example.com")
//     if err != nil {
//         fmt.Println(err)
//         os.Exit(1)
//     }
//     if !ok {
//         fmt.Println("Couldn't find that node!")
//     } else {
//         // do what you please with the "node" variable which is of the
//         // *Chef.Node type
//         fmt.Printf("%#v\n", node)
//     }
func (chef *Chef) GetNode(name string) (*Node, bool, error) {
	resp, err := chef.Get(fmt.Sprintf("nodes/%s", name))
	if err != nil {
		return nil, false, err
	}
	body, err := responseBody(resp)
	if err != nil {
		if strings.Contains(err.Error(), "404") {
			return nil, false, nil
		}
		return nil, false, err
	}

	node := new(Node)
	json.Unmarshal(body, node)

	return node, true, nil
}
<|endoftext|>


<commit_before>//
// Copyright © 2011-2013 Guy M. Allard
//
// 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.
//

// Show a number of queue writers and readers operating concurrently.
// Try to be realistic about workloads.
// Receiver checks messages for proper queue and message number.

/*
Send and receive many STOMP messages using multiple queues and goroutines
to service each send or receive instance.  All senders share a single
STOMP connection, as do all receivers.
*/
package main

import (
	"flag"
	"fmt"
	"github.com/gmallard/stompngo"
	"github.com/gmallard/stompngo_examples/sngecomm"
	"log"
	"net"
	"os"
	"runtime"
	"runtime/pprof"
	"sync"
	"time"
)

var exampid = "srmgor_2conn:"

var wgsend sync.WaitGroup
var wgrecv sync.WaitGroup
var wgall sync.WaitGroup

// We 'stagger' between each message send and message receive for a random
// amount of time.
// Vary these for experimental purposes.  YMMV.
var max int64 = 1e9      // Max stagger time (nanoseconds)
var min int64 = max / 10 // Min stagger time (nanoseconds)

// Wait flags
var send_wait = true
var recv_wait = true

// Sleep multipliers
var sendFact float64 = 1.0
var recvFact float64 = 1.0

// Possible profile file
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

// Send messages to a particular queue
func sender(conn *stompngo.Connection, qn, c int) {
	qns := fmt.Sprintf("%d", qn) // queue number
	fmt.Println(sngecomm.ExampIdNow(exampid), "send starts", qn)
	//
	qp := sngecomm.Dest() // queue name prefix
	q := qp + "." + qns
	fmt.Println(sngecomm.ExampIdNow(exampid), "send queue name", q)
	h := stompngo.Headers{"destination", q,
		"qnum", qns} // send Headers
	if sngecomm.Persistent() {
		h = h.Add("persistent", "true")
	}
	// Send loop
	for i := 1; i <= c; i++ {
		si := fmt.Sprintf("%d", i)
		sh := append(h, "msgnum", si)
		// Generate a message to send ...............
		fmt.Println(sngecomm.ExampIdNow(exampid), "send message", qns, si)
		e := conn.Send(sh, string(sngecomm.Partial()))

		if e != nil {
			log.Fatalln(sngecomm.ExampIdNow(exampid), "send error", e, qn)
			break
		}
		if send_wait {
			runtime.Gosched() // yield for this example
			d := time.Duration(sngecomm.ValueBetween(min, max, sendFact))
			fmt.Println(sngecomm.ExampIdNow(exampid), "send", "sleep", int64(d)/1000000, "ms")
			time.Sleep(d) // Time to build next message
		}
	}
	// Sending is done
	fmt.Println(sngecomm.ExampIdNow(exampid), "send ends", qn)
	wgsend.Done()
}

// Asynchronously process all messages for a given subscription.
func receiveWorker(mc <-chan stompngo.MessageData, qns string, c int, d chan<- bool) {
	// Receive loop
	for i := 1; i <= c; i++ {
		d := <-mc
		if d.Error != nil {
			log.Fatalln(sngecomm.ExampIdNow(exampid), "recv read error", d.Error, qns)
		}

		// Sanity check the queue and message numbers
		mns := fmt.Sprintf("%d", i) // message number
		if !d.Message.Headers.ContainsKV("qnum", qns) || !d.Message.Headers.ContainsKV("msgnum", mns) {
			log.Fatalln("Bad Headers", d.Message.Headers, qns, mns)
		}

		// Process the inbound message .................
		sl := 16
		if len(d.Message.Body) < sl {
			sl = len(d.Message.Body)
		}
		fmt.Println(sngecomm.ExampIdNow(exampid), "recv message", string(d.Message.Body[0:sl]), qns, d.Message.Headers.Value("msgnum"))
		if i == c {
			break
		}
		if recv_wait {
			runtime.Gosched() // yield for this example
			d := time.Duration(sngecomm.ValueBetween(min, max, recvFact))
			fmt.Println(sngecomm.ExampIdNow(exampid), "recv", "sleep", int64(d)/1000000, "ms")
			time.Sleep(d) // Time to build next message
		}
	}
	//
	d <- true
}

// Receive messages from a particular queue
func receiver(conn *stompngo.Connection, qn, c int) {
	qns := fmt.Sprintf("%d", qn) // queue number
	fmt.Println(sngecomm.ExampIdNow(exampid), "recv starts", qn)
	//
	qp := sngecomm.Dest() // queue name prefix
	q := qp + "." + qns
	fmt.Println(sngecomm.ExampIdNow(exampid), "recv queue name", q, qn)
	id := stompngo.Uuid() // A unique subscription ID
	r := sngecomm.Subscribe(conn, q, id, "auto")
	// Many receivers running under the same connection can cause
	// (wire read) performance issues.  This is *very* dependent on the broker
	// being used, specifically the broker's algorithm for putting messages on
	// the wire.
	// To alleviate those issues, this strategy insures that messages are
	// received from the wire as soon as possible.  Those messages are then
	// buffered internally for (possibly later) application processing.

	// Process all inputs async .......
	var mc chan stompngo.MessageData
	nb := c // Buffer size
	if sngecomm.Conn2Buffer() > 0 {
		nb = sngecomm.Conn2Buffer() // User spec'd bufsize
	}
	fmt.Println(sngecomm.ExampIdNow(exampid), "recv", "mdbuffersize", nb, qns)
	mc = make(chan stompngo.MessageData, nb) // MessageData Buffer size
	dc := make(chan bool)                    // Receive processing done channel
	go receiveWorker(mc, qns, c, dc)         // Start async processor
	for i := 1; i <= c; i++ {
		mc <- <-r // Receive message data as soon as possible, and internally queue it
	}
	fmt.Println(sngecomm.ExampIdNow(exampid), "recv", "waitforWorkersBegin", qns)
	<-dc // Wait until receive processing is done for this queue
	fmt.Println(sngecomm.ExampIdNow(exampid), "recv", "waitforWorkersEnd", qns)

	// Unsubscribe
	sngecomm.Unsubscribe(conn, q, id)

	// Receiving is done
	fmt.Println(sngecomm.ExampIdNow(exampid), "recv ends", qns)
	wgrecv.Done()
}

func startSenders(qn int) {
	fmt.Println(sngecomm.ExampIdNow(exampid), "startSenders starts", qn)

	// Open
	h, p := sngecomm.HostAndPort() // host and port
	n, e := net.Dial("tcp", net.JoinHostPort(h, p))
	if e != nil {
		log.Fatalln(sngecomm.ExampIdNow(exampid), "startSenders netconnect error", e, qn) // Handle this ......
	}

	// Stomp connect
	ch := sngecomm.ConnectHeaders()
	log.Println(sngecomm.ExampIdNow(exampid), "startSenders", "vhost:", sngecomm.Vhost(), "protocol:", sngecomm.Protocol())
	conn, e := stompngo.Connect(n, ch)
	if e != nil {
		log.Fatalln(sngecomm.ExampIdNow(exampid), "startSenders stompconnect error", e, qn) // Handle this ......
	}
	log.Println(sngecomm.ExampIdNow(exampid), "startSenders connection", conn, qn)
	c := sngecomm.Nmsgs() // message count
	fmt.Println(sngecomm.ExampIdNow(exampid), "startSenders message count", c, qn)
	for i := 1; i <= qn; i++ { // all queues
		wgsend.Add(1)
		go sender(conn, i, c)
	}
	wgsend.Wait()

	// Disconnect from Stomp server
	e = conn.Disconnect(stompngo.Headers{})
	if e != nil {
		log.Println(sngecomm.ExampIdNow(exampid), "startSenders disconnect error", e, qn) // Handle this ......
	}
	// Network close
	e = n.Close()
	if e != nil {
		log.Fatalln(sngecomm.ExampIdNow(exampid), "startSenders netclose error", e, qn) // Handle this ......
	}

	fmt.Println(sngecomm.ExampIdNow(exampid), "startSenders ends", qn)
	sngecomm.ShowStats(sngecomm.ExampIdNow(exampid), "startSenders", conn)
	wgall.Done()
}

func startReceivers(qn int) {
	fmt.Println(sngecomm.ExampIdNow(exampid), "startReceivers starts", qn)

	// Open
	h, p := sngecomm.HostAndPort() // host and port
	n, e := net.Dial("tcp", net.JoinHostPort(h, p))
	if e != nil {
		log.Fatalln(sngecomm.ExampIdNow(exampid), "startReceivers nectonnr:", e, qn) // Handle this ......
	}
	ch := sngecomm.ConnectHeaders()
	log.Println(sngecomm.ExampIdNow(exampid), "startReceivers", "vhost:", sngecomm.Vhost(), "protocol:", sngecomm.Protocol())
	conn, e := stompngo.Connect(n, ch)
	if e != nil {
		log.Fatalln(sngecomm.ExampIdNow(exampid), "startReceivers stompconnectr:", e, qn) // Handle this ......
	}
	log.Println("startReceivers Receive connection is:", conn, qn)
	c := sngecomm.Nmsgs() // get message count
	fmt.Println(sngecomm.ExampIdNow(exampid), "startReceivers message count", c, qn)
	for i := 1; i <= qn; i++ { // all queues
		wgrecv.Add(1)
		go receiver(conn, i, c)
	}
	wgrecv.Wait()

	// Disconnect from Stomp server
	e = conn.Disconnect(stompngo.Headers{})
	if e != nil {
		log.Println(sngecomm.ExampIdNow(exampid), "startReceivers disconnect error", e, qn) // Handle this ......
	}
	// Network close
	e = n.Close()
	if e != nil {
		log.Println(sngecomm.ExampIdNow(exampid), "startReceivers netclose error", e, qn) // Handle this ......
	}

	fmt.Println(sngecomm.ExampIdNow(exampid), "startReceivers ends", qn)
	sngecomm.ShowStats(sngecomm.ExampIdNow(exampid), "startReceivers", conn)
	wgall.Done()
}

// Show a number of writers and readers operating concurrently from unique
// destinations.
func main() {
	tn := time.Now()
	fmt.Println(sngecomm.ExampIdNow(exampid), "main starts")

	flag.Parse()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if sngecomm.SetMAXPROCS() {
		nc := runtime.NumCPU()
		fmt.Println(sngecomm.ExampIdNow(exampid), "main number of CPUs is:", nc)
		c := runtime.GOMAXPROCS(nc)
		fmt.Println(sngecomm.ExampIdNow(exampid), "main previous number of GOMAXPROCS is:", c)
		fmt.Println(sngecomm.ExampIdNow(exampid), "main current number of GOMAXPROCS is:", runtime.GOMAXPROCS(-1))
	}
	//
	send_wait = sngecomm.SendWait()
	recv_wait = sngecomm.RecvWait()
	sendFact = sngecomm.SendFactor()
	recvFact = sngecomm.RecvFactor()
	fmt.Println(sngecomm.ExampIdNow(exampid), "Sleep Factors", "send", sendFact, "recv", recvFact)
	//
	q := sngecomm.Nqs()
	fmt.Println(sngecomm.ExampIdNow(exampid), "main Nqs:", q)
	//
	wgall.Add(2)
	go startReceivers(q)
	go startSenders(q)
	wgall.Wait()

	fmt.Println(sngecomm.ExampIdNow(exampid), "main ends", time.Since(tn))
}
<commit_msg>Convert time.Sleep to time.Timer.<commit_after>//
// Copyright © 2011-2013 Guy M. Allard
//
// 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.
//

// Show a number of queue writers and readers operating concurrently.
// Try to be realistic about workloads.
// Receiver checks messages for proper queue and message number.

/*
Send and receive many STOMP messages using multiple queues and goroutines
to service each send or receive instance.  All senders share a single
STOMP connection, as do all receivers.
*/
package main

import (
	"flag"
	"fmt"
	"github.com/gmallard/stompngo"
	"github.com/gmallard/stompngo_examples/sngecomm"
	"log"
	"net"
	"os"
	"runtime"
	"runtime/pprof"
	"sync"
	"time"
)

var exampid = "srmgor_2conn:"

var wgsend sync.WaitGroup
var wgrecv sync.WaitGroup
var wgall sync.WaitGroup

// We 'stagger' between each message send and message receive for a random
// amount of time.
// Vary these for experimental purposes.  YMMV.
var max int64 = 1e9      // Max stagger time (nanoseconds)
var min int64 = max / 10 // Min stagger time (nanoseconds)

// Wait flags
var send_wait = true
var recv_wait = true

// Sleep multipliers
var sendFact float64 = 1.0
var recvFact float64 = 1.0

// Possible profile file
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

// Send messages to a particular queue
func sender(conn *stompngo.Connection, qn, c int) {
	qns := fmt.Sprintf("%d", qn) // queue number
	fmt.Println(sngecomm.ExampIdNow(exampid), "send starts", qn)
	//
	qp := sngecomm.Dest() // queue name prefix
	q := qp + "." + qns
	fmt.Println(sngecomm.ExampIdNow(exampid), "send queue name", q)
	h := stompngo.Headers{"destination", q,
		"qnum", qns} // send Headers
	if sngecomm.Persistent() {
		h = h.Add("persistent", "true")
	}
	//
	tmr := time.NewTimer(100 * time.Hour)
	// Send loop
	for i := 1; i <= c; i++ {
		si := fmt.Sprintf("%d", i)
		sh := append(h, "msgnum", si)
		// Generate a message to send ...............
		fmt.Println(sngecomm.ExampIdNow(exampid), "send message", qns, si)
		e := conn.Send(sh, string(sngecomm.Partial()))

		if e != nil {
			log.Fatalln(sngecomm.ExampIdNow(exampid), "send error", e, qn)
			break
		}
		if send_wait {
			runtime.Gosched() // yield for this example
			d := time.Duration(sngecomm.ValueBetween(min, max, sendFact))
			fmt.Println(sngecomm.ExampIdNow(exampid), "send", "stagger", int64(d)/1000000, "ms")
			tmr.Reset(d)
			_ = <-tmr.C
		}
	}
	// Sending is done
	fmt.Println(sngecomm.ExampIdNow(exampid), "send ends", qn)
	wgsend.Done()
}

// Asynchronously process all messages for a given subscription.
func receiveWorker(mc <-chan stompngo.MessageData, qns string, c int, d chan<- bool) {
	//
	tmr := time.NewTimer(100 * time.Hour)
	// Receive loop
	for i := 1; i <= c; i++ {
		d := <-mc
		if d.Error != nil {
			log.Fatalln(sngecomm.ExampIdNow(exampid), "recv read error", d.Error, qns)
		}

		// Sanity check the queue and message numbers
		mns := fmt.Sprintf("%d", i) // message number
		if !d.Message.Headers.ContainsKV("qnum", qns) || !d.Message.Headers.ContainsKV("msgnum", mns) {
			log.Fatalln("Bad Headers", d.Message.Headers, qns, mns)
		}

		// Process the inbound message .................
		sl := 16
		if len(d.Message.Body) < sl {
			sl = len(d.Message.Body)
		}
		fmt.Println(sngecomm.ExampIdNow(exampid), "recv message", string(d.Message.Body[0:sl]), qns, d.Message.Headers.Value("msgnum"))
		if i == c {
			break
		}
		if recv_wait {
			runtime.Gosched() // yield for this example
			d := time.Duration(sngecomm.ValueBetween(min, max, recvFact))
			fmt.Println(sngecomm.ExampIdNow(exampid), "recv", "stagger", int64(d)/1000000, "ms")
			tmr.Reset(d)
			_ = <-tmr.C
		}
	}
	//
	d <- true
}

// Receive messages from a particular queue
func receiver(conn *stompngo.Connection, qn, c int) {
	qns := fmt.Sprintf("%d", qn) // queue number
	fmt.Println(sngecomm.ExampIdNow(exampid), "recv starts", qn)
	//
	qp := sngecomm.Dest() // queue name prefix
	q := qp + "." + qns
	fmt.Println(sngecomm.ExampIdNow(exampid), "recv queue name", q, qn)
	id := stompngo.Uuid() // A unique subscription ID
	r := sngecomm.Subscribe(conn, q, id, "auto")
	// Many receivers running under the same connection can cause
	// (wire read) performance issues.  This is *very* dependent on the broker
	// being used, specifically the broker's algorithm for putting messages on
	// the wire.
	// To alleviate those issues, this strategy insures that messages are
	// received from the wire as soon as possible.  Those messages are then
	// buffered internally for (possibly later) application processing.

	// Process all inputs async .......
	var mc chan stompngo.MessageData
	nb := c // Buffer size
	if sngecomm.Conn2Buffer() > 0 {
		nb = sngecomm.Conn2Buffer() // User spec'd bufsize
	}
	fmt.Println(sngecomm.ExampIdNow(exampid), "recv", "mdbuffersize", nb, qns)
	mc = make(chan stompngo.MessageData, nb) // MessageData Buffer size
	dc := make(chan bool)                    // Receive processing done channel
	go receiveWorker(mc, qns, c, dc)         // Start async processor
	for i := 1; i <= c; i++ {
		mc <- <-r // Receive message data as soon as possible, and internally queue it
	}
	fmt.Println(sngecomm.ExampIdNow(exampid), "recv", "waitforWorkersBegin", qns)
	<-dc // Wait until receive processing is done for this queue
	fmt.Println(sngecomm.ExampIdNow(exampid), "recv", "waitforWorkersEnd", qns)

	// Unsubscribe
	sngecomm.Unsubscribe(conn, q, id)

	// Receiving is done
	fmt.Println(sngecomm.ExampIdNow(exampid), "recv ends", qns)
	wgrecv.Done()
}

func startSenders(qn int) {
	fmt.Println(sngecomm.ExampIdNow(exampid), "startSenders starts", qn)

	// Open
	h, p := sngecomm.HostAndPort() // host and port
	n, e := net.Dial("tcp", net.JoinHostPort(h, p))
	if e != nil {
		log.Fatalln(sngecomm.ExampIdNow(exampid), "startSenders netconnect error", e, qn) // Handle this ......
	}

	// Stomp connect
	ch := sngecomm.ConnectHeaders()
	log.Println(sngecomm.ExampIdNow(exampid), "startSenders", "vhost:", sngecomm.Vhost(), "protocol:", sngecomm.Protocol())
	conn, e := stompngo.Connect(n, ch)
	if e != nil {
		log.Fatalln(sngecomm.ExampIdNow(exampid), "startSenders stompconnect error", e, qn) // Handle this ......
	}
	log.Println(sngecomm.ExampIdNow(exampid), "startSenders connection", conn, qn)
	c := sngecomm.Nmsgs() // message count
	fmt.Println(sngecomm.ExampIdNow(exampid), "startSenders message count", c, qn)
	for i := 1; i <= qn; i++ { // all queues
		wgsend.Add(1)
		go sender(conn, i, c)
	}
	wgsend.Wait()

	// Disconnect from Stomp server
	e = conn.Disconnect(stompngo.Headers{})
	if e != nil {
		log.Println(sngecomm.ExampIdNow(exampid), "startSenders disconnect error", e, qn) // Handle this ......
	}
	// Network close
	e = n.Close()
	if e != nil {
		log.Fatalln(sngecomm.ExampIdNow(exampid), "startSenders netclose error", e, qn) // Handle this ......
	}

	fmt.Println(sngecomm.ExampIdNow(exampid), "startSenders ends", qn)
	sngecomm.ShowStats(sngecomm.ExampIdNow(exampid), "startSenders", conn)
	wgall.Done()
}

func startReceivers(qn int) {
	fmt.Println(sngecomm.ExampIdNow(exampid), "startReceivers starts", qn)

	// Open
	h, p := sngecomm.HostAndPort() // host and port
	n, e := net.Dial("tcp", net.JoinHostPort(h, p))
	if e != nil {
		log.Fatalln(sngecomm.ExampIdNow(exampid), "startReceivers nectonnr:", e, qn) // Handle this ......
	}
	ch := sngecomm.ConnectHeaders()
	log.Println(sngecomm.ExampIdNow(exampid), "startReceivers", "vhost:", sngecomm.Vhost(), "protocol:", sngecomm.Protocol())
	conn, e := stompngo.Connect(n, ch)
	if e != nil {
		log.Fatalln(sngecomm.ExampIdNow(exampid), "startReceivers stompconnectr:", e, qn) // Handle this ......
	}
	log.Println("startReceivers Receive connection is:", conn, qn)
	c := sngecomm.Nmsgs() // get message count
	fmt.Println(sngecomm.ExampIdNow(exampid), "startReceivers message count", c, qn)
	for i := 1; i <= qn; i++ { // all queues
		wgrecv.Add(1)
		go receiver(conn, i, c)
	}
	wgrecv.Wait()

	// Disconnect from Stomp server
	e = conn.Disconnect(stompngo.Headers{})
	if e != nil {
		log.Println(sngecomm.ExampIdNow(exampid), "startReceivers disconnect error", e, qn) // Handle this ......
	}
	// Network close
	e = n.Close()
	if e != nil {
		log.Println(sngecomm.ExampIdNow(exampid), "startReceivers netclose error", e, qn) // Handle this ......
	}

	fmt.Println(sngecomm.ExampIdNow(exampid), "startReceivers ends", qn)
	sngecomm.ShowStats(sngecomm.ExampIdNow(exampid), "startReceivers", conn)
	wgall.Done()
}

// Show a number of writers and readers operating concurrently from unique
// destinations.
func main() {
	tn := time.Now()
	fmt.Println(sngecomm.ExampIdNow(exampid), "main starts")

	flag.Parse()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if sngecomm.SetMAXPROCS() {
		nc := runtime.NumCPU()
		fmt.Println(sngecomm.ExampIdNow(exampid), "main number of CPUs is:", nc)
		c := runtime.GOMAXPROCS(nc)
		fmt.Println(sngecomm.ExampIdNow(exampid), "main previous number of GOMAXPROCS is:", c)
		fmt.Println(sngecomm.ExampIdNow(exampid), "main current number of GOMAXPROCS is:", runtime.GOMAXPROCS(-1))
	}
	//
	send_wait = sngecomm.SendWait()
	recv_wait = sngecomm.RecvWait()
	sendFact = sngecomm.SendFactor()
	recvFact = sngecomm.RecvFactor()
	fmt.Println(sngecomm.ExampIdNow(exampid), "Sleep Factors", "send", sendFact, "recv", recvFact)
	//
	q := sngecomm.Nqs()
	fmt.Println(sngecomm.ExampIdNow(exampid), "main Nqs:", q)
	//
	wgall.Add(2)
	go startReceivers(q)
	go startSenders(q)
	wgall.Wait()

	fmt.Println(sngecomm.ExampIdNow(exampid), "main ends", time.Since(tn))
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build solaris

package terminal // import "golang.org/x/crypto/ssh/terminal"

import (
	"golang.org/x/sys/unix"
	"io"
	"syscall"
)

// State contains the state of a terminal.
type State struct {
	termios syscall.Termios
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
	// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
	var termio unix.Termio
	err := unix.IoctlSetTermio(fd, unix.TCGETA, &termio)
	return err == nil
}

// ReadPassword reads a line of input from a terminal without local echo.  This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd int) ([]byte, error) {
	// see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
	val, err := unix.IoctlGetTermios(fd, unix.TCGETS)
	if err != nil {
		return nil, err
	}
	oldState := *val

	newState := oldState
	newState.Lflag &^= syscall.ECHO
	newState.Lflag |= syscall.ICANON | syscall.ISIG
	newState.Iflag |= syscall.ICRNL
	err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState)
	if err != nil {
		return nil, err
	}

	defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState)

	var buf [16]byte
	var ret []byte
	for {
		n, err := syscall.Read(fd, buf[:])
		if err != nil {
			return nil, err
		}
		if n == 0 {
			if len(ret) == 0 {
				return nil, io.EOF
			}
			break
		}
		if buf[n-1] == '\n' {
			n--
		}
		ret = append(ret, buf[:n]...)
		if n < len(buf) {
			break
		}
	}

	return ret, nil
}
<commit_msg>ssh/terminal: implement missing functions for Solaris/OmniOS<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build solaris

package terminal // import "golang.org/x/crypto/ssh/terminal"

import (
	"golang.org/x/sys/unix"
	"io"
	"syscall"
)

// State contains the state of a terminal.
type State struct {
	state *unix.Termios
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
	_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
	return err == nil
}

// ReadPassword reads a line of input from a terminal without local echo.  This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd int) ([]byte, error) {
	// see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
	val, err := unix.IoctlGetTermios(fd, unix.TCGETS)
	if err != nil {
		return nil, err
	}
	oldState := *val

	newState := oldState
	newState.Lflag &^= syscall.ECHO
	newState.Lflag |= syscall.ICANON | syscall.ISIG
	newState.Iflag |= syscall.ICRNL
	err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState)
	if err != nil {
		return nil, err
	}

	defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState)

	var buf [16]byte
	var ret []byte
	for {
		n, err := syscall.Read(fd, buf[:])
		if err != nil {
			return nil, err
		}
		if n == 0 {
			if len(ret) == 0 {
				return nil, io.EOF
			}
			break
		}
		if buf[n-1] == '\n' {
			n--
		}
		ret = append(ret, buf[:n]...)
		if n < len(buf) {
			break
		}
	}

	return ret, nil
}

// MakeRaw puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
// see http://cr.illumos.org/~webrev/andy_js/1060/
func MakeRaw(fd int) (*State, error) {
	oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS)
	if err != nil {
		return nil, err
	}
	oldTermios := *oldTermiosPtr

	newTermios := oldTermios
	newTermios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
	newTermios.Oflag &^= syscall.OPOST
	newTermios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
	newTermios.Cflag &^= syscall.CSIZE | syscall.PARENB
	newTermios.Cflag |= syscall.CS8
	newTermios.Cc[unix.VMIN] = 1
	newTermios.Cc[unix.VTIME] = 0

	if err := unix.IoctlSetTermios(fd, unix.TCSETS, &newTermios); err != nil {
		return nil, err
	}

	return &State{
		state: oldTermiosPtr,
	}, nil
}

// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, oldState *State) error {
	return unix.IoctlSetTermios(fd, unix.TCSETS, oldState.state)
}

// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd int) (*State, error) {
	oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS)
	if err != nil {
		return nil, err
	}

	return &State{
		state: oldTermiosPtr,
	}, nil
}

// GetSize returns the dimensions of the given terminal.
func GetSize(fd int) (width, height int, err error) {
	ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
	if err != nil {
		return 0, 0, err
	}
	return int(ws.Col), int(ws.Row), nil
}
<|endoftext|>


<commit_before>package play

import (
	"context"
	"errors"
	"sync"
	"time"

	"github.com/CyCoreSystems/ari"
)

// Session describes a structured Play session.
type Session interface {
	// Add appends a set of AudioURIs to a Play session.  Note that if the Play session
	// has already been completed, this will NOT make it start again.
	Add(list ...string)

	// Done returns a channel which is closed when the Play session completes
	Done() <-chan struct{}

	// Err waits for a session to end and returns its error
	Err() error

	// StopAudio stops the playback of the audio sequence (if there is one), but
	// unlike `Stop()`, this does _not_ necessarily terminate the session.  If
	// the Play session is configured to wait for DTMF following the playback,
	// it will still wait after StopAudio() is called.
	StopAudio()

	// Result waits for a session to end and returns its result
	Result() (*Result, error)

	// Stop stops a Play session immediately
	Stop()
}

type playSession struct {
	o *Options

	// cancel is the playback context's cancel function
	cancel context.CancelFunc

	// currentSequence is a pointer to the currently-playing sequence, if there is one
	currentSequence *sequence

	// digitChan is the channel on which any received DTMF digits will be sent.  The received DTMF will also be stored separately, so this channel is primarily for signaling purposes.
	digitChan chan string

	// closed is a wrapper for done which indicates that done has been closed
	closed bool

	// done is a channel which is closed when the playback completes execution
	done chan struct{}

	// mu provides locking for concurrency-related datastructures within the options
	mu sync.Mutex

	// result is the final result of the playback
	result *Result
}

type nilSession struct {
	res *Result
}

func (n *nilSession) Add(list ...string) {
	return
}

func (n *nilSession) Done() <-chan struct{} {
	ch := make(chan struct{})
	close(ch)

	return ch
}

func (n *nilSession) Err() error {
	return n.res.Error
}

func (n *nilSession) StopAudio() {
	return
}

func (n *nilSession) Result() (*Result, error) {
	return n.res, n.res.Error
}

func (n *nilSession) Stop() {
	return
}

func errorSession(err error) *nilSession {
	s := &nilSession{
		res: new(Result),
	}

	s.res.Error = err
	s.res.Status = Failed
	return s
}

func newPlaySession(o *Options) *playSession {
	return &playSession{
		o:         o,
		result:    new(Result),
		digitChan: make(chan string, DigitBufferSize),
		done:      make(chan struct{}),
	}
}

func (s *playSession) play(ctx context.Context, p ari.Player) {
	ctx, cancel := context.WithCancel(ctx)
	s.cancel = cancel

	defer s.Stop()

	if s.result == nil {
		s.result = new(Result)
	}

	if s.o.uriList == nil || s.o.uriList.Empty() {
		s.result.Error = errors.New("empty playback URI list")
		return
	}

	// cancel if we go over the maximum time
	go s.watchMaxTime(ctx)

	// Listen for DTMF
	go s.listenDTMF(ctx, p)

	for i := 0; i < s.o.maxReplays+1; i++ {
		if ctx.Err() != nil {
			break
		}

		// Reset the digit cache
		s.result.mu.Lock()
		s.result.DTMF = ""
		s.result.mu.Unlock()

		// Play the sequence of audio URIs
		s.playSequence(ctx, p)
		if s.result.Error != nil {
			return
		}

		// Wait for digits in the silence after the playback sequence completes
		s.waitDigits(ctx)
	}

	return
}

// playSequence plays the complete audio sequence
func (s *playSession) playSequence(ctx context.Context, p ari.Player) {
	seq := newSequence(s)

	s.mu.Lock()
	s.currentSequence = seq
	s.mu.Unlock()

	go seq.Play(ctx, p)

	// Wait for sequence playback to complete (or context closure to be caught)
	select {
	case <-ctx.Done():
	case <-seq.Done():
		if s.result.Status == InProgress {
			s.result.Status = Finished
		}
	}

	// Stop audio playback if it is still running
	seq.Stop()

	// wait for cleanup of sequence so we can get the proper error result
	<-seq.Done()

	return
}

func (s *playSession) waitDigits(ctx context.Context) {
	overallTimer := time.NewTimer(s.o.overallDigitTimeout)
	defer overallTimer.Stop()

	digitTimeout := s.o.firstDigitTimeout

	for {
		select {
		case <-ctx.Done():
			return
		case <-time.After(digitTimeout):
			return
		case <-overallTimer.C:
			return
		case <-s.digitChan:
			if len(s.result.DTMF) > 0 {
				digitTimeout = s.o.interDigitTimeout
			}

			// Determine if a match was found
			if s.o.matchFunc != nil {
				s.result.mu.Lock()
				s.result.DTMF, s.result.MatchResult = s.o.matchFunc(s.result.DTMF)
				s.result.mu.Unlock()

				switch s.result.MatchResult {
				case Complete:
					// If we have a complete response, close the entire playback
					// and return
					s.Stop()
					return
				case Invalid:
					// If invalid, return without waiting
					// for any more digits
					return
				default:
					// Incomplete means we should wait for more
				}
			}
		}
	}

}

// Stop terminates the execution of a playback
func (s *playSession) Stop() {
	if s.result == nil {
		s.result = new(Result)
	}

	// Stop any audio which is still playing
	if s.currentSequence != nil {
		s.currentSequence.Stop()
		<-s.currentSequence.Done()
	}

	// If we have no other status set, set it to Cancelled
	if s.result.Status == InProgress {
		s.result.Status = Cancelled
	}

	// Close out anything else
	if s.cancel != nil {
		s.cancel()
	}

	if !s.closed {
		s.closed = true
		close(s.done)
	}
}

func (s *playSession) watchMaxTime(ctx context.Context) {
	select {
	case <-ctx.Done():
		return
	case <-time.After(s.o.maxPlaybackTime):
		s.Stop()
	}
}
func (s *playSession) listenDTMF(ctx context.Context, p ari.Player) {
	sub := p.Subscribe(ari.Events.ChannelDtmfReceived)
	defer sub.Cancel()

	for {
		select {
		case <-ctx.Done():
			return
		case e := <-sub.Events():
			if e == nil {
				return
			}
			v, ok := e.(*ari.ChannelDtmfReceived)
			if !ok {
				continue
			}
			s.result.mu.Lock()
			s.result.DTMF += v.Digit
			s.result.mu.Unlock()

			// Signal receipt of digit, but never block in doing so
			select {
			case s.digitChan <- v.Digit:
			default:
			}

			// If we have a MatchFunc, stop any playing audio
			if s.o.matchFunc != nil && s.currentSequence != nil {
				s.currentSequence.Stop()
			}
		}
	}
}
func (s *playSession) Add(list ...string) {
	for _, i := range list {
		s.o.uriList.Add(i)
	}
}

func (s *playSession) Done() <-chan struct{} {
	return s.done
}

func (s *playSession) Err() error {
	<-s.Done()
	return s.result.Error
}

func (s *playSession) StopAudio() {
	if s.currentSequence != nil {
		s.currentSequence.Stop()
		<-s.currentSequence.Done()
	}
}

func (s *playSession) Result() (*Result, error) {
	<-s.Done()
	return s.result, s.result.Error
}
<commit_msg>nolint ext/play/waitDigits<commit_after>package play

import (
	"context"
	"errors"
	"sync"
	"time"

	"github.com/CyCoreSystems/ari"
)

// Session describes a structured Play session.
type Session interface {
	// Add appends a set of AudioURIs to a Play session.  Note that if the Play session
	// has already been completed, this will NOT make it start again.
	Add(list ...string)

	// Done returns a channel which is closed when the Play session completes
	Done() <-chan struct{}

	// Err waits for a session to end and returns its error
	Err() error

	// StopAudio stops the playback of the audio sequence (if there is one), but
	// unlike `Stop()`, this does _not_ necessarily terminate the session.  If
	// the Play session is configured to wait for DTMF following the playback,
	// it will still wait after StopAudio() is called.
	StopAudio()

	// Result waits for a session to end and returns its result
	Result() (*Result, error)

	// Stop stops a Play session immediately
	Stop()
}

type playSession struct {
	o *Options

	// cancel is the playback context's cancel function
	cancel context.CancelFunc

	// currentSequence is a pointer to the currently-playing sequence, if there is one
	currentSequence *sequence

	// digitChan is the channel on which any received DTMF digits will be sent.  The received DTMF will also be stored separately, so this channel is primarily for signaling purposes.
	digitChan chan string

	// closed is a wrapper for done which indicates that done has been closed
	closed bool

	// done is a channel which is closed when the playback completes execution
	done chan struct{}

	// mu provides locking for concurrency-related datastructures within the options
	mu sync.Mutex

	// result is the final result of the playback
	result *Result
}

type nilSession struct {
	res *Result
}

func (n *nilSession) Add(list ...string) {
	return
}

func (n *nilSession) Done() <-chan struct{} {
	ch := make(chan struct{})
	close(ch)

	return ch
}

func (n *nilSession) Err() error {
	return n.res.Error
}

func (n *nilSession) StopAudio() {
	return
}

func (n *nilSession) Result() (*Result, error) {
	return n.res, n.res.Error
}

func (n *nilSession) Stop() {
	return
}

func errorSession(err error) *nilSession {
	s := &nilSession{
		res: new(Result),
	}

	s.res.Error = err
	s.res.Status = Failed
	return s
}

func newPlaySession(o *Options) *playSession {
	return &playSession{
		o:         o,
		result:    new(Result),
		digitChan: make(chan string, DigitBufferSize),
		done:      make(chan struct{}),
	}
}

func (s *playSession) play(ctx context.Context, p ari.Player) {
	ctx, cancel := context.WithCancel(ctx)
	s.cancel = cancel

	defer s.Stop()

	if s.result == nil {
		s.result = new(Result)
	}

	if s.o.uriList == nil || s.o.uriList.Empty() {
		s.result.Error = errors.New("empty playback URI list")
		return
	}

	// cancel if we go over the maximum time
	go s.watchMaxTime(ctx)

	// Listen for DTMF
	go s.listenDTMF(ctx, p)

	for i := 0; i < s.o.maxReplays+1; i++ {
		if ctx.Err() != nil {
			break
		}

		// Reset the digit cache
		s.result.mu.Lock()
		s.result.DTMF = ""
		s.result.mu.Unlock()

		// Play the sequence of audio URIs
		s.playSequence(ctx, p)
		if s.result.Error != nil {
			return
		}

		// Wait for digits in the silence after the playback sequence completes
		s.waitDigits(ctx)
	}

	return
}

// playSequence plays the complete audio sequence
func (s *playSession) playSequence(ctx context.Context, p ari.Player) {
	seq := newSequence(s)

	s.mu.Lock()
	s.currentSequence = seq
	s.mu.Unlock()

	go seq.Play(ctx, p)

	// Wait for sequence playback to complete (or context closure to be caught)
	select {
	case <-ctx.Done():
	case <-seq.Done():
		if s.result.Status == InProgress {
			s.result.Status = Finished
		}
	}

	// Stop audio playback if it is still running
	seq.Stop()

	// wait for cleanup of sequence so we can get the proper error result
	<-seq.Done()

	return
}

// nolint: gocyclo
func (s *playSession) waitDigits(ctx context.Context) {
	overallTimer := time.NewTimer(s.o.overallDigitTimeout)
	defer overallTimer.Stop()

	digitTimeout := s.o.firstDigitTimeout

	for {
		select {
		case <-ctx.Done():
			return
		case <-time.After(digitTimeout):
			return
		case <-overallTimer.C:
			return
		case <-s.digitChan:
			if len(s.result.DTMF) > 0 {
				digitTimeout = s.o.interDigitTimeout
			}

			// Determine if a match was found
			if s.o.matchFunc != nil {
				s.result.mu.Lock()
				s.result.DTMF, s.result.MatchResult = s.o.matchFunc(s.result.DTMF)
				s.result.mu.Unlock()

				switch s.result.MatchResult {
				case Complete:
					// If we have a complete response, close the entire playback
					// and return
					s.Stop()
					return
				case Invalid:
					// If invalid, return without waiting
					// for any more digits
					return
				default:
					// Incomplete means we should wait for more
				}
			}
		}
	}

}

// Stop terminates the execution of a playback
func (s *playSession) Stop() {
	if s.result == nil {
		s.result = new(Result)
	}

	// Stop any audio which is still playing
	if s.currentSequence != nil {
		s.currentSequence.Stop()
		<-s.currentSequence.Done()
	}

	// If we have no other status set, set it to Cancelled
	if s.result.Status == InProgress {
		s.result.Status = Cancelled
	}

	// Close out anything else
	if s.cancel != nil {
		s.cancel()
	}

	if !s.closed {
		s.closed = true
		close(s.done)
	}
}

func (s *playSession) watchMaxTime(ctx context.Context) {
	select {
	case <-ctx.Done():
		return
	case <-time.After(s.o.maxPlaybackTime):
		s.Stop()
	}
}
func (s *playSession) listenDTMF(ctx context.Context, p ari.Player) {
	sub := p.Subscribe(ari.Events.ChannelDtmfReceived)
	defer sub.Cancel()

	for {
		select {
		case <-ctx.Done():
			return
		case e := <-sub.Events():
			if e == nil {
				return
			}
			v, ok := e.(*ari.ChannelDtmfReceived)
			if !ok {
				continue
			}
			s.result.mu.Lock()
			s.result.DTMF += v.Digit
			s.result.mu.Unlock()

			// Signal receipt of digit, but never block in doing so
			select {
			case s.digitChan <- v.Digit:
			default:
			}

			// If we have a MatchFunc, stop any playing audio
			if s.o.matchFunc != nil && s.currentSequence != nil {
				s.currentSequence.Stop()
			}
		}
	}
}
func (s *playSession) Add(list ...string) {
	for _, i := range list {
		s.o.uriList.Add(i)
	}
}

func (s *playSession) Done() <-chan struct{} {
	return s.done
}

func (s *playSession) Err() error {
	<-s.Done()
	return s.result.Error
}

func (s *playSession) StopAudio() {
	if s.currentSequence != nil {
		s.currentSequence.Stop()
		<-s.currentSequence.Done()
	}
}

func (s *playSession) Result() (*Result, error) {
	<-s.Done()
	return s.result, s.result.Error
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Tigera, Inc. All rights reserved.
//
// 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 routetable

import (
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/projectcalico/felix/go/felix/conntrack"
	"github.com/projectcalico/felix/go/felix/ifacemonitor"
	"github.com/projectcalico/felix/go/felix/ip"
	"github.com/projectcalico/felix/go/felix/set"
	calinet "github.com/projectcalico/libcalico-go/lib/net"
	"github.com/vishvananda/netlink"
	"net"
	"regexp"
	"strings"
	"syscall"
)

var (
	ListFailed        = errors.New("netlink list operation failed")
	UpdateFailed      = errors.New("netlink update operation failed")
	IfaceDown         = errors.New("interface was down")
	IfaceNotPresent   = errors.New("interface not present")
	ipV6LinkLocalCIDR = ip.MustParseCIDR("fe80::/64")
)

type Target struct {
	CIDR    ip.CIDR
	DestMAC net.HardwareAddr
}

type RouteTable struct {
	logCxt *log.Entry

	ipVersion     uint8
	netlinkFamily int

	dirtyIfaces set.Set

	ifacePrefixes     set.Set
	ifacePrefixRegexp *regexp.Regexp

	ifaceNameToTargets        map[string][]Target
	pendingIfaceNameToTargets map[string][]Target

	inSync bool

	// dataplane is our shim for the netlink/arp interface.  In production, it maps directly
	// through to calls to the netlink package and the arp command.
	dataplane dataplaneIface
}

func New(interfacePrefixes []string, ipVersion uint8) *RouteTable {
	return NewWithShims(interfacePrefixes, ipVersion, realDataplane{})
}

// NewWithShims is a test constructor, which allows netlink to be replaced by a shim.
func NewWithShims(interfacePrefixes []string, ipVersion uint8, nl dataplaneIface) *RouteTable {
	prefixSet := set.New()
	regexpParts := []string{}
	for _, prefix := range interfacePrefixes {
		prefixSet.Add(prefix)
		regexpParts = append(regexpParts, "^"+prefix+".*")
	}

	ifaceNamePattern := strings.Join(regexpParts, "|")
	log.WithField("regex", ifaceNamePattern).Info("Calculated interface name regexp")

	family := netlink.FAMILY_V4
	if ipVersion == 6 {
		family = netlink.FAMILY_V6
	} else if ipVersion != 4 {
		log.WithField("ipVersion", ipVersion).Panic("Unknown IP version")
	}

	return &RouteTable{
		logCxt: log.WithFields(log.Fields{
			"ipVersion": ipVersion,
		}),
		ipVersion:                 ipVersion,
		netlinkFamily:             family,
		ifacePrefixes:             prefixSet,
		ifacePrefixRegexp:         regexp.MustCompile(ifaceNamePattern),
		ifaceNameToTargets:        map[string][]Target{},
		pendingIfaceNameToTargets: map[string][]Target{},
		dirtyIfaces:               set.New(),
		dataplane:                 nl,
	}
}

func (r *RouteTable) OnIfaceStateChanged(ifaceName string, state ifacemonitor.State) {
	logCxt := r.logCxt.WithField("ifaceName", ifaceName)
	if !r.ifacePrefixRegexp.MatchString(ifaceName) {
		logCxt.Debug("Ignoring interface state change, not a Calico interface.")
		return
	}
	if state == ifacemonitor.StateUp {
		logCxt.Debug("Interface up, marking for route sync")
		r.dirtyIfaces.Add(ifaceName)
	}
}

func (r *RouteTable) SetRoutes(ifaceName string, targets []Target) {
	r.pendingIfaceNameToTargets[ifaceName] = targets
	r.dirtyIfaces.Add(ifaceName)
}

func (r *RouteTable) Apply() error {
	if !r.inSync {
		links, err := r.dataplane.LinkList()
		if err != nil {
			r.logCxt.WithError(err).Error("Failed to list interfaces, retrying...")
			return ListFailed
		}
		// Clear the dirty set; there's no point trying to update non-existent interfaces.
		r.dirtyIfaces = set.New()
		for _, link := range links {
			attrs := link.Attrs()
			if attrs == nil {
				continue
			}
			ifaceName := attrs.Name
			if r.ifacePrefixRegexp.MatchString(ifaceName) {
				r.logCxt.WithField("ifaceName", ifaceName).Debug(
					"Resync: found calico-owned interface")
				r.dirtyIfaces.Add(ifaceName)
			}
		}
		r.inSync = true
	}

	r.dirtyIfaces.Iter(func(item interface{}) error {
		retries := 2
		ifaceName := item.(string)
		logCxt := r.logCxt.WithField("ifaceName", ifaceName)
		for retries > 0 {
			err := r.syncRoutesForLink(ifaceName)
			if err == IfaceNotPresent {
				logCxt.Info("Interface missing, will retry if it appears.")
				break
			} else if err != nil {
				logCxt.WithError(err).Warn("Failed to syncronise interface routes.")
				retries--
				continue
			}
			logCxt.Info("Synchronised routes on interface")
			break
		}
		if retries == 0 {
			// The interface might be flapping or being deleted.
			logCxt.Warn("Failed to sync routes to interface. Leaving it dirty.")
			return nil
		}
		return set.RemoveItem
	})

	if r.dirtyIfaces.Len() > 0 {
		r.logCxt.Warn("Some interfaces still out-of sync.")
		r.inSync = false
		return UpdateFailed
	}

	return nil
}

func (r *RouteTable) syncRoutesForLink(ifaceName string) error {
	logCxt := r.logCxt.WithField("ifaceName", ifaceName)
	logCxt.Debug("Syncing interface routes")

	// If this is a modify or delete, grab a copy of the existing targets so we can clean up
	// conntrack entries even if the routes have been removed.  We'll remove any still-required
	// CIDRs from this set below.
	oldCIDRs := set.New()
	if updatedTargets, ok := r.pendingIfaceNameToTargets[ifaceName]; ok {
		logCxt.Debug("Have updated targets.")
		oldTargets := r.ifaceNameToTargets[ifaceName]
		if updatedTargets == nil {
			delete(r.ifaceNameToTargets, ifaceName)
		} else {
			r.ifaceNameToTargets[ifaceName] = updatedTargets
		}
		for _, target := range oldTargets {
			oldCIDRs.Add(target.CIDR)
		}
		delete(r.pendingIfaceNameToTargets, ifaceName)
	}

	expectedTargets := r.ifaceNameToTargets[ifaceName]
	expectedCIDRs := set.New()
	for _, t := range expectedTargets {
		expectedCIDRs.Add(t.CIDR)
		oldCIDRs.Discard(t.CIDR)
	}
	if r.ipVersion == 6 {
		expectedCIDRs.Add(ipV6LinkLocalCIDR)
		oldCIDRs.Discard(ipV6LinkLocalCIDR)
	}

	updatesFailed := false
	linkFound := false

	link, err := r.dataplane.LinkByName(ifaceName)
	if err != nil {
		if !strings.Contains(err.Error(), "not found") {
			// Unexpected error, return it.
			logCxt.WithError(err).Error("Route sync failed: failed to get interface.")
			return err
		} else {
			logCxt.Info("Unable to sync interface routes, interface is not present.")
		}
	} else {
		// Got the link try to sync its routes.
		linkFound = true
		linkAttrs := link.Attrs()

		oldRoutes, err := r.dataplane.RouteList(link, r.netlinkFamily)
		if err != nil {
			logCxt.WithError(err).WithField("link", ifaceName).Error(
				"Failed to list routes. Interface may have been removed.")
			return ListFailed
		}

		seenCIDRs := set.New()
		for _, route := range oldRoutes {
			var dest ip.CIDR
			if route.Dst != nil {
				dest = ip.CIDRFromIPNet(calinet.IPNet{*route.Dst})
			}
			if !expectedCIDRs.Contains(dest) {
				logCxt := logCxt.WithField("dest", dest)
				logCxt.Info("Syncing routes: removing old route.")
				if err := r.dataplane.RouteDel(&route); err != nil {
					// Probably a race with the interface being deleted.
					logCxt.WithError(err).Info(
						"Route deletion failed, assuming someone got there first.")
					updatesFailed = true
				}
				if dest != nil {
					// Collect any old route CIDRs that we find in the dataplane so we
					// can remove their conntrack entries later.
					oldCIDRs.Add(dest)
				}
			}
			seenCIDRs.Add(dest)
		}
		for _, target := range expectedTargets {
			cidr := target.CIDR
			if !seenCIDRs.Contains(cidr) {
				logCxt := logCxt.WithField("targetCIDR", target.CIDR)
				logCxt.Info("Syncing routes: adding new route.")
				ipNet := cidr.ToIPNet()
				route := netlink.Route{
					LinkIndex: linkAttrs.Index,
					Dst:       &ipNet,
					Type:      syscall.RTN_UNICAST,
					Protocol:  syscall.RTPROT_BOOT,
					Scope:     netlink.SCOPE_LINK,
				}
				if err := r.dataplane.RouteAdd(&route); err != nil {
					logCxt.WithError(err).Warn("Failed to add route")
					updatesFailed = true
				}
			}
			if r.ipVersion == 4 && target.DestMAC != nil {
				// TODO(smc) clean up/sync old ARP entries
				err := r.dataplane.AddStaticArpEntry(cidr, target.DestMAC, ifaceName)
				if err != nil {
					logCxt.WithError(err).Warn("Failed to set ARP entry")
					updatesFailed = true
				}
			}
		}
	}

	// Now remove and conntrack entries that should no longer be there.
	oldCIDRs.Iter(func(item interface{}) error {
		dest := item.(ip.CIDR)
		conntrack.RemoveConntrackFlows(dest.Version(), dest.Addr().AsNetIP())
		return nil
	})

	if !linkFound {
		return IfaceNotPresent
	}
	if updatesFailed {
		return UpdateFailed
	}

	return nil
}
<commit_msg>Recheck interface existence to avoid logging errors during tear down.<commit_after>// Copyright (c) 2016-2017 Tigera, Inc. All rights reserved.
//
// 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 routetable

import (
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/projectcalico/felix/go/felix/conntrack"
	"github.com/projectcalico/felix/go/felix/ifacemonitor"
	"github.com/projectcalico/felix/go/felix/ip"
	"github.com/projectcalico/felix/go/felix/set"
	calinet "github.com/projectcalico/libcalico-go/lib/net"
	"github.com/vishvananda/netlink"
	"net"
	"regexp"
	"strings"
	"syscall"
)

var (
	ListFailed      = errors.New("netlink list operation failed")
	UpdateFailed    = errors.New("netlink update operation failed")
	IfaceNotPresent = errors.New("interface not present")

	ipV6LinkLocalCIDR = ip.MustParseCIDR("fe80::/64")
)

type Target struct {
	CIDR    ip.CIDR
	DestMAC net.HardwareAddr
}

type RouteTable struct {
	logCxt *log.Entry

	ipVersion     uint8
	netlinkFamily int

	dirtyIfaces set.Set

	ifacePrefixes     set.Set
	ifacePrefixRegexp *regexp.Regexp

	ifaceNameToTargets        map[string][]Target
	pendingIfaceNameToTargets map[string][]Target

	inSync bool

	// dataplane is our shim for the netlink/arp interface.  In production, it maps directly
	// through to calls to the netlink package and the arp command.
	dataplane dataplaneIface
}

func New(interfacePrefixes []string, ipVersion uint8) *RouteTable {
	return NewWithShims(interfacePrefixes, ipVersion, realDataplane{})
}

// NewWithShims is a test constructor, which allows netlink to be replaced by a shim.
func NewWithShims(interfacePrefixes []string, ipVersion uint8, nl dataplaneIface) *RouteTable {
	prefixSet := set.New()
	regexpParts := []string{}
	for _, prefix := range interfacePrefixes {
		prefixSet.Add(prefix)
		regexpParts = append(regexpParts, "^"+prefix+".*")
	}

	ifaceNamePattern := strings.Join(regexpParts, "|")
	log.WithField("regex", ifaceNamePattern).Info("Calculated interface name regexp")

	family := netlink.FAMILY_V4
	if ipVersion == 6 {
		family = netlink.FAMILY_V6
	} else if ipVersion != 4 {
		log.WithField("ipVersion", ipVersion).Panic("Unknown IP version")
	}

	return &RouteTable{
		logCxt: log.WithFields(log.Fields{
			"ipVersion": ipVersion,
		}),
		ipVersion:                 ipVersion,
		netlinkFamily:             family,
		ifacePrefixes:             prefixSet,
		ifacePrefixRegexp:         regexp.MustCompile(ifaceNamePattern),
		ifaceNameToTargets:        map[string][]Target{},
		pendingIfaceNameToTargets: map[string][]Target{},
		dirtyIfaces:               set.New(),
		dataplane:                 nl,
	}
}

func (r *RouteTable) OnIfaceStateChanged(ifaceName string, state ifacemonitor.State) {
	logCxt := r.logCxt.WithField("ifaceName", ifaceName)
	if !r.ifacePrefixRegexp.MatchString(ifaceName) {
		logCxt.Debug("Ignoring interface state change, not a Calico interface.")
		return
	}
	if state == ifacemonitor.StateUp {
		logCxt.Debug("Interface up, marking for route sync")
		r.dirtyIfaces.Add(ifaceName)
	}
}

func (r *RouteTable) SetRoutes(ifaceName string, targets []Target) {
	r.pendingIfaceNameToTargets[ifaceName] = targets
	r.dirtyIfaces.Add(ifaceName)
}

func (r *RouteTable) Apply() error {
	if !r.inSync {
		links, err := r.dataplane.LinkList()
		if err != nil {
			r.logCxt.WithError(err).Error("Failed to list interfaces, retrying...")
			return ListFailed
		}
		// Clear the dirty set; there's no point trying to update non-existent interfaces.
		r.dirtyIfaces = set.New()
		for _, link := range links {
			attrs := link.Attrs()
			if attrs == nil {
				continue
			}
			ifaceName := attrs.Name
			if r.ifacePrefixRegexp.MatchString(ifaceName) {
				r.logCxt.WithField("ifaceName", ifaceName).Debug(
					"Resync: found calico-owned interface")
				r.dirtyIfaces.Add(ifaceName)
			}
		}
		r.inSync = true
	}

	r.dirtyIfaces.Iter(func(item interface{}) error {
		retries := 2
		ifaceName := item.(string)
		logCxt := r.logCxt.WithField("ifaceName", ifaceName)
		for retries > 0 {
			err := r.syncRoutesForLink(ifaceName)
			if err == IfaceNotPresent {
				logCxt.Info("Interface missing, will retry if it appears.")
				break
			} else if err != nil {
				logCxt.WithError(err).Warn("Failed to syncronise interface routes.")
				retries--
				continue
			}
			logCxt.Info("Synchronised routes on interface")
			break
		}
		if retries == 0 {
			// The interface might be flapping or being deleted.
			logCxt.Warn("Failed to sync routes to interface. Leaving it dirty.")
			return nil
		}
		return set.RemoveItem
	})

	if r.dirtyIfaces.Len() > 0 {
		r.logCxt.Warn("Some interfaces still out-of sync.")
		r.inSync = false
		return UpdateFailed
	}

	return nil
}

func (r *RouteTable) syncRoutesForLink(ifaceName string) error {
	logCxt := r.logCxt.WithField("ifaceName", ifaceName)
	logCxt.Debug("Syncing interface routes")

	// If this is a modify or delete, grab a copy of the existing targets so we can clean up
	// conntrack entries even if the routes have been removed.  We'll remove any still-required
	// CIDRs from this set below.
	oldCIDRs := set.New()
	if updatedTargets, ok := r.pendingIfaceNameToTargets[ifaceName]; ok {
		logCxt.Debug("Have updated targets.")
		oldTargets := r.ifaceNameToTargets[ifaceName]
		if updatedTargets == nil {
			delete(r.ifaceNameToTargets, ifaceName)
		} else {
			r.ifaceNameToTargets[ifaceName] = updatedTargets
		}
		for _, target := range oldTargets {
			oldCIDRs.Add(target.CIDR)
		}
		delete(r.pendingIfaceNameToTargets, ifaceName)
	}

	expectedTargets := r.ifaceNameToTargets[ifaceName]
	expectedCIDRs := set.New()
	for _, t := range expectedTargets {
		expectedCIDRs.Add(t.CIDR)
		oldCIDRs.Discard(t.CIDR)
	}
	if r.ipVersion == 6 {
		expectedCIDRs.Add(ipV6LinkLocalCIDR)
		oldCIDRs.Discard(ipV6LinkLocalCIDR)
	}

	updatesFailed := false
	linkFound := false

	link, err := r.dataplane.LinkByName(ifaceName)
	if err != nil {
		if !strings.Contains(err.Error(), "not found") {
			// Unexpected error, return it.
			logCxt.WithError(err).Error("Route sync failed: failed to get interface.")
			return err
		} else {
			logCxt.Info("Unable to sync interface routes, interface is not present.")
		}
	} else {
		// Got the link try to sync its routes.
		linkFound = true
		linkAttrs := link.Attrs()

		oldRoutes, err := r.dataplane.RouteList(link, r.netlinkFamily)
		if err != nil {
			// Recheck whether the interface exists so we don't produce spammy logs
			// during interface removal.
			if exists, err2 := r.ifaceExists(ifaceName); exists || err2 != nil {
				logCxt.WithError(err).Error("Failed to list routes. Unable to sync.")
				return ListFailed
			} else {
				logCxt.Info("Link removed during sync.")
				return IfaceNotPresent
			}
		}

		seenCIDRs := set.New()
		for _, route := range oldRoutes {
			var dest ip.CIDR
			if route.Dst != nil {
				dest = ip.CIDRFromIPNet(calinet.IPNet{*route.Dst})
			}
			if !expectedCIDRs.Contains(dest) {
				logCxt := logCxt.WithField("dest", dest)
				logCxt.Info("Syncing routes: removing old route.")
				if err := r.dataplane.RouteDel(&route); err != nil {
					// Probably a race with the interface being deleted.
					logCxt.WithError(err).Info(
						"Route deletion failed, assuming someone got there first.")
					updatesFailed = true
				}
				if dest != nil {
					// Collect any old route CIDRs that we find in the dataplane so we
					// can remove their conntrack entries later.
					oldCIDRs.Add(dest)
				}
			}
			seenCIDRs.Add(dest)
		}
		for _, target := range expectedTargets {
			cidr := target.CIDR
			if !seenCIDRs.Contains(cidr) {
				logCxt := logCxt.WithField("targetCIDR", target.CIDR)
				logCxt.Info("Syncing routes: adding new route.")
				ipNet := cidr.ToIPNet()
				route := netlink.Route{
					LinkIndex: linkAttrs.Index,
					Dst:       &ipNet,
					Type:      syscall.RTN_UNICAST,
					Protocol:  syscall.RTPROT_BOOT,
					Scope:     netlink.SCOPE_LINK,
				}
				if err := r.dataplane.RouteAdd(&route); err != nil {
					logCxt.WithError(err).Warn("Failed to add route")
					updatesFailed = true
				}
			}
			if r.ipVersion == 4 && target.DestMAC != nil {
				// TODO(smc) clean up/sync old ARP entries
				err := r.dataplane.AddStaticArpEntry(cidr, target.DestMAC, ifaceName)
				if err != nil {
					logCxt.WithError(err).Warn("Failed to set ARP entry")
					updatesFailed = true
				}
			}
		}
	}

	// Now remove and conntrack entries that should no longer be there.
	oldCIDRs.Iter(func(item interface{}) error {
		dest := item.(ip.CIDR)
		conntrack.RemoveConntrackFlows(dest.Version(), dest.Addr().AsNetIP())
		return nil
	})

	if !linkFound {
		return IfaceNotPresent
	}
	if updatesFailed {
		// Recheck whether the interface exists so we don't produce spammy logs during
		// interface removal.
		exists, err := r.ifaceExists(ifaceName)
		if err == nil || exists {
			return UpdateFailed
		} else {
			return IfaceNotPresent
		}
	}

	return nil
}

func (r *RouteTable) ifaceExists(name string) (bool, error) {
	_, err := r.dataplane.LinkByName(name)
	if err == nil {
		return true, nil
	} else if strings.Contains(err.Error(), "not found") {
		return false, nil
	}
	return false, err
}
<|endoftext|>