<commit_before>package main

import (
	"fmt"
	"log"
	"strings"
	"sync"

	"os"

	"github.com/Shopify/sarama"
	cluster "github.com/bsm/sarama-cluster"
	docopt "github.com/docopt/docopt-go"
	"github.com/jurriaan/kafkatools"
)

var (
	version     = "0.1"
	gitrev      = "unknown"
	versionInfo = `reset_consumer_group %s (git rev %s)`
	usage       = `reset_consumer_group - a tool to reset the consumer group offset for a specific topic

usage:
  reset_consumer_group [options] <group> <topic>

options:
  -h --help          show this screen.
  --version          show version.
  --broker [broker]  the kafka bootstrap broker
`
)

func main() {
	docOpts, err := docopt.Parse(usage, nil, true, fmt.Sprintf(versionInfo, version, gitrev), false)

	if err != nil {
		log.Panicf("[PANIC] We couldn't parse doc opts params: %v", err)
	}

	if docOpts["--broker"] == nil {
		log.Fatal("You have to provide a broker")
	}
	broker := docOpts["--broker"].(string)
	topics := strings.Split(docOpts["<topic>"].(string), ",")
	consumerGroup := docOpts["<group>"].(string)

	client := kafkatools.GetSaramaClient(broker)
	consumer := kafkatools.GetSaramaConsumer(broker, consumerGroup, topics)
	defer func() {
		err := consumer.Close()
		if err != nil {
			log.Fatal("Could not properly close the consumer")
		}
	}()

	requests := kafkatools.GenerateOffsetRequests(client)

	var wg, wg2 sync.WaitGroup
	topicOffsetChannel := make(chan kafkatools.TopicPartitionOffset, 20)

	wg.Add(len(requests))
	for broker, request := range requests {
		// Fetch topic offsets (log end)
		go func(broker *sarama.Broker, request *sarama.OffsetRequest) {
			defer wg.Done()
			kafkatools.GetBrokerTopicOffsets(broker, request, topicOffsetChannel)
		}(broker, request)
	}

	// Setup lookup table for topic offsets
	topicOffsets := make(map[string]map[int32]kafkatools.TopicPartitionOffset)
	go func() {
		defer wg2.Done()
		wg2.Add(1)
		for topicOffset := range topicOffsetChannel {
			if _, ok := topicOffsets[topicOffset.Topic]; !ok {
				topicOffsets[topicOffset.Topic] = make(map[int32]kafkatools.TopicPartitionOffset)
			}
			topicOffsets[topicOffset.Topic][topicOffset.Partition] = topicOffset
		}
	}()

	// wait for goroutines to finish
	wg.Wait()
	close(topicOffsetChannel)
	wg2.Wait()
	// Init consumer, consume errors & messages

	go func() {
		for err := range consumer.Errors() {
			log.Printf("Error: %s\n", err.Error())
		}
	}()

	setConsumerOffsets(consumer, topics, topicOffsets)
}

func setConsumerOffsets(consumer *cluster.Consumer, topics []string, topicOffsets map[string]map[int32]kafkatools.TopicPartitionOffset) {
	log.Println("Waiting for consumer to join all partitions")
	log.Println("Make sure there are no other consumers listening for this to work")

	for note := range consumer.Notifications() {
		log.Printf("Rebalanced: %+v\n", note)
		if len(note.Current) != len(topics) {
			continue
		} else {
			for _, topic := range topics {
				if len(note.Current[topic]) != len(topicOffsets[topic]) {
					continue
				}
			}
		}

		for _, topic := range topics {
			topicPartitionOffset := topicOffsets[topic]
			for partition, offset := range topicPartitionOffset {
				log.Printf("Setting %s:%d's offset to %d", topic, partition, offset.Offset)
				consumer.MarkPartitionOffset(topic, partition, offset.Offset, "")
			}
		}

		err := consumer.CommitOffsets()
		if err != nil {
			log.Fatal("Error committing offsets", err)
		}

		log.Println("Quitting")
		os.Exit(0)
	}

}
<commit_msg>Properly close consumer and client<commit_after>package main

import (
	"fmt"
	"log"
	"strings"
	"sync"

	"github.com/Shopify/sarama"
	cluster "github.com/bsm/sarama-cluster"
	docopt "github.com/docopt/docopt-go"
	"github.com/jurriaan/kafkatools"
)

var (
	version     = "0.1"
	gitrev      = "unknown"
	versionInfo = `reset_consumer_group %s (git rev %s)`
	usage       = `reset_consumer_group - a tool to reset the consumer group offset for a specific topic

usage:
  reset_consumer_group [options] <group> <topic>

options:
  -h --help          show this screen.
  --version          show version.
  --broker [broker]  the kafka bootstrap broker
`
)

func main() {
	docOpts, err := docopt.Parse(usage, nil, true, fmt.Sprintf(versionInfo, version, gitrev), false)

	if err != nil {
		log.Panicf("[PANIC] We couldn't parse doc opts params: %v", err)
	}

	if docOpts["--broker"] == nil {
		log.Fatal("You have to provide a broker")
	}
	broker := docOpts["--broker"].(string)
	topics := strings.Split(docOpts["<topic>"].(string), ",")
	consumerGroup := docOpts["<group>"].(string)

	client := kafkatools.GetSaramaClient(broker)
	consumer := kafkatools.GetSaramaConsumer(broker, consumerGroup, topics)
	defer func() {
		err := consumer.Close()
		if err != nil {
			log.Fatal("Could not properly close the consumer")
		}
		err = client.Close()
		if err != nil {
			log.Fatal("Could not properly close the client")
		}
		log.Println("Connection closed. Bye.")
	}()

	requests := kafkatools.GenerateOffsetRequests(client)

	var wg, wg2 sync.WaitGroup
	topicOffsetChannel := make(chan kafkatools.TopicPartitionOffset, 20)

	wg.Add(len(requests))
	for broker, request := range requests {
		// Fetch topic offsets (log end)
		go func(broker *sarama.Broker, request *sarama.OffsetRequest) {
			defer wg.Done()
			kafkatools.GetBrokerTopicOffsets(broker, request, topicOffsetChannel)
		}(broker, request)
	}

	// Setup lookup table for topic offsets
	topicOffsets := make(map[string]map[int32]kafkatools.TopicPartitionOffset)
	go func() {
		defer wg2.Done()
		wg2.Add(1)
		for topicOffset := range topicOffsetChannel {
			if _, ok := topicOffsets[topicOffset.Topic]; !ok {
				topicOffsets[topicOffset.Topic] = make(map[int32]kafkatools.TopicPartitionOffset)
			}
			topicOffsets[topicOffset.Topic][topicOffset.Partition] = topicOffset
		}
	}()

	// wait for goroutines to finish
	wg.Wait()
	close(topicOffsetChannel)
	wg2.Wait()
	// Init consumer, consume errors & messages

	go func() {
		for err := range consumer.Errors() {
			log.Printf("Error: %s\n", err.Error())
		}
	}()

	setConsumerOffsets(consumer, topics, topicOffsets)
}

func setConsumerOffsets(consumer *cluster.Consumer, topics []string, topicOffsets map[string]map[int32]kafkatools.TopicPartitionOffset) {
	log.Println("Waiting for consumer to join all partitions")
	log.Println("Make sure there are no other consumers listening for this to work")

	for note := range consumer.Notifications() {
		log.Printf("Rebalanced: %+v\n", note)
		if len(note.Current) != len(topics) {
			continue
		} else {
			for _, topic := range topics {
				if len(note.Current[topic]) != len(topicOffsets[topic]) {
					continue
				}
			}
		}

		for _, topic := range topics {
			topicPartitionOffset := topicOffsets[topic]
			for partition, offset := range topicPartitionOffset {
				log.Printf("Setting %s:%d's offset to %d", topic, partition, offset.Offset)
				consumer.MarkPartitionOffset(topic, partition, offset.Offset, "")
			}
		}

		err := consumer.CommitOffsets()
		if err != nil {
			log.Fatal("Error committing offsets", err)
		}

		log.Println("Quitting")
		return
	}

}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"log"
	"os"

	_ "github.com/denisenkom/go-mssqldb"
	_ "github.com/go-sql-driver/mysql"
	_ "github.com/lib/pq"
	"github.com/spf13/pflag"

	"github.com/go-testfixtures/testfixtures/v3"
)

var version = "master"

const usage = `TODO`

func main() {
	log.SetFlags(0)
	log.SetOutput(os.Stderr)

	var (
		versionFlag bool
		dialect     string
		connString  string
		dir         string
		files       []string
	)

	pflag.BoolVarP(&versionFlag, "version", "v", false, "show testfixtures version")
	pflag.StringVarP(&dialect, "dialect", "d", "", "which database system you're using (postgres, timescaledb, mysql, mariadb, sqlite or sqlserver)")
	pflag.StringVarP(&connString, "conn", "c", "", "a database connection string")
	pflag.StringVarP(&dir, "dir", "D", "", "a directory of YAML fixtures to load")
	pflag.StringSliceVarP(&files, "files", "f", nil, "a list of YAML files to load")
	pflag.Parse()

	if versionFlag {
		log.Printf("testfixtures version: %s", version)
		return
	}

	if dialect == "" && connString == "" {
		log.Fatal("testfixtures: both --dialect (-d) and --conn (-c) are required")
		return
	}
	if dir == "" && len(files) == 0 {
		log.Fatal("testfixtures: either --dir (-D) or --files (-f) need to be given")
		return
	}
	if dir != "" && len(files) > 0 {
		log.Fatal("testfixtures: you can use --dir (-D) and --files (-f) together")
		return
	}

	dialect, err := getDialect(dialect)
	if err != nil {
		log.Fatal(err)
		return
	}

	db, err := sql.Open(dialect, connString)
	if err != nil {
		log.Fatalf("testfixtures: could not connect to database: %v", err)
		return
	}
	defer db.Close()

	if err := db.Ping(); err != nil {
		log.Fatalf("testfixtures: could not ping database: %v", err)
		return
	}

	options := []func(*testfixtures.Loader) error{
		testfixtures.Database(db),
		testfixtures.Dialect(dialect),
	}
	if dir != "" {
		options = append(options, testfixtures.Directory(dir))
	} else {
		options = append(options, testfixtures.Files(files...))
	}
	loader, err := testfixtures.New(options...)
	if err != nil {
		log.Fatal(err)
	}
	if err := loader.Load(); err != nil {
		log.Fatal(err)
	}
	log.Printf("testfixtures: fixtures loaded successfully")
}

func getDialect(dialect string) (string, error) {
	switch dialect {
	case "postgres", "postgresql", "timescaledb":
		return "postgres", nil
	case "mysql", "mariadb":
		return "mysql", nil
	case "sqlite", "sqlite3":
		return "sqlite3", nil
	case "mssql", "sqlserver":
		return "mssql", nil
	default:
		return "", fmt.Errorf(`testfixtures: unrecognized dialect "%s"`, dialect)
	}
}
<commit_msg>v3: CLI: Let's reserve the -v alias for a possible future --verbose flag<commit_after>package main

import (
	"database/sql"
	"fmt"
	"log"
	"os"

	_ "github.com/denisenkom/go-mssqldb"
	_ "github.com/go-sql-driver/mysql"
	_ "github.com/lib/pq"
	"github.com/spf13/pflag"

	"github.com/go-testfixtures/testfixtures/v3"
)

var version = "master"

const usage = `TODO`

func main() {
	log.SetFlags(0)
	log.SetOutput(os.Stderr)

	var (
		versionFlag bool
		dialect     string
		connString  string
		dir         string
		files       []string
	)

	pflag.BoolVar(&versionFlag, "version", false, "show testfixtures version")
	pflag.StringVarP(&dialect, "dialect", "d", "", "which database system you're using (postgres, timescaledb, mysql, mariadb, sqlite or sqlserver)")
	pflag.StringVarP(&connString, "conn", "c", "", "a database connection string")
	pflag.StringVarP(&dir, "dir", "D", "", "a directory of YAML fixtures to load")
	pflag.StringSliceVarP(&files, "files", "f", nil, "a list of YAML files to load")
	pflag.Parse()

	if versionFlag {
		log.Printf("testfixtures version: %s", version)
		return
	}

	if dialect == "" && connString == "" {
		log.Fatal("testfixtures: both --dialect (-d) and --conn (-c) are required")
		return
	}
	if dir == "" && len(files) == 0 {
		log.Fatal("testfixtures: either --dir (-D) or --files (-f) need to be given")
		return
	}
	if dir != "" && len(files) > 0 {
		log.Fatal("testfixtures: you can use --dir (-D) and --files (-f) together")
		return
	}

	dialect, err := getDialect(dialect)
	if err != nil {
		log.Fatal(err)
		return
	}

	db, err := sql.Open(dialect, connString)
	if err != nil {
		log.Fatalf("testfixtures: could not connect to database: %v", err)
		return
	}
	defer db.Close()

	if err := db.Ping(); err != nil {
		log.Fatalf("testfixtures: could not ping database: %v", err)
		return
	}

	options := []func(*testfixtures.Loader) error{
		testfixtures.Database(db),
		testfixtures.Dialect(dialect),
	}
	if dir != "" {
		options = append(options, testfixtures.Directory(dir))
	} else {
		options = append(options, testfixtures.Files(files...))
	}
	loader, err := testfixtures.New(options...)
	if err != nil {
		log.Fatal(err)
	}
	if err := loader.Load(); err != nil {
		log.Fatal(err)
	}
	log.Printf("testfixtures: fixtures loaded successfully")
}

func getDialect(dialect string) (string, error) {
	switch dialect {
	case "postgres", "postgresql", "timescaledb":
		return "postgres", nil
	case "mysql", "mariadb":
		return "mysql", nil
	case "sqlite", "sqlite3":
		return "sqlite3", nil
	case "mssql", "sqlserver":
		return "mssql", nil
	default:
		return "", fmt.Errorf(`testfixtures: unrecognized dialect "%s"`, dialect)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"io"
	"net"

	"github.com/hashicorp/scada-client/scada"
	"github.com/hashicorp/vault/version"
)

type SCADAListener struct {
	ln            net.Listener
	scadaProvider *scada.Provider
}

func (s *SCADAListener) Accept() (net.Conn, error) {
	return s.ln.Accept()
}

func (s *SCADAListener) Close() error {
	s.scadaProvider.Shutdown()
	return s.ln.Close()
}

func (s *SCADAListener) Addr() net.Addr {
	return s.ln.Addr()
}

func atlasListenerFactory(config map[string]string, logger io.Writer) (net.Listener, map[string]string, ReloadFunc, error) {
	scadaConfig := &scada.Config{
		Service:      "vault",
		Version:      version.GetVersion().String(),
		ResourceType: "vault-cluster",
		Meta: map[string]string{
			"node_id": config["node_id"],
		},
		Atlas: scada.AtlasConfig{
			Endpoint:       config["endpoint"],
			Infrastructure: config["infrastructure"],
			Token:          config["token"],
		},
	}

	provider, list, err := scada.NewHTTPProvider(scadaConfig, logger)
	if err != nil {
		return nil, nil, nil, err
	}

	ln := &SCADAListener{
		ln:            list,
		scadaProvider: provider,
	}

	props := map[string]string{
		"addr":           "Atlas/SCADA",
		"infrastructure": scadaConfig.Atlas.Infrastructure,
	}

	return listenerWrapTLS(ln, props, config)
}
<commit_msg>Report the simple version string<commit_after>package server

import (
	"io"
	"net"

	"github.com/hashicorp/scada-client/scada"
	"github.com/hashicorp/vault/version"
)

type SCADAListener struct {
	ln            net.Listener
	scadaProvider *scada.Provider
}

func (s *SCADAListener) Accept() (net.Conn, error) {
	return s.ln.Accept()
}

func (s *SCADAListener) Close() error {
	s.scadaProvider.Shutdown()
	return s.ln.Close()
}

func (s *SCADAListener) Addr() net.Addr {
	return s.ln.Addr()
}

func atlasListenerFactory(config map[string]string, logger io.Writer) (net.Listener, map[string]string, ReloadFunc, error) {
	scadaConfig := &scada.Config{
		Service:      "vault",
		Version:      version.GetVersion().Version,
		ResourceType: "vault-cluster",
		Meta: map[string]string{
			"node_id": config["node_id"],
		},
		Atlas: scada.AtlasConfig{
			Endpoint:       config["endpoint"],
			Infrastructure: config["infrastructure"],
			Token:          config["token"],
		},
	}

	provider, list, err := scada.NewHTTPProvider(scadaConfig, logger)
	if err != nil {
		return nil, nil, nil, err
	}

	ln := &SCADAListener{
		ln:            list,
		scadaProvider: provider,
	}

	props := map[string]string{
		"addr":           "Atlas/SCADA",
		"infrastructure": scadaConfig.Atlas.Infrastructure,
	}

	return listenerWrapTLS(ln, props, config)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/coreos/go-etcd/etcd"
)

type stringslice []string

func (self *stringslice) String() string {
	return fmt.Sprintf("%d", *self)
}

func (self *stringslice) Set(value string) error {
	*self = append(*self, value)
	return nil
}

var port uint
var blocksize uint64
var etcd_nodes stringslice

func init() {
	flag.UintVar(&port, "port", 9000, "Port to run HTTP server on")
	flag.Uint64Var(&blocksize, "blocksize", 64, "Block size")
	flag.Var(&etcd_nodes, "etcd", "Etcd server")
	flag.Parse()
}

type Req interface{}

type IncReq struct {
	id  int
	ret chan uint64
}

type DelReq struct {
	id int
}

type Nexter struct {
	reqchan chan Req
	done    chan bool
}

func (self *Nexter) countloop(ch chan uint64, id int, client *etcd.Client) {
	var start uint64
	var end uint64
	path := "nexter/" + strconv.Itoa(id)

	for {
		node, err := client.Get(path, false, false)
		if err != nil {
			ee, ok := err.(*etcd.EtcdError)
			if ok && ee.ErrorCode == 100 { // node does not exist
				_, err := client.Create(path, "0", 0)
				if err != nil {
					ee, ok := err.(*etcd.EtcdError)
					// Catch race condition where another node did the same client.Create()
					if ok && ee.ErrorCode == 105 { // Node has been created
						continue
					} else {
						log.Fatal(err)
					}
				}
				continue
			} else {
				log.Fatal(err)
			}
		} else { // No error, get start of series from etcd node
			start, err = strconv.ParseUint(node.Node.Value, 10, 0)
			if err != nil {
				log.Fatal(err)
			}
			end = start + blocksize
		}
		for {
			newval, err := client.CompareAndSwap(path, strconv.FormatUint(end, 10), 0, strconv.FormatUint(start, 10), 0)
			if err == nil {
				break
			} else {
				log.Println("Error with CompareAndSet! Trying again in 1 second...")
				time.Sleep(time.Second)
				start, err = strconv.ParseUint(newval.Node.Value, 10, 0)
				if err != nil {
					log.Fatal(err)
				}
				end = start + blocksize
			}
		}
		for c := start; c < end; c += 1 {
			ch <- c
		}
	}
}

func (self *Nexter) loop() {
	counters := make(map[int]chan uint64)
	client := etcd.NewClient(etcd_nodes)
	for {
		select {
		case req := <-self.reqchan:
			switch req.(type) {
			case *IncReq:
				countreq := req.(*IncReq)
				counter, ok := counters[countreq.id]
				if !ok {
					counter = make(chan uint64)
					counters[countreq.id] = counter
					go self.countloop(counter, countreq.id, client)
				}
				go func() { countreq.ret <- <-counter }()
			case *DelReq:
				delreq := req.(*DelReq)
				delete(counters, delreq.id)
				path := "nexter/" + strconv.Itoa(delreq.id)
				client.Delete(path, true)
			default:
			}
		case <-self.done:
			break
		}
	}
}

func (self *Nexter) GetCount(id int) uint64 {
	ret := make(chan uint64)
	self.reqchan <- &IncReq{id, ret}
	return <-ret
}
func (self *Nexter) Delete(id int) {
	self.reqchan <- &DelReq{id}
}
func (self *Nexter) Stop() {
	self.done <- true
}

func NewNexter() *Nexter {
	nexter := &Nexter{make(chan Req), make(chan bool)}
	go nexter.loop()
	return nexter
}

func main() {
	log.Println("Starting Nexter...")
	nexter := NewNexter()
	http.HandleFunc("/nexter/", func(w http.ResponseWriter, r *http.Request) {
		url := r.URL.Path
		splits := strings.Split(url, "/")
		if len(splits) < 3 {
			http.Error(w, "Missing property id", http.StatusBadRequest)
			return
		}
		id_string := splits[2]
		id, err := strconv.Atoi(id_string)
		if err != nil {
			http.Error(w, "Property id is not a number", http.StatusBadRequest)
			return
		}

		num := nexter.GetCount(id)
		dec := json.NewEncoder(w)
		dec.Encode(num)
	})
	port_string := strconv.FormatUint(uint64(port), 10)
	http.ListenAndServe(":"+port_string, nil)
}
<commit_msg>Simplify logic, fix potential nil pointer dereference<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/coreos/go-etcd/etcd"
)

type stringslice []string

func (self *stringslice) String() string {
	return fmt.Sprintf("%d", *self)
}

func (self *stringslice) Set(value string) error {
	*self = append(*self, value)
	return nil
}

var port uint
var blocksize uint64
var etcd_nodes stringslice

func init() {
	flag.UintVar(&port, "port", 9000, "Port to run HTTP server on")
	flag.Uint64Var(&blocksize, "blocksize", 64, "Block size")
	flag.Var(&etcd_nodes, "etcd", "Etcd server")
	flag.Parse()
}

type Req interface{}

type IncReq struct {
	id  int
	ret chan uint64
}

type DelReq struct {
	id int
}

type Nexter struct {
	reqchan chan Req
	done    chan bool
}

func (self *Nexter) countloop(ch chan uint64, id int, client *etcd.Client) {
	var start uint64
	var end uint64
	path := "nexter/" + strconv.Itoa(id)

	for {
		node, err := client.Get(path, false, false)
		if err != nil {
			ee, ok := err.(*etcd.EtcdError)
			if ok && ee.ErrorCode == 100 { // node does not exist
				_, err := client.Create(path, "0", 0)
				if err != nil {
					ee, ok := err.(*etcd.EtcdError)
					// Catch race condition where another node did the same client.Create()
					if ok && ee.ErrorCode == 105 { // Node has been created
						continue
					} else {
						log.Fatal(err)
					}
				}
				continue
			} else {
				log.Fatal(err)
			}
		} else { // No error, get start of series from etcd node
			start, err = strconv.ParseUint(node.Node.Value, 10, 0)
			if err != nil {
				log.Fatal(err)
			}
			end = start + blocksize
		}
		_, err = client.CompareAndSwap(path, strconv.FormatUint(end, 10), 0, strconv.FormatUint(start, 10), 0)
		if err != nil {
			log.Println("Error with CompareAndSet! Trying again in 1 second...")
			time.Sleep(time.Second)
			continue
		}
		for c := start; c < end; c += 1 {
			ch <- c
		}
	}
}

func (self *Nexter) loop() {
	counters := make(map[int]chan uint64)
	client := etcd.NewClient(etcd_nodes)
	for {
		select {
		case req := <-self.reqchan:
			switch req.(type) {
			case *IncReq:
				countreq := req.(*IncReq)
				counter, ok := counters[countreq.id]
				if !ok {
					counter = make(chan uint64)
					counters[countreq.id] = counter
					go self.countloop(counter, countreq.id, client)
				}
				go func() { countreq.ret <- <-counter }()
			case *DelReq:
				delreq := req.(*DelReq)
				delete(counters, delreq.id)
				path := "nexter/" + strconv.Itoa(delreq.id)
				client.Delete(path, true)
			default:
			}
		case <-self.done:
			break
		}
	}
}

func (self *Nexter) GetCount(id int) uint64 {
	ret := make(chan uint64)
	self.reqchan <- &IncReq{id, ret}
	return <-ret
}
func (self *Nexter) Delete(id int) {
	self.reqchan <- &DelReq{id}
}
func (self *Nexter) Stop() {
	self.done <- true
}

func NewNexter() *Nexter {
	nexter := &Nexter{make(chan Req), make(chan bool)}
	go nexter.loop()
	return nexter
}

func main() {
	log.Println("Starting Nexter...")
	nexter := NewNexter()
	http.HandleFunc("/nexter/", func(w http.ResponseWriter, r *http.Request) {
		url := r.URL.Path
		splits := strings.Split(url, "/")
		if len(splits) < 3 {
			http.Error(w, "Missing property id", http.StatusBadRequest)
			return
		}
		id_string := splits[2]
		id, err := strconv.Atoi(id_string)
		if err != nil {
			http.Error(w, "Property id is not a number", http.StatusBadRequest)
			return
		}

		num := nexter.GetCount(id)
		dec := json.NewEncoder(w)
		dec.Encode(num)
	})
	port_string := strconv.FormatUint(uint64(port), 10)
	http.ListenAndServe(":"+port_string, nil)
}
<|endoftext|>


<commit_before>package aws

import (
	"bytes"
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsVolumeAttachment() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsVolumeAttachmentCreate,
		Read:   resourceAwsVolumeAttachmentRead,
		Delete: resourceAwsVolumeAttachmentDelete,

		Schema: map[string]*schema.Schema{
			"device_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"instance_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"volume_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"force_detach": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func resourceAwsVolumeAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	name := d.Get("device_name").(string)
	iID := d.Get("instance_id").(string)
	vID := d.Get("volume_id").(string)

	opts := &ec2.AttachVolumeInput{
		Device:     aws.String(name),
		InstanceId: aws.String(iID),
		VolumeId:   aws.String(vID),
	}

	log.Printf("[DEBUG] Attaching Volume (%s) to Instance (%s)", vID, iID)
	_, err := conn.AttachVolume(opts)
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok {
			return fmt.Errorf("[WARN] Error attaching volume (%s) to instance (%s), message: \"%s\", code: \"%s\"",
				vID, iID, awsErr.Message(), awsErr.Code())
		}
		return err
	}

	stateConf := &resource.StateChangeConf{
		Pending:    []string{"attaching"},
		Target:     []string{"attached"},
		Refresh:    volumeAttachmentStateRefreshFunc(conn, vID, iID),
		Timeout:    5 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	_, err = stateConf.WaitForState()
	if err != nil {
		return fmt.Errorf(
			"Error waiting for Volume (%s) to attach to Instance: %s, error: %s",
			vID, iID, err)
	}

	d.SetId(volumeAttachmentID(name, vID, iID))
	return resourceAwsVolumeAttachmentRead(d, meta)
}

func volumeAttachmentStateRefreshFunc(conn *ec2.EC2, volumeID, instanceID string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {

		request := &ec2.DescribeVolumesInput{
			VolumeIds: []*string{aws.String(volumeID)},
			Filters: []*ec2.Filter{
				&ec2.Filter{
					Name:   aws.String("attachment.instance-id"),
					Values: []*string{aws.String(instanceID)},
				},
			},
		}

		resp, err := conn.DescribeVolumes(request)
		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				return nil, "failed", fmt.Errorf("code: %s, message: %s", awsErr.Code(), awsErr.Message())
			}
			return nil, "failed", err
		}

		if len(resp.Volumes) > 0 {
			v := resp.Volumes[0]
			for _, a := range v.Attachments {
				if a.InstanceId != nil && *a.InstanceId == instanceID {
					return a, *a.State, nil
				}
			}
		}
		// assume detached if volume count is 0
		return 42, "detached", nil
	}
}
func resourceAwsVolumeAttachmentRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	request := &ec2.DescribeVolumesInput{
		VolumeIds: []*string{aws.String(d.Get("volume_id").(string))},
		Filters: []*ec2.Filter{
			&ec2.Filter{
				Name:   aws.String("attachment.instance-id"),
				Values: []*string{aws.String(d.Get("instance_id").(string))},
			},
		},
	}

	_, err := conn.DescribeVolumes(request)
	if err != nil {
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVolume.NotFound" {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading EC2 volume %s for instance: %s: %#v", d.Get("volume_id").(string), d.Get("instance_id").(string), err)
	}
	return nil
}

func resourceAwsVolumeAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	vID := d.Get("volume_id").(string)
	iID := d.Get("instance_id").(string)

	opts := &ec2.DetachVolumeInput{
		Device:     aws.String(d.Get("device_name").(string)),
		InstanceId: aws.String(iID),
		VolumeId:   aws.String(vID),
		Force:      aws.Bool(d.Get("force_detach").(bool)),
	}

	_, err := conn.DetachVolume(opts)
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"detaching"},
		Target:     []string{"detached"},
		Refresh:    volumeAttachmentStateRefreshFunc(conn, vID, iID),
		Timeout:    5 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	log.Printf("[DEBUG] Detaching Volume (%s) from Instance (%s)", vID, iID)
	_, err = stateConf.WaitForState()
	if err != nil {
		return fmt.Errorf(
			"Error waiting for Volume (%s) to detach from Instance: %s",
			vID, iID)
	}
	d.SetId("")
	return nil
}

func volumeAttachmentID(name, volumeID, instanceID string) string {
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("%s-", name))
	buf.WriteString(fmt.Sprintf("%s-", instanceID))
	buf.WriteString(fmt.Sprintf("%s-", volumeID))

	return fmt.Sprintf("vai-%d", hashcode.String(buf.String()))
}
<commit_msg>provider/aws: Propagate errors from DetachVolume<commit_after>package aws

import (
	"bytes"
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsVolumeAttachment() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsVolumeAttachmentCreate,
		Read:   resourceAwsVolumeAttachmentRead,
		Delete: resourceAwsVolumeAttachmentDelete,

		Schema: map[string]*schema.Schema{
			"device_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"instance_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"volume_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"force_detach": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func resourceAwsVolumeAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	name := d.Get("device_name").(string)
	iID := d.Get("instance_id").(string)
	vID := d.Get("volume_id").(string)

	opts := &ec2.AttachVolumeInput{
		Device:     aws.String(name),
		InstanceId: aws.String(iID),
		VolumeId:   aws.String(vID),
	}

	log.Printf("[DEBUG] Attaching Volume (%s) to Instance (%s)", vID, iID)
	_, err := conn.AttachVolume(opts)
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok {
			return fmt.Errorf("[WARN] Error attaching volume (%s) to instance (%s), message: \"%s\", code: \"%s\"",
				vID, iID, awsErr.Message(), awsErr.Code())
		}
		return err
	}

	stateConf := &resource.StateChangeConf{
		Pending:    []string{"attaching"},
		Target:     []string{"attached"},
		Refresh:    volumeAttachmentStateRefreshFunc(conn, vID, iID),
		Timeout:    5 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	_, err = stateConf.WaitForState()
	if err != nil {
		return fmt.Errorf(
			"Error waiting for Volume (%s) to attach to Instance: %s, error: %s",
			vID, iID, err)
	}

	d.SetId(volumeAttachmentID(name, vID, iID))
	return resourceAwsVolumeAttachmentRead(d, meta)
}

func volumeAttachmentStateRefreshFunc(conn *ec2.EC2, volumeID, instanceID string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {

		request := &ec2.DescribeVolumesInput{
			VolumeIds: []*string{aws.String(volumeID)},
			Filters: []*ec2.Filter{
				&ec2.Filter{
					Name:   aws.String("attachment.instance-id"),
					Values: []*string{aws.String(instanceID)},
				},
			},
		}

		resp, err := conn.DescribeVolumes(request)
		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				return nil, "failed", fmt.Errorf("code: %s, message: %s", awsErr.Code(), awsErr.Message())
			}
			return nil, "failed", err
		}

		if len(resp.Volumes) > 0 {
			v := resp.Volumes[0]
			for _, a := range v.Attachments {
				if a.InstanceId != nil && *a.InstanceId == instanceID {
					return a, *a.State, nil
				}
			}
		}
		// assume detached if volume count is 0
		return 42, "detached", nil
	}
}
func resourceAwsVolumeAttachmentRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	request := &ec2.DescribeVolumesInput{
		VolumeIds: []*string{aws.String(d.Get("volume_id").(string))},
		Filters: []*ec2.Filter{
			&ec2.Filter{
				Name:   aws.String("attachment.instance-id"),
				Values: []*string{aws.String(d.Get("instance_id").(string))},
			},
		},
	}

	_, err := conn.DescribeVolumes(request)
	if err != nil {
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVolume.NotFound" {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading EC2 volume %s for instance: %s: %#v", d.Get("volume_id").(string), d.Get("instance_id").(string), err)
	}
	return nil
}

func resourceAwsVolumeAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	vID := d.Get("volume_id").(string)
	iID := d.Get("instance_id").(string)

	opts := &ec2.DetachVolumeInput{
		Device:     aws.String(d.Get("device_name").(string)),
		InstanceId: aws.String(iID),
		VolumeId:   aws.String(vID),
		Force:      aws.Bool(d.Get("force_detach").(bool)),
	}

	_, err := conn.DetachVolume(opts)
	if err != nil {
		return fmt.Errorf("Failed to detach Volume (%s) from Instance (%s): %s",
			vID, iID, err)
	}
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"detaching"},
		Target:     []string{"detached"},
		Refresh:    volumeAttachmentStateRefreshFunc(conn, vID, iID),
		Timeout:    5 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	log.Printf("[DEBUG] Detaching Volume (%s) from Instance (%s)", vID, iID)
	_, err = stateConf.WaitForState()
	if err != nil {
		return fmt.Errorf(
			"Error waiting for Volume (%s) to detach from Instance: %s",
			vID, iID)
	}
	d.SetId("")
	return nil
}

func volumeAttachmentID(name, volumeID, instanceID string) string {
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("%s-", name))
	buf.WriteString(fmt.Sprintf("%s-", instanceID))
	buf.WriteString(fmt.Sprintf("%s-", volumeID))

	return fmt.Sprintf("vai-%d", hashcode.String(buf.String()))
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 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 rest

import (
	"fmt"
	"net/http"
	"sync"

	access "github.com/ligato/cn-infra/rpc/rest/security/model/access-security"

	"github.com/ligato/vpp-agent/plugins/linux"

	"git.fd.io/govpp.git/api"
	"github.com/ligato/cn-infra/infra"
	"github.com/ligato/cn-infra/rpc/rest"
	"github.com/ligato/cn-infra/utils/safeclose"
	"github.com/ligato/vpp-agent/plugins/govppmux"
	iflinuxcalls "github.com/ligato/vpp-agent/plugins/linux/ifplugin/linuxcalls"
	l3linuxcalls "github.com/ligato/vpp-agent/plugins/linux/l3plugin/linuxcalls"
	"github.com/ligato/vpp-agent/plugins/rest/resturl"
	"github.com/ligato/vpp-agent/plugins/vpp"
	aclvppcalls "github.com/ligato/vpp-agent/plugins/vpp/aclplugin/vppcalls"
	ifvppcalls "github.com/ligato/vpp-agent/plugins/vpp/ifplugin/vppcalls"
	ipsecvppcalls "github.com/ligato/vpp-agent/plugins/vpp/ipsecplugin/vppcalls"
	l2vppcalls "github.com/ligato/vpp-agent/plugins/vpp/l2plugin/vppcalls"
	l3vppcalls "github.com/ligato/vpp-agent/plugins/vpp/l3plugin/vppcalls"
	l4vppcalls "github.com/ligato/vpp-agent/plugins/vpp/l4plugin/vppcalls"
)

// REST api methods
const (
	GET  = "GET"
	POST = "POST"
)

// Plugin registers Rest Plugin
type Plugin struct {
	Deps

	// Index page
	index *index

	// Channels
	vppChan  api.Channel
	dumpChan api.Channel

	// VPP Handlers
	aclHandler   aclvppcalls.ACLVppRead
	ifHandler    ifvppcalls.IfVppRead
	bfdHandler   ifvppcalls.BfdVppRead
	natHandler   ifvppcalls.NatVppRead
	stnHandler   ifvppcalls.StnVppRead
	ipSecHandler ipsecvppcalls.IPSecVPPRead
	bdHandler    l2vppcalls.BridgeDomainVppRead
	fibHandler   l2vppcalls.FibVppRead
	xcHandler    l2vppcalls.XConnectVppRead
	arpHandler   l3vppcalls.ArpVppRead
	pArpHandler  l3vppcalls.ProxyArpVppRead
	rtHandler    l3vppcalls.RouteVppRead
	l4Handler    l4vppcalls.L4VppRead
	// Linux handlers
	linuxIfHandler iflinuxcalls.NetlinkAPI
	linuxL3Handler l3linuxcalls.NetlinkAPI

	govppmux sync.Mutex
}

// Deps represents dependencies of Rest Plugin
type Deps struct {
	infra.PluginDeps
	HTTPHandlers rest.HTTPHandlers
	GoVppmux     govppmux.TraceAPI
	VPP          vpp.API
	Linux        linux.API
}

// index defines map of main index page entries
type index struct {
	ItemMap map[string][]indexItem
}

// indexItem is single index page entry
type indexItem struct {
	Name string
	Path string
}

// Init initializes the Rest Plugin
func (plugin *Plugin) Init() (err error) {
	// Check VPP dependency
	if plugin.VPP == nil {
		return fmt.Errorf("REST plugin requires VPP plugin API")
	}
	// VPP channels
	if plugin.vppChan, err = plugin.GoVppmux.NewAPIChannel(); err != nil {
		return err
	}
	if plugin.dumpChan, err = plugin.GoVppmux.NewAPIChannel(); err != nil {
		return err
	}
	// VPP Indexes
	ifIndexes := plugin.VPP.GetSwIfIndexes()
	bdIndexes := plugin.VPP.GetBDIndexes()
	spdIndexes := plugin.VPP.GetIPSecSPDIndexes()
	// Initialize VPP handlers
	plugin.aclHandler = aclvppcalls.NewACLVppHandler(plugin.vppChan, plugin.dumpChan)
	plugin.ifHandler = ifvppcalls.NewIfVppHandler(plugin.vppChan, plugin.Log)
	plugin.bfdHandler = ifvppcalls.NewBfdVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.natHandler = ifvppcalls.NewNatVppHandler(plugin.vppChan, plugin.dumpChan, ifIndexes, plugin.Log)
	plugin.stnHandler = ifvppcalls.NewStnVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.ipSecHandler = ipsecvppcalls.NewIPsecVppHandler(plugin.vppChan, ifIndexes, spdIndexes, plugin.Log)
	plugin.bdHandler = l2vppcalls.NewBridgeDomainVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.fibHandler = l2vppcalls.NewFibVppHandler(plugin.vppChan, plugin.dumpChan, ifIndexes, bdIndexes, plugin.Log)
	plugin.xcHandler = l2vppcalls.NewXConnectVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.arpHandler = l3vppcalls.NewArpVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.pArpHandler = l3vppcalls.NewProxyArpVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.rtHandler = l3vppcalls.NewRouteVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.l4Handler = l4vppcalls.NewL4VppHandler(plugin.vppChan, plugin.Log)
	// Linux indexes and handlers
	if plugin.Linux != nil {
		linuxIfIndexes := plugin.Linux.GetLinuxIfIndexes()
		linuxArpIndexes := plugin.Linux.GetLinuxARPIndexes()
		linuxRtIndexes := plugin.Linux.GetLinuxRouteIndexes()
		// Initialize Linux handlers
		linuxNsHandler := plugin.Linux.GetNamespaceHandler()
		plugin.linuxIfHandler = iflinuxcalls.NewNetLinkHandler(linuxNsHandler, linuxIfIndexes, plugin.Log)
		plugin.linuxL3Handler = l3linuxcalls.NewNetLinkHandler(linuxNsHandler, linuxIfIndexes, linuxArpIndexes, linuxRtIndexes, plugin.Log)
	}

	plugin.index = &index{
		ItemMap: getIndexMap(),
	}

	// Register permission groups, used if REST security is enabled
	plugin.HTTPHandlers.RegisterPermissionGroup(getPermissionsGroups()...)

	return nil
}

// AfterInit is used to register HTTP handlers
func (plugin *Plugin) AfterInit() (err error) {
	plugin.Log.Debug("REST API Plugin is up and running")

	// VPP handlers
	plugin.registerAccessListHandlers()
	plugin.registerInterfaceHandlers()
	plugin.registerBfdHandlers()
	plugin.registerNatHandlers()
	plugin.registerStnHandlers()
	plugin.registerIPSecHandlers()
	plugin.registerL2Handlers()
	plugin.registerL3Handlers()
	plugin.registerL4Handlers()
	// Linux handlers
	if plugin.Linux != nil {
		plugin.registerLinuxInterfaceHandlers()
		plugin.registerLinuxL3Handlers()
	}
	// Telemetry, command, index, tracer
	plugin.registerTracerHandler()
	plugin.registerTelemetryHandlers()
	plugin.registerCommandHandler()
	plugin.registerIndexHandlers()

	return nil
}

// Close is used to clean up resources used by Plugin
func (plugin *Plugin) Close() (err error) {
	return safeclose.Close(plugin.vppChan, plugin.dumpChan)
}

// Fill index item lists
func getIndexMap() map[string][]indexItem {
	idxMap := map[string][]indexItem{
		"ACL plugin": {
			{Name: "IP-type access lists", Path: resturl.ACLIP},
			{Name: "MACIP-type access lists", Path: resturl.ACLMACIP},
		},
		"Interface plugin": {
			{Name: "All interfaces", Path: resturl.Interface},
			{Name: "Loopbacks", Path: resturl.Loopback},
			{Name: "Ethernets", Path: resturl.Ethernet},
			{Name: "Memifs", Path: resturl.Memif},
			{Name: "Taps", Path: resturl.Tap},
			{Name: "VxLANs", Path: resturl.VxLan},
			{Name: "Af-packets", Path: resturl.AfPacket},
		},
		"IPSec plugin": {
			{Name: "Security policy databases", Path: resturl.IPSecSpd},
			{Name: "Security associations", Path: resturl.IPSecSa},
			{Name: "Tunnel interfaces", Path: resturl.IPSecTnIf},
		},
		"L2 plugin": {
			{Name: "Bridge domains", Path: resturl.Bd},
			{Name: "Bridge domain IDs", Path: resturl.BdID},
			{Name: "L2Fibs", Path: resturl.Fib},
			{Name: "Cross connects", Path: resturl.Xc},
		},
		"L3 plugin": {
			{Name: "Routes", Path: resturl.Routes},
			{Name: "ARPs", Path: resturl.Arps},
			{Name: "Proxy ARP interfaces", Path: resturl.PArpIfs},
			{Name: "Proxy ARP ranges", Path: resturl.PArpRngs},
		},
		"L4 plugin": {
			{Name: "L4 sessions", Path: resturl.Sessions},
		},
		"Telemetry": {
			{Name: "All data", Path: resturl.Telemetry},
			{Name: "Memory", Path: resturl.TMemory},
			{Name: "Runtime", Path: resturl.TRuntime},
			{Name: "Node count", Path: resturl.TNodeCount},
		},
		"Tracer": {
			{Name: "Binary API", Path: resturl.Tracer},
		},
	}
	return idxMap
}

// Create permission groups (tracer, telemetry, dump - optionally add more in the future). Used only if
// REST security is enabled in plugin
func getPermissionsGroups() []*access.PermissionGroup {
	tracerPg := &access.PermissionGroup{
		Name: "tracer",
		Permissions: []*access.PermissionGroup_Permissions{
			{
				Url:            resturl.Index,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.Tracer,
				AllowedMethods: []string{http.MethodGet},
			},
		},
	}
	telemetryPg := &access.PermissionGroup{
		Name: "telemetry",
		Permissions: []*access.PermissionGroup_Permissions{
			{
				Url:            resturl.Index,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.Telemetry,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.TMemory,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.TRuntime,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.TNodeCount,
				AllowedMethods: []string{http.MethodGet},
			},
		},
	}
	dumpPg := &access.PermissionGroup{
		Name: "dump",
		Permissions: []*access.PermissionGroup_Permissions{
			{
				Url:            resturl.Index,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.ACLIP,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.ACLMACIP,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.Interface,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.Loopback,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.Ethernet,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.Memif,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.Tap,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.VxLan,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.AfPacket,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.IPSecSpd,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.IPSecSa,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.IPSecTnIf,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.Bd,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.BdID,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.Fib,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.Xc,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.Arps,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.Routes,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.PArpIfs,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.PArpRngs,
				AllowedMethods: []string{http.MethodGet},
			},
			{
				Url:            resturl.Sessions,
				AllowedMethods: []string{http.MethodGet},
			},
		},
	}

	return []*access.PermissionGroup{tracerPg, telemetryPg, dumpPg}
}
<commit_msg>shorten permission declaration<commit_after>// Copyright (c) 2017 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 rest

import (
	"fmt"
	"net/http"
	"sync"

	access "github.com/ligato/cn-infra/rpc/rest/security/model/access-security"

	"github.com/ligato/vpp-agent/plugins/linux"

	"git.fd.io/govpp.git/api"
	"github.com/ligato/cn-infra/infra"
	"github.com/ligato/cn-infra/rpc/rest"
	"github.com/ligato/cn-infra/utils/safeclose"
	"github.com/ligato/vpp-agent/plugins/govppmux"
	iflinuxcalls "github.com/ligato/vpp-agent/plugins/linux/ifplugin/linuxcalls"
	l3linuxcalls "github.com/ligato/vpp-agent/plugins/linux/l3plugin/linuxcalls"
	"github.com/ligato/vpp-agent/plugins/rest/resturl"
	"github.com/ligato/vpp-agent/plugins/vpp"
	aclvppcalls "github.com/ligato/vpp-agent/plugins/vpp/aclplugin/vppcalls"
	ifvppcalls "github.com/ligato/vpp-agent/plugins/vpp/ifplugin/vppcalls"
	ipsecvppcalls "github.com/ligato/vpp-agent/plugins/vpp/ipsecplugin/vppcalls"
	l2vppcalls "github.com/ligato/vpp-agent/plugins/vpp/l2plugin/vppcalls"
	l3vppcalls "github.com/ligato/vpp-agent/plugins/vpp/l3plugin/vppcalls"
	l4vppcalls "github.com/ligato/vpp-agent/plugins/vpp/l4plugin/vppcalls"
)

// REST api methods
const (
	GET  = "GET"
	POST = "POST"
)

// Plugin registers Rest Plugin
type Plugin struct {
	Deps

	// Index page
	index *index

	// Channels
	vppChan  api.Channel
	dumpChan api.Channel

	// VPP Handlers
	aclHandler   aclvppcalls.ACLVppRead
	ifHandler    ifvppcalls.IfVppRead
	bfdHandler   ifvppcalls.BfdVppRead
	natHandler   ifvppcalls.NatVppRead
	stnHandler   ifvppcalls.StnVppRead
	ipSecHandler ipsecvppcalls.IPSecVPPRead
	bdHandler    l2vppcalls.BridgeDomainVppRead
	fibHandler   l2vppcalls.FibVppRead
	xcHandler    l2vppcalls.XConnectVppRead
	arpHandler   l3vppcalls.ArpVppRead
	pArpHandler  l3vppcalls.ProxyArpVppRead
	rtHandler    l3vppcalls.RouteVppRead
	l4Handler    l4vppcalls.L4VppRead
	// Linux handlers
	linuxIfHandler iflinuxcalls.NetlinkAPI
	linuxL3Handler l3linuxcalls.NetlinkAPI

	govppmux sync.Mutex
}

// Deps represents dependencies of Rest Plugin
type Deps struct {
	infra.PluginDeps
	HTTPHandlers rest.HTTPHandlers
	GoVppmux     govppmux.TraceAPI
	VPP          vpp.API
	Linux        linux.API
}

// index defines map of main index page entries
type index struct {
	ItemMap map[string][]indexItem
}

// indexItem is single index page entry
type indexItem struct {
	Name string
	Path string
}

// Init initializes the Rest Plugin
func (plugin *Plugin) Init() (err error) {
	// Check VPP dependency
	if plugin.VPP == nil {
		return fmt.Errorf("REST plugin requires VPP plugin API")
	}
	// VPP channels
	if plugin.vppChan, err = plugin.GoVppmux.NewAPIChannel(); err != nil {
		return err
	}
	if plugin.dumpChan, err = plugin.GoVppmux.NewAPIChannel(); err != nil {
		return err
	}
	// VPP Indexes
	ifIndexes := plugin.VPP.GetSwIfIndexes()
	bdIndexes := plugin.VPP.GetBDIndexes()
	spdIndexes := plugin.VPP.GetIPSecSPDIndexes()
	// Initialize VPP handlers
	plugin.aclHandler = aclvppcalls.NewACLVppHandler(plugin.vppChan, plugin.dumpChan)
	plugin.ifHandler = ifvppcalls.NewIfVppHandler(plugin.vppChan, plugin.Log)
	plugin.bfdHandler = ifvppcalls.NewBfdVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.natHandler = ifvppcalls.NewNatVppHandler(plugin.vppChan, plugin.dumpChan, ifIndexes, plugin.Log)
	plugin.stnHandler = ifvppcalls.NewStnVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.ipSecHandler = ipsecvppcalls.NewIPsecVppHandler(plugin.vppChan, ifIndexes, spdIndexes, plugin.Log)
	plugin.bdHandler = l2vppcalls.NewBridgeDomainVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.fibHandler = l2vppcalls.NewFibVppHandler(plugin.vppChan, plugin.dumpChan, ifIndexes, bdIndexes, plugin.Log)
	plugin.xcHandler = l2vppcalls.NewXConnectVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.arpHandler = l3vppcalls.NewArpVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.pArpHandler = l3vppcalls.NewProxyArpVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.rtHandler = l3vppcalls.NewRouteVppHandler(plugin.vppChan, ifIndexes, plugin.Log)
	plugin.l4Handler = l4vppcalls.NewL4VppHandler(plugin.vppChan, plugin.Log)
	// Linux indexes and handlers
	if plugin.Linux != nil {
		linuxIfIndexes := plugin.Linux.GetLinuxIfIndexes()
		linuxArpIndexes := plugin.Linux.GetLinuxARPIndexes()
		linuxRtIndexes := plugin.Linux.GetLinuxRouteIndexes()
		// Initialize Linux handlers
		linuxNsHandler := plugin.Linux.GetNamespaceHandler()
		plugin.linuxIfHandler = iflinuxcalls.NewNetLinkHandler(linuxNsHandler, linuxIfIndexes, plugin.Log)
		plugin.linuxL3Handler = l3linuxcalls.NewNetLinkHandler(linuxNsHandler, linuxIfIndexes, linuxArpIndexes, linuxRtIndexes, plugin.Log)
	}

	plugin.index = &index{
		ItemMap: getIndexMap(),
	}

	// Register permission groups, used if REST security is enabled
	plugin.HTTPHandlers.RegisterPermissionGroup(getPermissionsGroups()...)

	return nil
}

// AfterInit is used to register HTTP handlers
func (plugin *Plugin) AfterInit() (err error) {
	plugin.Log.Debug("REST API Plugin is up and running")

	// VPP handlers
	plugin.registerAccessListHandlers()
	plugin.registerInterfaceHandlers()
	plugin.registerBfdHandlers()
	plugin.registerNatHandlers()
	plugin.registerStnHandlers()
	plugin.registerIPSecHandlers()
	plugin.registerL2Handlers()
	plugin.registerL3Handlers()
	plugin.registerL4Handlers()
	// Linux handlers
	if plugin.Linux != nil {
		plugin.registerLinuxInterfaceHandlers()
		plugin.registerLinuxL3Handlers()
	}
	// Telemetry, command, index, tracer
	plugin.registerTracerHandler()
	plugin.registerTelemetryHandlers()
	plugin.registerCommandHandler()
	plugin.registerIndexHandlers()

	return nil
}

// Close is used to clean up resources used by Plugin
func (plugin *Plugin) Close() (err error) {
	return safeclose.Close(plugin.vppChan, plugin.dumpChan)
}

// Fill index item lists
func getIndexMap() map[string][]indexItem {
	idxMap := map[string][]indexItem{
		"ACL plugin": {
			{Name: "IP-type access lists", Path: resturl.ACLIP},
			{Name: "MACIP-type access lists", Path: resturl.ACLMACIP},
		},
		"Interface plugin": {
			{Name: "All interfaces", Path: resturl.Interface},
			{Name: "Loopbacks", Path: resturl.Loopback},
			{Name: "Ethernets", Path: resturl.Ethernet},
			{Name: "Memifs", Path: resturl.Memif},
			{Name: "Taps", Path: resturl.Tap},
			{Name: "VxLANs", Path: resturl.VxLan},
			{Name: "Af-packets", Path: resturl.AfPacket},
		},
		"IPSec plugin": {
			{Name: "Security policy databases", Path: resturl.IPSecSpd},
			{Name: "Security associations", Path: resturl.IPSecSa},
			{Name: "Tunnel interfaces", Path: resturl.IPSecTnIf},
		},
		"L2 plugin": {
			{Name: "Bridge domains", Path: resturl.Bd},
			{Name: "Bridge domain IDs", Path: resturl.BdID},
			{Name: "L2Fibs", Path: resturl.Fib},
			{Name: "Cross connects", Path: resturl.Xc},
		},
		"L3 plugin": {
			{Name: "Routes", Path: resturl.Routes},
			{Name: "ARPs", Path: resturl.Arps},
			{Name: "Proxy ARP interfaces", Path: resturl.PArpIfs},
			{Name: "Proxy ARP ranges", Path: resturl.PArpRngs},
		},
		"L4 plugin": {
			{Name: "L4 sessions", Path: resturl.Sessions},
		},
		"Telemetry": {
			{Name: "All data", Path: resturl.Telemetry},
			{Name: "Memory", Path: resturl.TMemory},
			{Name: "Runtime", Path: resturl.TRuntime},
			{Name: "Node count", Path: resturl.TNodeCount},
		},
		"Tracer": {
			{Name: "Binary API", Path: resturl.Tracer},
		},
	}
	return idxMap
}

// Create permission groups (tracer, telemetry, dump - optionally add more in the future). Used only if
// REST security is enabled in plugin
func getPermissionsGroups() []*access.PermissionGroup {
	tracerPg := &access.PermissionGroup{
		Name: "tracer",
		Permissions: []*access.PermissionGroup_Permissions{
			newPermission(resturl.Index, http.MethodGet),
			newPermission(resturl.Tracer, http.MethodGet),
		},
	}
	telemetryPg := &access.PermissionGroup{
		Name: "telemetry",
		Permissions: []*access.PermissionGroup_Permissions{
			newPermission(resturl.Index, http.MethodGet),
			newPermission(resturl.Telemetry, http.MethodGet),
			newPermission(resturl.TMemory, http.MethodGet),
			newPermission(resturl.TRuntime, http.MethodGet),
			newPermission(resturl.TNodeCount, http.MethodGet),
		},
	}
	dumpPg := &access.PermissionGroup{
		Name: "dump",
		Permissions: []*access.PermissionGroup_Permissions{
			newPermission(resturl.Index, http.MethodGet),
			newPermission(resturl.ACLIP, http.MethodGet),
			newPermission(resturl.ACLMACIP, http.MethodGet),
			newPermission(resturl.Interface, http.MethodGet),
			newPermission(resturl.Loopback, http.MethodGet),
			newPermission(resturl.Ethernet, http.MethodGet),
			newPermission(resturl.Memif, http.MethodGet),
			newPermission(resturl.Tap, http.MethodGet),
			newPermission(resturl.VxLan, http.MethodGet),
			newPermission(resturl.AfPacket, http.MethodGet),
			newPermission(resturl.IPSecSpd, http.MethodGet),
			newPermission(resturl.IPSecSa, http.MethodGet),
			newPermission(resturl.IPSecTnIf, http.MethodGet),
			newPermission(resturl.Bd, http.MethodGet),
			newPermission(resturl.BdID, http.MethodGet),
			newPermission(resturl.Fib, http.MethodGet),
			newPermission(resturl.Xc, http.MethodGet),
			newPermission(resturl.Arps, http.MethodGet),
			newPermission(resturl.Routes, http.MethodGet),
			newPermission(resturl.PArpIfs, http.MethodGet),
			newPermission(resturl.PArpRngs, http.MethodGet),
			newPermission(resturl.Sessions, http.MethodGet),
		},
	}

	return []*access.PermissionGroup{tracerPg, telemetryPg, dumpPg}
}

// Returns permission object with url and provided methods
func newPermission(url string, methods ...string) *access.PermissionGroup_Permissions {
	return &access.PermissionGroup_Permissions{
		Url:            url,
		AllowedMethods: methods,
	}
}
<|endoftext|>


<commit_before>package systemd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"syscall"

	"github.com/coreos/fleet/third_party/github.com/coreos/go-systemd/dbus"
	log "github.com/coreos/fleet/third_party/github.com/golang/glog"

	"github.com/coreos/fleet/event"
	"github.com/coreos/fleet/job"
	"github.com/coreos/fleet/machine"
	"github.com/coreos/fleet/unit"
)

const (
	defaultSystemdRuntimePath = "/run/systemd/system/"
)

type SystemdManager struct {
	Systemd  *dbus.Conn
	Machine  *machine.Machine
	unitPath string

	subscriptions *dbus.SubscriptionSet
	stop          chan bool
}

func NewSystemdManager(machine *machine.Machine) *SystemdManager {
	systemd, err := dbus.New()
	if err != nil {
		panic(err)
	}

	return &SystemdManager{systemd, machine, defaultSystemdRuntimePath, systemd.NewSubscriptionSet(), nil}
}

func (m *SystemdManager) MarshalJSON() ([]byte, error) {
	data := struct {
		DBUSSubscriptions []string
	}{
		DBUSSubscriptions: m.subscriptions.Values(),
	}
	return json.Marshal(data)
}

// Publish is a long-running function that streams dbus events through
// a translation layer and on to the EventBus
func (m *SystemdManager) Publish(bus *event.EventBus, stopchan chan bool) {
	m.Systemd.Subscribe()

	changechan, errchan := m.subscriptions.Subscribe()

	stream := NewEventStream()
	stream.Stream(changechan, bus.Channel)

	for true {
		select {
		case <-stopchan:
			break
		case err := <-errchan:
			var errString string
			if err != nil {
				errString = err.Error()
			} else {
				errString = "N/A"
			}
			log.Errorf("Received error from dbus: err=%s", errString)
		}
	}

	stream.Close()
	m.Systemd.Unsubscribe()
}

// LoadJob writes the unit of the given Job to disk, subscribes to
// relevant dbus events, and only if necessary, instructs the systemd
// daemon to reload
func (m *SystemdManager) LoadJob(job *job.Job) {
	m.writeUnit(job.Name, job.Unit.String())
	m.subscriptions.Add(job.Name)

	if m.unitRequiresDaemonReload(job.Name) {
		m.daemonReload()
	}
}

// UnloadJob removes the unit associated with the indicated Job from
// the filesystem, unsubscribing from relevant dbus events
func (m *SystemdManager) UnloadJob(jobName string) {
	m.subscriptions.Remove(jobName)
	m.removeUnit(jobName)
}

// StartJob starts the unit created for the indicated Job
func (m *SystemdManager) StartJob(jobName string) {
	m.startUnit(jobName)
}

// StopJob stops the unit created for the indicated Job
func (m *SystemdManager) StopJob(jobName string) {
	m.stopUnit(jobName)
}

// GetUnitState generates a UnitState object representing the
// current state of a Job's unit
func (m *SystemdManager) GetUnitState(jobName string) (*unit.UnitState, error) {
	loadState, activeState, subState, err := m.getUnitStates(jobName)
	if err != nil {
		return nil, err
	}
	ms := m.Machine.State()
	return unit.NewUnitState(loadState, activeState, subState, nil, &ms), nil
}

func (m *SystemdManager) getUnitStates(name string) (string, string, string, error) {
	info, err := m.Systemd.GetUnitProperties(name)

	if err != nil {
		return "", "", "", err
	} else {
		loadState := info["LoadState"].(string)
		activeState := info["ActiveState"].(string)
		subState := info["SubState"].(string)
		return loadState, activeState, subState, nil
	}
}

func (m *SystemdManager) startUnit(name string) {
	files := []string{name}
	if _, _, err := m.Systemd.EnableUnitFiles(files, true, false); err != nil {
		log.Errorf("Failed to enable systemd unit %s: %v", name, err)
		return
	} else {
		log.Infof("Enabled systemd unit %s", name)
	}

	if stat, err := m.Systemd.StartUnit(name, "replace"); err != nil {
		log.Errorf("Failed to start systemd unit %s: %v", name, err)
	} else {
		log.Infof("Started systemd unit %s(%s)", name, stat)
	}
}

func (m *SystemdManager) stopUnit(name string) {
	if stat, err := m.Systemd.StopUnit(name, "replace"); err != nil {
		log.Errorf("Failed to stop systemd unit %s: %v", name, err)
	} else {
		log.Infof("Stopped systemd unit %s(%s)", name, stat)
	}

	// go-systemd does not yet have this implemented
	//files := []string{name}
	//Systemd.DisableUnitFiles(files, true, false)
}

func (m *SystemdManager) removeUnit(name string) {
	file := m.getLocalPath(name)
	log.Infof("Removing systemd unit file %s", file)
	syscall.Unlink(file)
}

func (m *SystemdManager) readUnit(name string) (string, error) {
	path := m.getLocalPath(name)
	contents, err := ioutil.ReadFile(path)
	if err == nil {
		return string(contents), nil
	} else {
		return "", errors.New(fmt.Sprintf("No unit file at local path %s", path))
	}
}

func (m *SystemdManager) unitRequiresDaemonReload(name string) bool {
	prop, err := m.Systemd.GetUnitProperty(name, "NeedDaemonReload")
	if prop == nil || err != nil {
		return false
	}

	return prop.Value.Value().(bool)
}

func (m *SystemdManager) daemonReload() error {
	log.Infof("Instructing systemd to reload units")
	return m.Systemd.Reload()
}

func (m *SystemdManager) writeUnit(name string, contents string) error {
	log.Infof("Writing systemd unit file %s", name)

	path := path.Join(m.unitPath, name)
	file, err := os.Create(path)
	defer file.Close()

	if err != nil {
		return err
	}

	file.Write([]byte(contents))
	return nil
}

func (m *SystemdManager) getLocalPath(name string) string {
	return path.Join(m.unitPath, name)
}
<commit_msg>refactor(systemd): s/defaultSystemdRuntimePath/systemdRuntimePath/<commit_after>package systemd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"syscall"

	"github.com/coreos/fleet/third_party/github.com/coreos/go-systemd/dbus"
	log "github.com/coreos/fleet/third_party/github.com/golang/glog"

	"github.com/coreos/fleet/event"
	"github.com/coreos/fleet/job"
	"github.com/coreos/fleet/machine"
	"github.com/coreos/fleet/unit"
)

const (
	systemdRuntimePath = "/run/systemd/system/"
)

type SystemdManager struct {
	Systemd  *dbus.Conn
	Machine  *machine.Machine
	unitPath string

	subscriptions *dbus.SubscriptionSet
	stop          chan bool
}

func NewSystemdManager(machine *machine.Machine) *SystemdManager {
	systemd, err := dbus.New()
	if err != nil {
		panic(err)
	}

	return &SystemdManager{systemd, machine, systemdRuntimePath, systemd.NewSubscriptionSet(), nil}
}

func (m *SystemdManager) MarshalJSON() ([]byte, error) {
	data := struct {
		DBUSSubscriptions []string
	}{
		DBUSSubscriptions: m.subscriptions.Values(),
	}
	return json.Marshal(data)
}

// Publish is a long-running function that streams dbus events through
// a translation layer and on to the EventBus
func (m *SystemdManager) Publish(bus *event.EventBus, stopchan chan bool) {
	m.Systemd.Subscribe()

	changechan, errchan := m.subscriptions.Subscribe()

	stream := NewEventStream()
	stream.Stream(changechan, bus.Channel)

	for true {
		select {
		case <-stopchan:
			break
		case err := <-errchan:
			var errString string
			if err != nil {
				errString = err.Error()
			} else {
				errString = "N/A"
			}
			log.Errorf("Received error from dbus: err=%s", errString)
		}
	}

	stream.Close()
	m.Systemd.Unsubscribe()
}

// LoadJob writes the unit of the given Job to disk, subscribes to
// relevant dbus events, and only if necessary, instructs the systemd
// daemon to reload
func (m *SystemdManager) LoadJob(job *job.Job) {
	m.writeUnit(job.Name, job.Unit.String())
	m.subscriptions.Add(job.Name)

	if m.unitRequiresDaemonReload(job.Name) {
		m.daemonReload()
	}
}

// UnloadJob removes the unit associated with the indicated Job from
// the filesystem, unsubscribing from relevant dbus events
func (m *SystemdManager) UnloadJob(jobName string) {
	m.subscriptions.Remove(jobName)
	m.removeUnit(jobName)
}

// StartJob starts the unit created for the indicated Job
func (m *SystemdManager) StartJob(jobName string) {
	m.startUnit(jobName)
}

// StopJob stops the unit created for the indicated Job
func (m *SystemdManager) StopJob(jobName string) {
	m.stopUnit(jobName)
}

// GetUnitState generates a UnitState object representing the
// current state of a Job's unit
func (m *SystemdManager) GetUnitState(jobName string) (*unit.UnitState, error) {
	loadState, activeState, subState, err := m.getUnitStates(jobName)
	if err != nil {
		return nil, err
	}
	ms := m.Machine.State()
	return unit.NewUnitState(loadState, activeState, subState, nil, &ms), nil
}

func (m *SystemdManager) getUnitStates(name string) (string, string, string, error) {
	info, err := m.Systemd.GetUnitProperties(name)

	if err != nil {
		return "", "", "", err
	} else {
		loadState := info["LoadState"].(string)
		activeState := info["ActiveState"].(string)
		subState := info["SubState"].(string)
		return loadState, activeState, subState, nil
	}
}

func (m *SystemdManager) startUnit(name string) {
	files := []string{name}
	if _, _, err := m.Systemd.EnableUnitFiles(files, true, false); err != nil {
		log.Errorf("Failed to enable systemd unit %s: %v", name, err)
		return
	} else {
		log.Infof("Enabled systemd unit %s", name)
	}

	if stat, err := m.Systemd.StartUnit(name, "replace"); err != nil {
		log.Errorf("Failed to start systemd unit %s: %v", name, err)
	} else {
		log.Infof("Started systemd unit %s(%s)", name, stat)
	}
}

func (m *SystemdManager) stopUnit(name string) {
	if stat, err := m.Systemd.StopUnit(name, "replace"); err != nil {
		log.Errorf("Failed to stop systemd unit %s: %v", name, err)
	} else {
		log.Infof("Stopped systemd unit %s(%s)", name, stat)
	}

	// go-systemd does not yet have this implemented
	//files := []string{name}
	//Systemd.DisableUnitFiles(files, true, false)
}

func (m *SystemdManager) removeUnit(name string) {
	file := m.getLocalPath(name)
	log.Infof("Removing systemd unit file %s", file)
	syscall.Unlink(file)
}

func (m *SystemdManager) readUnit(name string) (string, error) {
	path := m.getLocalPath(name)
	contents, err := ioutil.ReadFile(path)
	if err == nil {
		return string(contents), nil
	} else {
		return "", errors.New(fmt.Sprintf("No unit file at local path %s", path))
	}
}

func (m *SystemdManager) unitRequiresDaemonReload(name string) bool {
	prop, err := m.Systemd.GetUnitProperty(name, "NeedDaemonReload")
	if prop == nil || err != nil {
		return false
	}

	return prop.Value.Value().(bool)
}

func (m *SystemdManager) daemonReload() error {
	log.Infof("Instructing systemd to reload units")
	return m.Systemd.Reload()
}

func (m *SystemdManager) writeUnit(name string, contents string) error {
	log.Infof("Writing systemd unit file %s", name)

	path := path.Join(m.unitPath, name)
	file, err := os.Create(path)
	defer file.Close()

	if err != nil {
		return err
	}

	file.Write([]byte(contents))
	return nil
}

func (m *SystemdManager) getLocalPath(name string) string {
	return path.Join(m.unitPath, name)
}
<|endoftext|>


<commit_before>package systray

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"syscall"
	"unsafe"
)

var (
	iconFiles = make([]*os.File, 0)
	dllDir    = path.Join(os.Getenv("APPDATA"), "systray")
	dllFile   = path.Join(dllDir, "systray.dll")

	mod                      = syscall.NewLazyDLL(dllFile)
	_nativeLoop              = mod.NewProc("nativeLoop")
	_quit                    = mod.NewProc("quit")
	_setIcon                 = mod.NewProc("setIcon")
	_setTitle                = mod.NewProc("setTitle")
	_setTooltip              = mod.NewProc("setTooltip")
	_add_or_update_menu_item = mod.NewProc("add_or_update_menu_item")
)

func init() {
	// Write DLL to file
	b, err := Asset("systray.dll")
	if err != nil {
		panic(fmt.Errorf("Unable to read systray.dll: %v", err))
	}

	err = os.MkdirAll(dllDir, 0755)
	if err != nil {
		panic(fmt.Errorf("Unable to create directory %v to hold systray.dll: %v", dllDir, err))
	}

	err = ioutil.WriteFile(dllFile, b, 0644)
	if err != nil {
		panic(fmt.Errorf("Unable to save systray.dll to %v: %v", dllFile, err))
	}
}

func nativeLoop() {
	_nativeLoop.Call(
		syscall.NewCallbackCDecl(systray_ready),
		syscall.NewCallbackCDecl(systray_menu_item_selected))
}

func quit() {
	_quit.Call()
	for _, f := range iconFiles {
		err := os.Remove(f.Name())
		if err != nil {
			log.Debugf("Unable to delete temporary icon file %v: %v", f.Name(), err)
		}
	}
}

// SetIcon sets the systray icon.
// iconBytes should be the content of .ico for windows and .ico/.jpg/.png
// for other platforms.
func SetIcon(iconBytes []byte) {
	f, err := ioutil.TempFile("", "systray_temp_icon")
	if err != nil {
		log.Errorf("Unable to create temp icon: %v", err)
		return
	}
	defer f.Close()
	_, err = f.Write(iconBytes)
	if err != nil {
		log.Errorf("Unable to write icon to temp file %v: %v", f.Name(), f)
		return
	}
	// Need to close file before we load it to make sure contents is flushed.
	f.Close()
	u16, name, err := strPtr(f.Name())
	if err != nil {
		log.Errorf("Unable to convert name to string pointer: %v", err)
		return
	}
	_setIcon.Call(name)
	noop(u16)
}

// SetTitle sets the systray title, only available on Mac.
func SetTitle(title string) {
	// do nothing
}

// SetTooltip sets the systray tooltip to display on mouse hover of the tray icon,
// only available on Mac and Windows.
func SetTooltip(tooltip string) {
	u16, t, err := strPtr(tooltip)
	if err != nil {
		log.Errorf("Unable to convert tooltip to string pointer: %v", err)
		return
	}
	_setTooltip.Call(t)
	noop(u16)
}

func addOrUpdateMenuItem(item *MenuItem) {
	var disabled = 0
	if item.disabled {
		disabled = 1
	}
	var checked = 0
	if item.checked {
		checked = 1
	}
	u16a, title, err := strPtr(item.title)
	if err != nil {
		log.Errorf("Unable to convert title to string pointer: %v", err)
		return
	}
	u16b, tooltip, err := strPtr(item.tooltip)
	if err != nil {
		log.Errorf("Unable to convert tooltip to string pointer: %v", err)
		return
	}
	_add_or_update_menu_item.Call(
		uintptr(item.id),
		title,
		tooltip,
		uintptr(disabled),
		uintptr(checked),
	)
	noop(u16a)
	noop(u16b)
}

// strPrt converts a Go string into a wchar_t*. It returns the underlying UTF-16
// array, which needs to be referenced until after it's been passed to the DLL
// to avoid it being garbage collected.
func strPtr(s string) ([]uint16, uintptr, error) {
	u16, err := syscall.UTF16FromString(s)
	if err != nil {
		return nil, 0, err
	}
	return u16, uintptr(unsafe.Pointer(&u16[0])), nil
}

// noop does nothing. We just call it so that we're doing something with the u16
// variable, which we just hang on to to prevent it being gc'd.
func noop(u16 []uint16) {
	// do nothing
}

// systray_ready takes an ignored parameter just so we can compile a callback
// (for some reason in Go 1.4.x, syscall.NewCallback panics if there's no
// parameter to the function).
func systray_ready(ignore uintptr) uintptr {
	systrayReady()
	return 0
}

func systray_menu_item_selected(id uintptr) uintptr {
	systrayMenuItemSelected(int32(id))
	return 0
}
<commit_msg>refactor strPtr to strUTF16<commit_after>package systray

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"syscall"
	"unsafe"
)

var (
	iconFiles = make([]*os.File, 0)
	dllDir    = path.Join(os.Getenv("APPDATA"), "systray")
	dllFile   = path.Join(dllDir, "systray.dll")

	mod                      = syscall.NewLazyDLL(dllFile)
	_nativeLoop              = mod.NewProc("nativeLoop")
	_quit                    = mod.NewProc("quit")
	_setIcon                 = mod.NewProc("setIcon")
	_setTitle                = mod.NewProc("setTitle")
	_setTooltip              = mod.NewProc("setTooltip")
	_add_or_update_menu_item = mod.NewProc("add_or_update_menu_item")
)

func init() {
	// Write DLL to file
	b, err := Asset("systray.dll")
	if err != nil {
		panic(fmt.Errorf("Unable to read systray.dll: %v", err))
	}

	err = os.MkdirAll(dllDir, 0755)
	if err != nil {
		panic(fmt.Errorf("Unable to create directory %v to hold systray.dll: %v", dllDir, err))
	}

	err = ioutil.WriteFile(dllFile, b, 0644)
	if err != nil {
		panic(fmt.Errorf("Unable to save systray.dll to %v: %v", dllFile, err))
	}
}

func nativeLoop() {
	_nativeLoop.Call(
		syscall.NewCallbackCDecl(systray_ready),
		syscall.NewCallbackCDecl(systray_menu_item_selected))
}

func quit() {
	_quit.Call()
	for _, f := range iconFiles {
		err := os.Remove(f.Name())
		if err != nil {
			log.Debugf("Unable to delete temporary icon file %v: %v", f.Name(), err)
		}
	}
}

// SetIcon sets the systray icon.
// iconBytes should be the content of .ico for windows and .ico/.jpg/.png
// for other platforms.
func SetIcon(iconBytes []byte) {
	f, err := ioutil.TempFile("", "systray_temp_icon")
	if err != nil {
		log.Errorf("Unable to create temp icon: %v", err)
		return
	}
	defer f.Close()
	_, err = f.Write(iconBytes)
	if err != nil {
		log.Errorf("Unable to write icon to temp file %v: %v", f.Name(), f)
		return
	}
	// Need to close file before we load it to make sure contents is flushed.
	f.Close()
	name, err := strUTF16(f.Name())
	if err != nil {
		log.Errorf("Unable to convert name to string pointer: %v", err)
		return
	}
	_setIcon.Call(name.Raw())
}

// SetTitle sets the systray title, only available on Mac.
func SetTitle(title string) {
	// do nothing
}

// SetTooltip sets the systray tooltip to display on mouse hover of the tray icon,
// only available on Mac and Windows.
func SetTooltip(tooltip string) {
	t, err := strUTF16(tooltip)
	if err != nil {
		log.Errorf("Unable to convert tooltip to string pointer: %v", err)
		return
	}
	_setTooltip.Call(t.Raw())
}

func addOrUpdateMenuItem(item *MenuItem) {
	var disabled = 0
	if item.disabled {
		disabled = 1
	}
	var checked = 0
	if item.checked {
		checked = 1
	}
	title, err := strUTF16(item.title)
	if err != nil {
		log.Errorf("Unable to convert title to string pointer: %v", err)
		return
	}
	tooltip, err := strUTF16(item.tooltip)
	if err != nil {
		log.Errorf("Unable to convert tooltip to string pointer: %v", err)
		return
	}
	_add_or_update_menu_item.Call(
		uintptr(item.id),
		title.Raw(),
		tooltip.Raw(),
		uintptr(disabled),
		uintptr(checked),
	)
}

type utf16 []uint16

// Raw returns the underlying *wchar_t of an utf16 so we can pass to DLL
func (u utf16) Raw() uintptr {
	return uintptr(unsafe.Pointer(&u[0]))
}

// strUTF16 converts a Go string into a utf16 byte sequence
func strUTF16(s string) (utf16, error) {
	return syscall.UTF16FromString(s)
}

// systray_ready takes an ignored parameter just so we can compile a callback
// (for some reason in Go 1.4.x, syscall.NewCallback panics if there's no
// parameter to the function).
func systray_ready(ignore uintptr) uintptr {
	systrayReady()
	return 0
}

func systray_menu_item_selected(id uintptr) uintptr {
	systrayMenuItemSelected(int32(id))
	return 0
}
<|endoftext|>


<commit_before>// +build windows
package tap

import (
	"errors"
	"fmt"
	"golang.org/x/sys/windows/registry"
	"net"
	"os"
	"os/exec"
	"strings"
	"syscall"
)

var (
	IfceNameNotFound  = errors.New("Failed to find the name of interface.")
	TapDeviceNotFound = errors.New("Failed to find the tap device in registry.")
	// Device Control Codes
	tap_win_ioctl_get_mac             = tap_control_code(1, 0)
	tap_win_ioctl_get_version         = tap_control_code(2, 0)
	tap_win_ioctl_get_mtu             = tap_control_code(3, 0)
	tap_win_ioctl_get_info            = tap_control_code(4, 0)
	tap_ioctl_config_point_to_point   = tap_control_code(5, 0)
	tap_ioctl_set_media_status        = tap_control_code(6, 0)
	tap_win_ioctl_config_dhcp_masq    = tap_control_code(7, 0)
	tap_win_ioctl_get_log_line        = tap_control_code(8, 0)
	tap_win_ioctl_config_dhcp_set_opt = tap_control_code(9, 0)
	tap_ioctl_config_tun              = tap_control_code(10, 0)
	// w32 api
	file_device_unknown = uint32(0x00000022)
)

func ctl_code(device_type, function, method, access uint32) uint32 {
	return (device_type << 16) | (access << 14) | (function << 2) | method
}

func tap_control_code(request, method uint32) uint32 {
	return ctl_code(file_device_unknown, request, method, 0)
}

// GetDeviceId finds out a TAP device from registry, it requires privileged right.
func getdeviceid() (string, error) {
	// TAP driver key location
	regkey := `SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}`
	k, err := registry.OpenKey(registry.LOCAL_MACHINE, regkey, registry.ALL_ACCESS)
	if err != nil {
		return "", err
	}
	defer k.Close()
	// read all subkeys
	keys, err := k.ReadSubKeyNames(-1)
	if err != nil {
		return "", err
	}
	// find the one with ComponentId == "tap0901"
	for _, v := range keys {
		key, err := registry.OpenKey(registry.LOCAL_MACHINE, regkey+"\\"+v, registry.ALL_ACCESS)
		if err != nil {
			continue
		}
		val, _, err := key.GetStringValue("ComponentId")
		if err != nil {
			goto next
		}
		if val == "tap0901" {
			val, _, err = key.GetStringValue("NetCfgInstanceId")
			if err != nil {
				goto next
			}
			key.Close()
			return val, nil
		}
	next:
		key.Close()
	}
	return "", TapDeviceNotFound
}

// NewTAP find and open a TAP device.
func newTAP() (ifce *Interface, err error) {
	deviceid, err := getdeviceid()
	if err != nil {
		return nil, err
	}
	path := "\\\\.\\Global\\" + deviceid + ".tap"
	pathp, err := syscall.UTF16PtrFromString(path)
	if err != nil {
		return nil, err
	}
	// type Handle uintptr
	file, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, uint32(syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE), nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_SYSTEM, 0)
	// if err hanppens, close the interface.
	defer func() {
		if err != nil {
			syscall.Close(file)
		}
		if err := recover(); err != nil {
			syscall.Close(file)
		}
	}()
	if err != nil {
		return nil, err
	}
	var bytesReturned uint32
	// find the mac address of tap device.
	mac := make([]byte, 6)
	err = syscall.DeviceIoControl(file, tap_win_ioctl_get_mac, &mac[0], uint32(len(mac)), &mac[0], uint32(len(mac)), &bytesReturned, nil)
	if err != nil {
		return nil, err
	}
	//TUN
	//code2 := []byte{0x0a, 0x03, 0x00, 0x01, 0x0a, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00}
	//err = syscall.DeviceIoControl(file, tap_ioctl_config_tun, &code2[0], uint32(12), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
	//if err != nil {
	//	log.Fatalln("code2 err:", err)
	//}
	fd := os.NewFile(uintptr(file), path)
	ifce = &Interface{tap: true, file: fd}
	copy(ifce.mac[:6], mac[:6])

	// find the name of tap interface(to set the ip)
	hwaddr_equal := func(a net.HardwareAddr, b []byte) bool {
		for i := 0; i < 6; i++ {
			if a[i] != b[i] {
				return false
			}
		}
		return true
	}
	ifces, err := net.Interfaces()
	if err != nil {
		return
	}
	for _, v := range ifces {
		if hwaddr_equal(v.HardwareAddr[:6], mac[:6]) {
			ifce.name = v.Name
			if err := ifce.ignoreDefaultRoutes(); err != nil {
				return nil, err
			}
			// bring up device.
			rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
			code := []byte{0x01, 0x00, 0x00, 0x00}
			err = syscall.DeviceIoControl(file, tap_ioctl_set_media_status, &code[0], uint32(4), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
			if err != nil {
				return nil, err
			}
			return
		}
	}
	err = IfceNameNotFound
	return

}

func (ifce *Interface) ignoreDefaultRoutes() error {
	sargs := "interface ip set interface interface=REPLACE_ME ignoredefaultroutes=enabled"
	args := strings.Split(sargs, " ")
	args[4] = fmt.Sprintf("interface=\"%s\"", ifce.name)
	cmd := exec.Command("netsh", args...)
	return cmd.Run()
}

func (ifce *Interface) setIP(ip_mask *net.IPNet) (err error) {
	sargs := fmt.Sprintf("interface ip set address name=REPLACE_ME source=static addr=REPLACE_ME mask=REPLACE_ME gateway=none")
	args := strings.Split(sargs, " ")
	args[4] = fmt.Sprintf("name=\"%s\"", ifce.Name())
	args[6] = fmt.Sprintf("addr=%s", ip_mask.IP)
	args[7] = fmt.Sprintf("mask=%d.%d.%d.%d", ip_mask.Mask[0], ip_mask.Mask[1], ip_mask.Mask[2], ip_mask.Mask[3])
	cmd := exec.Command("netsh", args...)
	err = cmd.Run()
	return
}

func addRoute(ip net.IP, ip_mask *net.IPNet, ifce string) (err error) {
	sargs := fmt.Sprintf("interface ip add route %s REPLACE_ME %s", ip_mask, ip)
	args := strings.Split(sargs, " ")
	args[5] = fmt.Sprintf("\"%s\"", ifce)
	cmd := exec.Command("netsh", args...)
	err = cmd.Run()
	return
}

func (ifce *Interface) addRoute(ip net.IP, ip_mask *net.IPNet) (err error) {
	return addRoute(ip, ip_mask, ifce.name)
}

func delRoute(ip net.IP, ip_mask *net.IPNet, ifce string) (err error) {
	sargs := fmt.Sprintf("interface ip del route %s REPLACE_ME %s", ip_mask, ip)
	args := strings.Split(sargs, " ")
	args[5] = fmt.Sprintf("\"%s\"", ifce)
	cmd := exec.Command("netsh", args...)
	err = cmd.Run()
	return
}

func (ifce *Interface) delRoute(ip net.IP, ip_mask *net.IPNet) (err error) {
	return delRoute(ip, ip_mask, ifce.name)
}
<commit_msg>Bring up the device first so that can read the ifces on windows.<commit_after>// +build windows
package tap

import (
	"errors"
	"fmt"
	"golang.org/x/sys/windows/registry"
	"net"
	"os"
	"os/exec"
	"strings"
	"syscall"
)

var (
	IfceNameNotFound  = errors.New("Failed to find the name of interface.")
	TapDeviceNotFound = errors.New("Failed to find the tap device in registry.")
	// Device Control Codes
	tap_win_ioctl_get_mac             = tap_control_code(1, 0)
	tap_win_ioctl_get_version         = tap_control_code(2, 0)
	tap_win_ioctl_get_mtu             = tap_control_code(3, 0)
	tap_win_ioctl_get_info            = tap_control_code(4, 0)
	tap_ioctl_config_point_to_point   = tap_control_code(5, 0)
	tap_ioctl_set_media_status        = tap_control_code(6, 0)
	tap_win_ioctl_config_dhcp_masq    = tap_control_code(7, 0)
	tap_win_ioctl_get_log_line        = tap_control_code(8, 0)
	tap_win_ioctl_config_dhcp_set_opt = tap_control_code(9, 0)
	tap_ioctl_config_tun              = tap_control_code(10, 0)
	// w32 api
	file_device_unknown = uint32(0x00000022)
)

func ctl_code(device_type, function, method, access uint32) uint32 {
	return (device_type << 16) | (access << 14) | (function << 2) | method
}

func tap_control_code(request, method uint32) uint32 {
	return ctl_code(file_device_unknown, request, method, 0)
}

// GetDeviceId finds out a TAP device from registry, it requires privileged right.
func getdeviceid() (string, error) {
	// TAP driver key location
	regkey := `SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}`
	k, err := registry.OpenKey(registry.LOCAL_MACHINE, regkey, registry.ALL_ACCESS)
	if err != nil {
		return "", err
	}
	defer k.Close()
	// read all subkeys
	keys, err := k.ReadSubKeyNames(-1)
	if err != nil {
		return "", err
	}
	// find the one with ComponentId == "tap0901"
	for _, v := range keys {
		key, err := registry.OpenKey(registry.LOCAL_MACHINE, regkey+"\\"+v, registry.ALL_ACCESS)
		if err != nil {
			continue
		}
		val, _, err := key.GetStringValue("ComponentId")
		if err != nil {
			goto next
		}
		if val == "tap0901" {
			val, _, err = key.GetStringValue("NetCfgInstanceId")
			if err != nil {
				goto next
			}
			key.Close()
			return val, nil
		}
	next:
		key.Close()
	}
	return "", TapDeviceNotFound
}

// NewTAP find and open a TAP device.
func newTAP() (ifce *Interface, err error) {
	deviceid, err := getdeviceid()
	if err != nil {
		return nil, err
	}
	path := "\\\\.\\Global\\" + deviceid + ".tap"
	pathp, err := syscall.UTF16PtrFromString(path)
	if err != nil {
		return nil, err
	}
	// type Handle uintptr
	file, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, uint32(syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE), nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_SYSTEM, 0)
	// if err hanppens, close the interface.
	defer func() {
		if err != nil {
			syscall.Close(file)
		}
		if err := recover(); err != nil {
			syscall.Close(file)
		}
	}()
	if err != nil {
		return nil, err
	}
	var bytesReturned uint32
	// find the mac address of tap device.
	mac := make([]byte, 6)
	err = syscall.DeviceIoControl(file, tap_win_ioctl_get_mac, &mac[0], uint32(len(mac)), &mac[0], uint32(len(mac)), &bytesReturned, nil)
	if err != nil {
		return nil, err
	}
	//TUN
	//code2 := []byte{0x0a, 0x03, 0x00, 0x01, 0x0a, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00}
	//err = syscall.DeviceIoControl(file, tap_ioctl_config_tun, &code2[0], uint32(12), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
	//if err != nil {
	//	log.Fatalln("code2 err:", err)
	//}
	fd := os.NewFile(uintptr(file), path)
	ifce = &Interface{tap: true, file: fd}
	copy(ifce.mac[:6], mac[:6])

	// find the name of tap interface(to set the ip)
	hwaddr_equal := func(a net.HardwareAddr, b []byte) bool {
		for i := 0; i < 6; i++ {
			if a[i] != b[i] {
				return false
			}
		}
		return true
	}
	ifces, err := net.Interfaces()
	if err != nil {
		return
	}
	// bring up device.
	rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
	code := []byte{0x01, 0x00, 0x00, 0x00}
	err = syscall.DeviceIoControl(file, tap_ioctl_set_media_status, &code[0], uint32(4), &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
	if err != nil {
		return nil, err
	}
	for _, v := range ifces {
		if hwaddr_equal(v.HardwareAddr[:6], mac[:6]) {
			ifce.name = v.Name
			//ifce.ignoreDefaultRoutes()
			return
		}
	}
	err = IfceNameNotFound
	return

}

func (ifce *Interface) ignoreDefaultRoutes() error {
	sargs := "interface ip set interface interface=REPLACE_ME ignoredefaultroutes=enabled"
	args := strings.Split(sargs, " ")
	args[4] = fmt.Sprintf("interface=\"%s\"", ifce.name)
	cmd := exec.Command("netsh", args...)
	return cmd.Run()
}

func (ifce *Interface) setIP(ip_mask *net.IPNet) (err error) {
	sargs := fmt.Sprintf("interface ip set address name=REPLACE_ME source=static addr=REPLACE_ME mask=REPLACE_ME gateway=none")
	args := strings.Split(sargs, " ")
	args[4] = fmt.Sprintf("name=\"%s\"", ifce.Name())
	args[6] = fmt.Sprintf("addr=%s", ip_mask.IP)
	args[7] = fmt.Sprintf("mask=%d.%d.%d.%d", ip_mask.Mask[0], ip_mask.Mask[1], ip_mask.Mask[2], ip_mask.Mask[3])
	cmd := exec.Command("netsh", args...)
	err = cmd.Run()
	return
}

func addRoute(ip net.IP, ip_mask *net.IPNet, ifce string) (err error) {
	sargs := fmt.Sprintf("interface ip add route %s REPLACE_ME %s", ip_mask, ip)
	args := strings.Split(sargs, " ")
	args[5] = fmt.Sprintf("\"%s\"", ifce)
	cmd := exec.Command("netsh", args...)
	err = cmd.Run()
	return
}

func (ifce *Interface) addRoute(ip net.IP, ip_mask *net.IPNet) (err error) {
	return addRoute(ip, ip_mask, ifce.name)
}

func delRoute(ip net.IP, ip_mask *net.IPNet, ifce string) (err error) {
	sargs := fmt.Sprintf("interface ip del route %s REPLACE_ME %s", ip_mask, ip)
	args := strings.Split(sargs, " ")
	args[5] = fmt.Sprintf("\"%s\"", ifce)
	cmd := exec.Command("netsh", args...)
	err = cmd.Run()
	return
}

func (ifce *Interface) delRoute(ip net.IP, ip_mask *net.IPNet) (err error) {
	return delRoute(ip, ip_mask, ifce.name)
}
<|endoftext|>