<commit_before>package nbfx

import (
	"bytes"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"

	"github.com/satori/go.uuid"
)

type encoder struct {
	dict        map[string]uint32
	xml         *xml.Decoder
	bin         *bytes.Buffer
	tokenBuffer *Queue
}

func (e *encoder) addDictionaryString(index uint32, value string) {
	if _, ok := e.dict[value]; ok {
		return
	}
	e.dict[value] = index
}

func NewEncoder() Encoder {
	return NewEncoderWithStrings(nil)
}

func NewEncoderWithStrings(dictionaryStrings map[uint32]string) Encoder {
	encoder := &encoder{dict: map[string]uint32{}, tokenBuffer: &Queue{}}
	if dictionaryStrings != nil {
		for k, v := range dictionaryStrings {
			encoder.addDictionaryString(k, v)
		}
	}
	return encoder
}

func (e *encoder) popToken() (xml.Token, error) {
	var token xml.Token
	var err error
	if e.tokenBuffer.Len() > 0 {
		token = e.tokenBuffer.Dequeue().(xml.Token)
		return token, err
	}
	token, err = e.xml.RawToken()
	token = xml.CopyToken(token) // make the token immutable (see doc for xml.Decoder.Token())
	return token, err
}

func (e *encoder) pushToken(token xml.Token) {
	e.tokenBuffer.Enqueue(token)
}

func (e *encoder) Encode(reader io.Reader) ([]byte, error) {
	e.bin = &bytes.Buffer{}
	e.xml = xml.NewDecoder(reader)
	token, err := e.popToken()
	for err == nil && token != nil {
		record, err := e.getRecordFromToken(token)
		if err != nil {
			return e.bin.Bytes(), err
		}
		if record.isStartElement() {
			elementWriter := record.(elementRecordEncoder)
			err = elementWriter.encodeElement(e, token.(xml.StartElement))
		} else if record.isText() {
			textWriter := record.(textRecordEncoder)
			if _, ok := token.(xml.Comment); ok {
				err = textWriter.encodeText(e, textWriter, string(token.(xml.Comment)))
			} else {
				err = textWriter.encodeText(e, textWriter, string(token.(xml.CharData)))
			}
		} else if record.isEndElement() {
			elementWriter := record.(elementRecordEncoder)
			err = elementWriter.encodeElement(e, xml.StartElement{})
		} else {
			err = errors.New(fmt.Sprint("NotSupported: Encoding record", record))
		}
		if err != nil {
			return e.bin.Bytes(), errors.New(fmt.Sprintf("Error writing Token %s :: %s", token, err.Error()))
		}
		token, err = e.popToken()
	}
	return e.bin.Bytes(), nil
}

func (e *encoder) getRecordFromToken(token xml.Token) (record, error) {
	switch token.(type) {
	case xml.StartElement:
		return e.getStartElementRecordFromToken(token.(xml.StartElement))
	case xml.CharData:
		return e.getTextRecordFromToken(token.(xml.CharData))
	case xml.EndElement:
		return records[endElement], nil
	case xml.Comment:
		return records[comment], nil
	}

	tokenXmlBytes, err := xml.Marshal(token)
	var tokenXml string
	if err != nil {
		tokenXml = "[[UNKNOWN]]"
	} else {
		tokenXml = string(tokenXmlBytes)
	}
	return nil, errors.New(fmt.Sprint("Unknown token", tokenXml))
}

func (e *encoder) getTextRecordFromToken(cd xml.CharData) (record, error) {
	withEndElement := false
	next, _ := e.popToken()
	switch next.(type) {
	case xml.EndElement:
		withEndElement = true
	}
	text := string(cd)
	if !withEndElement {
		e.pushToken(next)
	}
	return e.getTextRecordFromText(text, withEndElement)
}

func (e *encoder) getTextRecordFromText(text string, withEndElement bool) (record, error) {
	var id byte
	id = 0x00
	if text == "" {
		id = emptyText
	} else if text == "0" {
		id = zeroText
	} else if text == "1" {
		id = oneText
	} else if text == "false" {
		id = falseText
	} else if text == "true" {
		id = trueText
	} else if isUuid(text) {
		id = uuidText
	} else if isUniqueId(text) {
		id = uniqueIdText
	} else {
		if _, ok := e.dict[text]; ok {
			id = dictionaryText
		} else {
			id = chars8Text
		}
	}
	if id != 0 && withEndElement {
		id += 1
	}
	if rec, ok := records[id]; ok {
		return rec, nil
	}
	return nil, errors.New(fmt.Sprintf("Unknown text record id %#X for %s withEndElement %v", id, text, withEndElement))
}

func (e *encoder) getStartElementRecordFromToken(startElement xml.StartElement) (record, error) {
	prefix := startElement.Name.Space
	name := startElement.Name.Local
	prefixIndex := -1
	if len(prefix) == 1 && byte(prefix[0]) >= byte('a') && byte(prefix[0]) <= byte('z') {
		prefixIndex = int(byte(prefix[0]) - byte('a'))
	}
	isNameIndexAssigned := false
	if _, ok := e.dict[name]; ok {
		isNameIndexAssigned = true
	}
	localHasStrPrefix := strings.HasPrefix(startElement.Name.Local, "str")

	if prefix == "" {
		if isNameIndexAssigned || localHasStrPrefix {
			return records[shortDictionaryElement], nil
		} else {
			return records[shortElement], nil
		}
	} else if prefixIndex != -1 {
		if isNameIndexAssigned || localHasStrPrefix {
			return records[prefixDictionaryElementA+byte(prefixIndex)], nil
		} else {
			return records[prefixElementA+byte(prefixIndex)], nil
		}
	} else {
		if isNameIndexAssigned || localHasStrPrefix {
			return records[dictionaryElement], nil
		} else {
			return records[element], nil
		}
	}
	return nil, errors.New(fmt.Sprint("getStartElementRecordFromToken unable to resolve", startElement))
}

func (e *encoder) getAttributeRecordFromToken(attr xml.Attr) (record, error) {
	prefix := attr.Name.Space
	name := attr.Name.Local
	isXmlns := prefix == "xmlns" || prefix == "" && name == "xmlns"
	prefixIndex := -1
	if len(prefix) == 1 && byte(prefix[0]) >= byte('a') && byte(prefix[0]) <= byte('z') {
		prefixIndex = int(byte(prefix[0]) - byte('a'))
	}
	isNameIndexAssigned := false
	if _, ok := e.dict[name]; ok {
		isNameIndexAssigned = true
	}
	localHasStrPrefix := strings.HasPrefix(attr.Name.Local, "str")
	valueHasStrPrefix := strings.HasPrefix(attr.Value, "str")

	if prefix == "" {
		if isXmlns {
			if _, ok := e.dict[attr.Value]; ok || valueHasStrPrefix {
				return records[shortDictionaryXmlnsAttribute], nil
			} else {
				return records[shortXmlnsAttribute], nil
			}
		} else if isNameIndexAssigned || localHasStrPrefix {
			return records[shortDictionaryAttribute], nil
		} else {
			return records[shortAttribute], nil
		}
	} else if prefixIndex != -1 {
		if isNameIndexAssigned || localHasStrPrefix {
			return records[prefixDictionaryAttributeA+byte(prefixIndex)], nil
		} else {
			return records[prefixAttributeA+byte(prefixIndex)], nil
		}
	} else {
		if isXmlns {
			if isNameIndexAssigned || valueHasStrPrefix {
				return records[dictionaryXmlnsAttribute], nil
			} else {
				return records[xmlnsAttribute], nil
			}
		} else {
			if isNameIndexAssigned || localHasStrPrefix {
				return records[dictionaryAttribute], nil
			} else {
				return records[attribute], nil
			}
		}
	}
	return nil, errors.New(fmt.Sprint("getAttributeRecordFromToken unable to resolve", attr))
}

func writeString(e *encoder, str string) (int, error) {
	var strBytes = []byte(str)
	lenByteLen, err := writeMultiByteInt31(e, uint32(len(strBytes)))
	if err != nil {
		return lenByteLen, err
	}
	strByteLen, err := e.bin.Write(strBytes)
	return lenByteLen + strByteLen, err
}

func writeMultiByteInt31(e *encoder, num uint32) (int, error) {
	max := uint32(2147483647)
	if num > max {
		return 0, errors.New(fmt.Sprintf("Overflow: i (%d) must be <= max (%d)", num, max))
	}
	if num < mask_mbi31 {
		return e.bin.Write([]byte{byte(num)})
	}
	q := num / mask_mbi31
	rem := num % mask_mbi31
	n1, err := e.bin.Write([]byte{byte(mask_mbi31 + rem)})
	if err != nil {
		return n1, err
	}
	n2, err := writeMultiByteInt31(e, q)
	return n1 + n2, err
}

func writeChars8Text(e *encoder, text string) error {
	bytes := []byte(text)
	writeMultiByteInt31(e, uint32(len(bytes)))
	_, err := e.bin.Write(bytes)
	return err
}

func writeChars32Text(e *encoder, text string) error {
	bytes := []byte(text)
	writeMultiByteInt31(e, uint32(len(bytes)))
	_, err := e.bin.Write(bytes)
	return err
}

func writeUuidText(e *encoder, text string) error {
	id, err := uuid.FromString(text)
	bin := id.Bytes()
	bin, err = flipUuidByteOrder(bin)
	if err != nil {
		return err
	}
	_, err = e.bin.Write(bin)
	if err != nil {
		return err
	}
	return nil
}

func writeDictionaryString(e *encoder, str string) error {
	if val, ok := e.dict[str]; ok {
		_, err := writeMultiByteInt31(e, val)
		if err != nil {
			return err
		}
	} else if strings.HasPrefix(str, "str") {
		// capture "8" in "str8" and write "8"
		numString := str[3:]
		numInt, err := strconv.Atoi(numString)
		if err != nil {
			return err
		}
		_, err = writeMultiByteInt31(e, uint32(numInt))
		if err != nil {
			return err
		}
	} else {
		return errors.New("Invalid Operation")
	}
	return nil
}
<commit_msg>Use value for encoder.Queue instead of pointer<commit_after>package nbfx

import (
	"bytes"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"

	"github.com/satori/go.uuid"
)

type encoder struct {
	dict        map[string]uint32
	xml         *xml.Decoder
	bin         *bytes.Buffer
	tokenBuffer Queue
}

func (e *encoder) addDictionaryString(index uint32, value string) {
	if _, ok := e.dict[value]; ok {
		return
	}
	e.dict[value] = index
}

func NewEncoder() Encoder {
	return NewEncoderWithStrings(nil)
}

func NewEncoderWithStrings(dictionaryStrings map[uint32]string) Encoder {
	encoder := &encoder{dict: map[string]uint32{}}
	if dictionaryStrings != nil {
		for k, v := range dictionaryStrings {
			encoder.addDictionaryString(k, v)
		}
	}
	return encoder
}

func (e *encoder) popToken() (xml.Token, error) {
	var token xml.Token
	var err error
	if e.tokenBuffer.Len() > 0 {
		token = e.tokenBuffer.Dequeue().(xml.Token)
		return token, err
	}
	token, err = e.xml.RawToken()
	token = xml.CopyToken(token) // make the token immutable (see doc for xml.Decoder.Token())
	return token, err
}

func (e *encoder) pushToken(token xml.Token) {
	e.tokenBuffer.Enqueue(token)
}

func (e *encoder) Encode(reader io.Reader) ([]byte, error) {
	e.bin = &bytes.Buffer{}
	e.xml = xml.NewDecoder(reader)
	token, err := e.popToken()
	for err == nil && token != nil {
		record, err := e.getRecordFromToken(token)
		if err != nil {
			return e.bin.Bytes(), err
		}
		if record.isStartElement() {
			elementWriter := record.(elementRecordEncoder)
			err = elementWriter.encodeElement(e, token.(xml.StartElement))
		} else if record.isText() {
			textWriter := record.(textRecordEncoder)
			if _, ok := token.(xml.Comment); ok {
				err = textWriter.encodeText(e, textWriter, string(token.(xml.Comment)))
			} else {
				err = textWriter.encodeText(e, textWriter, string(token.(xml.CharData)))
			}
		} else if record.isEndElement() {
			elementWriter := record.(elementRecordEncoder)
			err = elementWriter.encodeElement(e, xml.StartElement{})
		} else {
			err = errors.New(fmt.Sprint("NotSupported: Encoding record", record))
		}
		if err != nil {
			return e.bin.Bytes(), errors.New(fmt.Sprintf("Error writing Token %s :: %s", token, err.Error()))
		}
		token, err = e.popToken()
	}
	return e.bin.Bytes(), nil
}

func (e *encoder) getRecordFromToken(token xml.Token) (record, error) {
	switch token.(type) {
	case xml.StartElement:
		return e.getStartElementRecordFromToken(token.(xml.StartElement))
	case xml.CharData:
		return e.getTextRecordFromToken(token.(xml.CharData))
	case xml.EndElement:
		return records[endElement], nil
	case xml.Comment:
		return records[comment], nil
	}

	tokenXmlBytes, err := xml.Marshal(token)
	var tokenXml string
	if err != nil {
		tokenXml = "[[UNKNOWN]]"
	} else {
		tokenXml = string(tokenXmlBytes)
	}
	return nil, errors.New(fmt.Sprint("Unknown token", tokenXml))
}

func (e *encoder) getTextRecordFromToken(cd xml.CharData) (record, error) {
	withEndElement := false
	next, _ := e.popToken()
	switch next.(type) {
	case xml.EndElement:
		withEndElement = true
	}
	text := string(cd)
	if !withEndElement {
		e.pushToken(next)
	}
	return e.getTextRecordFromText(text, withEndElement)
}

func (e *encoder) getTextRecordFromText(text string, withEndElement bool) (record, error) {
	var id byte
	id = 0x00
	if text == "" {
		id = emptyText
	} else if text == "0" {
		id = zeroText
	} else if text == "1" {
		id = oneText
	} else if text == "false" {
		id = falseText
	} else if text == "true" {
		id = trueText
	} else if isUuid(text) {
		id = uuidText
	} else if isUniqueId(text) {
		id = uniqueIdText
	} else {
		if _, ok := e.dict[text]; ok {
			id = dictionaryText
		} else {
			id = chars8Text
		}
	}
	if id != 0 && withEndElement {
		id += 1
	}
	if rec, ok := records[id]; ok {
		return rec, nil
	}
	return nil, errors.New(fmt.Sprintf("Unknown text record id %#X for %s withEndElement %v", id, text, withEndElement))
}

func (e *encoder) getStartElementRecordFromToken(startElement xml.StartElement) (record, error) {
	prefix := startElement.Name.Space
	name := startElement.Name.Local
	prefixIndex := -1
	if len(prefix) == 1 && byte(prefix[0]) >= byte('a') && byte(prefix[0]) <= byte('z') {
		prefixIndex = int(byte(prefix[0]) - byte('a'))
	}
	isNameIndexAssigned := false
	if _, ok := e.dict[name]; ok {
		isNameIndexAssigned = true
	}
	localHasStrPrefix := strings.HasPrefix(startElement.Name.Local, "str")

	if prefix == "" {
		if isNameIndexAssigned || localHasStrPrefix {
			return records[shortDictionaryElement], nil
		} else {
			return records[shortElement], nil
		}
	} else if prefixIndex != -1 {
		if isNameIndexAssigned || localHasStrPrefix {
			return records[prefixDictionaryElementA+byte(prefixIndex)], nil
		} else {
			return records[prefixElementA+byte(prefixIndex)], nil
		}
	} else {
		if isNameIndexAssigned || localHasStrPrefix {
			return records[dictionaryElement], nil
		} else {
			return records[element], nil
		}
	}
	return nil, errors.New(fmt.Sprint("getStartElementRecordFromToken unable to resolve", startElement))
}

func (e *encoder) getAttributeRecordFromToken(attr xml.Attr) (record, error) {
	prefix := attr.Name.Space
	name := attr.Name.Local
	isXmlns := prefix == "xmlns" || prefix == "" && name == "xmlns"
	prefixIndex := -1
	if len(prefix) == 1 && byte(prefix[0]) >= byte('a') && byte(prefix[0]) <= byte('z') {
		prefixIndex = int(byte(prefix[0]) - byte('a'))
	}
	isNameIndexAssigned := false
	if _, ok := e.dict[name]; ok {
		isNameIndexAssigned = true
	}
	localHasStrPrefix := strings.HasPrefix(attr.Name.Local, "str")
	valueHasStrPrefix := strings.HasPrefix(attr.Value, "str")

	if prefix == "" {
		if isXmlns {
			if _, ok := e.dict[attr.Value]; ok || valueHasStrPrefix {
				return records[shortDictionaryXmlnsAttribute], nil
			} else {
				return records[shortXmlnsAttribute], nil
			}
		} else if isNameIndexAssigned || localHasStrPrefix {
			return records[shortDictionaryAttribute], nil
		} else {
			return records[shortAttribute], nil
		}
	} else if prefixIndex != -1 {
		if isNameIndexAssigned || localHasStrPrefix {
			return records[prefixDictionaryAttributeA+byte(prefixIndex)], nil
		} else {
			return records[prefixAttributeA+byte(prefixIndex)], nil
		}
	} else {
		if isXmlns {
			if isNameIndexAssigned || valueHasStrPrefix {
				return records[dictionaryXmlnsAttribute], nil
			} else {
				return records[xmlnsAttribute], nil
			}
		} else {
			if isNameIndexAssigned || localHasStrPrefix {
				return records[dictionaryAttribute], nil
			} else {
				return records[attribute], nil
			}
		}
	}
	return nil, errors.New(fmt.Sprint("getAttributeRecordFromToken unable to resolve", attr))
}

func writeString(e *encoder, str string) (int, error) {
	var strBytes = []byte(str)
	lenByteLen, err := writeMultiByteInt31(e, uint32(len(strBytes)))
	if err != nil {
		return lenByteLen, err
	}
	strByteLen, err := e.bin.Write(strBytes)
	return lenByteLen + strByteLen, err
}

func writeMultiByteInt31(e *encoder, num uint32) (int, error) {
	max := uint32(2147483647)
	if num > max {
		return 0, errors.New(fmt.Sprintf("Overflow: i (%d) must be <= max (%d)", num, max))
	}
	if num < mask_mbi31 {
		return e.bin.Write([]byte{byte(num)})
	}
	q := num / mask_mbi31
	rem := num % mask_mbi31
	n1, err := e.bin.Write([]byte{byte(mask_mbi31 + rem)})
	if err != nil {
		return n1, err
	}
	n2, err := writeMultiByteInt31(e, q)
	return n1 + n2, err
}

func writeChars8Text(e *encoder, text string) error {
	bytes := []byte(text)
	writeMultiByteInt31(e, uint32(len(bytes)))
	_, err := e.bin.Write(bytes)
	return err
}

func writeChars32Text(e *encoder, text string) error {
	bytes := []byte(text)
	writeMultiByteInt31(e, uint32(len(bytes)))
	_, err := e.bin.Write(bytes)
	return err
}

func writeUuidText(e *encoder, text string) error {
	id, err := uuid.FromString(text)
	bin := id.Bytes()
	bin, err = flipUuidByteOrder(bin)
	if err != nil {
		return err
	}
	_, err = e.bin.Write(bin)
	if err != nil {
		return err
	}
	return nil
}

func writeDictionaryString(e *encoder, str string) error {
	if val, ok := e.dict[str]; ok {
		_, err := writeMultiByteInt31(e, val)
		if err != nil {
			return err
		}
	} else if strings.HasPrefix(str, "str") {
		// capture "8" in "str8" and write "8"
		numString := str[3:]
		numInt, err := strconv.Atoi(numString)
		if err != nil {
			return err
		}
		_, err = writeMultiByteInt31(e, uint32(numInt))
		if err != nil {
			return err
		}
	} else {
		return errors.New("Invalid Operation")
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/insomniacslk/dhcp/dhcpv4"
	"github.com/insomniacslk/dhcp/dhcpv6"
	"github.com/insomniacslk/dhcp/iana"
	"github.com/insomniacslk/dhcp/interfaces"
	"github.com/insomniacslk/dhcp/netboot"
	"github.com/systemboot/systemboot/pkg/crypto"
	"github.com/u-root/u-root/pkg/kexec"
)

var (
	useV4              = flag.Bool("4", false, "Get a DHCPv4 lease")
	useV6              = flag.Bool("6", true, "Get a DHCPv6 lease")
	ifname             = flag.String("i", "", "Interface to send packets through")
	dryRun             = flag.Bool("dryrun", false, "Do everything except assigning IP addresses, changing DNS, and kexec")
	doDebug            = flag.Bool("d", false, "Print debug output")
	skipDHCP           = flag.Bool("skip-dhcp", false, "Skip DHCP and rely on SLAAC for network configuration. This requires -netboot-url")
	overrideNetbootURL = flag.String("netboot-url", "", "Override the netboot URL normally obtained via DHCP")
	readTimeout        = flag.Int("timeout", 3, "Read timeout in seconds")
	dhcpRetries        = flag.Int("retries", 3, "Number of times a DHCP request is retried")
	userClass          = flag.String("userclass", "", "Override DHCP User Class option")
	caCertFile         = flag.String("cacerts", "", "Base64 encoded CA cert file")
	skipCertVerify     = flag.Bool("skip-cert-verify", false, "Don't authenticate https certs")
)

const (
	interfaceUpTimeout = 10 * time.Second
	maxHTTPAttempts    = 3
	retryInterval      = time.Second
)

var banner = `

 _________________________________
< Net booting is so hot right now >
 ---------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

`
var debug = func(string, ...interface{}) {}

func main() {
	flag.Parse()
	if *skipDHCP && *overrideNetbootURL == "" {
		log.Fatal("-skip-dhcp requires -netboot-url")
	}
	if *doDebug {
		debug = log.Printf
	}
	log.Print(banner)

	if !*useV6 && !*useV4 {
		log.Fatal("At least one of DHCPv6 and DHCPv4 is required")
	}

	iflist := []net.Interface{}
	if *ifname != "" {
		var iface *net.Interface
		var err error
		if iface, err = net.InterfaceByName(*ifname); err != nil {
			log.Fatalf("Could not find interface %s: %v", *ifname, err)
		}
		iflist = append(iflist, *iface)
	} else {
		var err error
		if iflist, err = interfaces.GetNonLoopbackInterfaces(); err != nil {
			log.Fatalf("Could not obtain the list of network interfaces: %v", err)
		}
	}

	for _, iface := range iflist {
		log.Printf("Waiting for network interface %s to come up", iface.Name)
		start := time.Now()
		_, err := netboot.IfUp(iface.Name, interfaceUpTimeout)
		if err != nil {
			log.Printf("IfUp failed: %v", err)
			continue
		}
		debug("Interface %s is up after %v", iface.Name, time.Since(start))

		var dhcp []dhcpFunc
		if *useV6 {
			dhcp = append(dhcp, dhcp6)
		}
		if *useV4 {
			dhcp = append(dhcp, dhcp4)
		}
		for _, d := range dhcp {
			if err := boot(iface.Name, d); err != nil {
				log.Printf("Could not boot from %s: %v", iface.Name, err)
			}
		}
	}

	log.Fatalln("Could not boot from any interfaces")
}

func retryableNetError(err error) bool {
	if err == nil {
		return false
	}
	switch err := err.(type) {
	case net.Error:
		if err.Timeout() {
			return true
		}
	}
	return false
}

func retryableHTTPError(resp *http.Response) bool {
	if resp == nil {
		return false
	}
	if resp.StatusCode == 500 || resp.StatusCode == 502 {
		return true
	}
	return false
}

func boot(ifname string, dhcp dhcpFunc) error {
	var (
		netconf  *netboot.NetConf
		bootfile string
		err      error
	)
	if *skipDHCP {
		log.Print("Skipping DHCP")
	} else {
		// send a netboot request via DHCP
		netconf, bootfile, err = dhcp(ifname)
		if err != nil {
			return fmt.Errorf("DHCPv6: netboot request for interface %s failed: %v", ifname, err)
		}
		debug("DHCP: network configuration: %+v", netconf)
		if !*dryRun {
			log.Printf("DHCP: configuring network interface %s with %v", ifname, netconf)
			if err = netboot.ConfigureInterface(ifname, netconf); err != nil {
				return fmt.Errorf("DHCP: cannot configure interface %s: %v", ifname, err)
			}
		}
		if *overrideNetbootURL != "" {
			bootfile = *overrideNetbootURL
		}
		log.Printf("DHCP: boot file for interface %s is %s", ifname, bootfile)
	}
	if *overrideNetbootURL != "" {
		bootfile = *overrideNetbootURL
	}
	debug("DHCP: boot file URL is %s", bootfile)
	// check for supported schemes
	scheme, err := getScheme(bootfile)
	if err != nil {
		return fmt.Errorf("DHCP: cannot get scheme from URL: %v", err)
	}
	if scheme == "" {
		return errors.New("DHCP: no valid scheme found in URL")
	}

	client, err := getClientForBootfile(bootfile)
	if err != nil {
		return fmt.Errorf("DHCP: cannot get client for %s: %v", bootfile, err)
	}
	log.Printf("DHCP: fetching boot file URL: %s", bootfile)

	var resp *http.Response
	for attempt := 0; attempt < maxHTTPAttempts; attempt++ {
		log.Printf("netboot: attempt %d for http.Get", attempt+1)
		req, err := http.NewRequest(http.MethodGet, bootfile, nil)
		if err != nil {
			return fmt.Errorf("could not build request for %s: %v", bootfile, err)
		}
		resp, err = client.Do(req)
		if err != nil && retryableNetError(err) || retryableHTTPError(resp) {
			time.Sleep(retryInterval)
			continue
		}
		if err == nil {
			break
		}
		return fmt.Errorf("DHCP: http.Get of %s failed: %v", bootfile, err)
	}
	// FIXME this will not be called if something fails after this point
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return fmt.Errorf("status code is not 200 OK: %d", resp.StatusCode)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("DHCP: cannot read boot file from the network: %v", err)
	}
	crypto.TryMeasureData(crypto.BootConfig, body, bootfile)
	u, err := url.Parse(bootfile)
	if err != nil {
		return fmt.Errorf("DHCP: cannot parse URL %s: %v", bootfile, err)
	}
	// extract file name component
	if strings.HasSuffix(u.Path, "/") {
		return fmt.Errorf("invalid file path, cannot end with '/': %s", u.Path)
	}
	filename := filepath.Base(u.Path)
	if filename == "." || filename == "" {
		return fmt.Errorf("invalid empty file name extracted from file path %s", u.Path)
	}
	if err = ioutil.WriteFile(filename, body, 0400); err != nil {
		return fmt.Errorf("DHCP: cannot write to file %s: %v", filename, err)
	}
	debug("DHCP: saved boot file to %s", filename)
	if !*dryRun {
		log.Printf("DHCP: kexec'ing into %s", filename)
		kernel, err := os.OpenFile(filename, os.O_RDONLY, 0)
		if err != nil {
			return fmt.Errorf("DHCP: cannot open file %s: %v", filename, err)
		}
		if err = kexec.FileLoad(kernel, nil /* ramfs */, "" /* cmdline */); err != nil {
			return fmt.Errorf("DHCP: kexec.FileLoad failed: %v", err)
		}
		if err = kexec.Reboot(); err != nil {
			return fmt.Errorf("DHCP: kexec.Reboot failed: %v", err)
		}
	}
	return nil
}

func getScheme(urlstring string) (string, error) {
	u, err := url.Parse(urlstring)
	if err != nil {
		return "", err
	}
	scheme := strings.ToLower(u.Scheme)
	if scheme != "http" && scheme != "https" {
		return "", fmt.Errorf("URL scheme '%s' must be http or https", scheme)
	}
	return scheme, nil
}

func loadCaCerts() (*x509.CertPool, error) {
	rootCAs, err := x509.SystemCertPool()
	if err != nil {
		return nil, err
	}
	if rootCAs == nil {
		debug("certs: rootCAs == nil")
		rootCAs = x509.NewCertPool()
	}
	base64Certs, err := ioutil.ReadFile(*caCertFile)
	if err != nil {
		return nil, fmt.Errorf("could not find cert file '%v' - %v", *caCertFile, err)
	}
	// TODO: Decide if this should also support compressed certs
	// Might be better to have a generic compressed config API
	decLen := base64.StdEncoding.DecodedLen(len(base64Certs))
	certs := make([]byte, decLen)
	n, err := base64.StdEncoding.Decode(certs, base64Certs)
	if err != nil {
		return nil, fmt.Errorf("failed to append %s to RootCAs: %v", *caCertFile, err)
	}
	if ok := rootCAs.AppendCertsFromPEM(certs); !ok {
		debug("No certs extracted from VPD, using system certs only")
	} else {
		debug("%d bytes of certs extrated from VPD", n)
	}
	return rootCAs, nil

}

func getClientForBootfile(bootfile string) (*http.Client, error) {
	var client *http.Client
	scheme, err := getScheme(bootfile)
	if err != nil {
		return nil, err
	}

	switch scheme {
	case "https":
		var config *tls.Config
		if *skipCertVerify {
			config = &tls.Config{
				InsecureSkipVerify: true,
			}
		} else if *caCertFile != "" {
			rootCAs, err := loadCaCerts()
			if err != nil {
				return nil, err
			}
			config = &tls.Config{
				RootCAs: rootCAs,
			}
		}
		tr := &http.Transport{TLSClientConfig: config}
		client = &http.Client{Transport: tr}
		debug("https client setup (use certs from VPD: %t, skipCertVerify %t)",
			*skipCertVerify, *caCertFile != "")
	case "http":
		client = &http.Client{}
		debug("http client setup")
	default:
		return nil, fmt.Errorf("Scheme %s is unsupported", scheme)
	}
	return client, nil
}

type dhcpFunc func(string) (*netboot.NetConf, string, error)

func dhcp6(ifname string) (*netboot.NetConf, string, error) {
	log.Printf("Trying to obtain a DHCPv6 lease on %s", ifname)
	modifiers := []dhcpv6.Modifier{
		dhcpv6.WithArchType(iana.EFI_X86_64),
	}
	if *userClass != "" {
		modifiers = append(modifiers, dhcpv6.WithUserClass([]byte(*userClass)))
	}
	conversation, err := netboot.RequestNetbootv6(ifname, time.Duration(*readTimeout)*time.Second, *dhcpRetries, modifiers...)
	for _, m := range conversation {
		debug(m.Summary())
	}
	if err != nil {
		return nil, "", fmt.Errorf("DHCPv6: netboot request for interface %s failed: %v", ifname, err)
	}
	return netboot.ConversationToNetconf(conversation)
}

func dhcp4(ifname string) (*netboot.NetConf, string, error) {
	log.Printf("Trying to obtain a DHCPv4 lease on %s", ifname)
	var modifiers []dhcpv4.Modifier
	if *userClass != "" {
		modifiers = append(modifiers, dhcpv4.WithUserClass(*userClass, false))
	}
	conversation, err := netboot.RequestNetbootv4(ifname, time.Duration(*readTimeout)*time.Second, *dhcpRetries, modifiers...)
	for _, m := range conversation {
		debug(m.Summary())
	}
	if err != nil {
		return nil, "", fmt.Errorf("DHCPv4: netboot request for interface %s failed: %v", ifname, err)
	}
	return netboot.ConversationToNetconfv4(conversation)
}
<commit_msg>Don't read CA Certs from VPD, read them from the filesystem instead<commit_after>package main

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/insomniacslk/dhcp/dhcpv4"
	"github.com/insomniacslk/dhcp/dhcpv6"
	"github.com/insomniacslk/dhcp/iana"
	"github.com/insomniacslk/dhcp/interfaces"
	"github.com/insomniacslk/dhcp/netboot"
	"github.com/systemboot/systemboot/pkg/crypto"
	"github.com/u-root/u-root/pkg/kexec"
)

var (
	useV4              = flag.Bool("4", false, "Get a DHCPv4 lease")
	useV6              = flag.Bool("6", true, "Get a DHCPv6 lease")
	ifname             = flag.String("i", "", "Interface to send packets through")
	dryRun             = flag.Bool("dryrun", false, "Do everything except assigning IP addresses, changing DNS, and kexec")
	doDebug            = flag.Bool("d", false, "Print debug output")
	skipDHCP           = flag.Bool("skip-dhcp", false, "Skip DHCP and rely on SLAAC for network configuration. This requires -netboot-url")
	overrideNetbootURL = flag.String("netboot-url", "", "Override the netboot URL normally obtained via DHCP")
	readTimeout        = flag.Int("timeout", 3, "Read timeout in seconds")
	dhcpRetries        = flag.Int("retries", 3, "Number of times a DHCP request is retried")
	userClass          = flag.String("userclass", "", "Override DHCP User Class option")
	caCertFile         = flag.String("cacerts", "/etc/cacerts.pem", "CA cert file")
	skipCertVerify     = flag.Bool("skip-cert-verify", false, "Don't authenticate https certs")
)

const (
	interfaceUpTimeout = 10 * time.Second
	maxHTTPAttempts    = 3
	retryInterval      = time.Second
)

var banner = `

 _________________________________
< Net booting is so hot right now >
 ---------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

`
var debug = func(string, ...interface{}) {}

func main() {
	flag.Parse()
	if *skipDHCP && *overrideNetbootURL == "" {
		log.Fatal("-skip-dhcp requires -netboot-url")
	}
	if *doDebug {
		debug = log.Printf
	}
	log.Print(banner)

	if !*useV6 && !*useV4 {
		log.Fatal("At least one of DHCPv6 and DHCPv4 is required")
	}

	iflist := []net.Interface{}
	if *ifname != "" {
		var iface *net.Interface
		var err error
		if iface, err = net.InterfaceByName(*ifname); err != nil {
			log.Fatalf("Could not find interface %s: %v", *ifname, err)
		}
		iflist = append(iflist, *iface)
	} else {
		var err error
		if iflist, err = interfaces.GetNonLoopbackInterfaces(); err != nil {
			log.Fatalf("Could not obtain the list of network interfaces: %v", err)
		}
	}

	for _, iface := range iflist {
		log.Printf("Waiting for network interface %s to come up", iface.Name)
		start := time.Now()
		_, err := netboot.IfUp(iface.Name, interfaceUpTimeout)
		if err != nil {
			log.Printf("IfUp failed: %v", err)
			continue
		}
		debug("Interface %s is up after %v", iface.Name, time.Since(start))

		var dhcp []dhcpFunc
		if *useV6 {
			dhcp = append(dhcp, dhcp6)
		}
		if *useV4 {
			dhcp = append(dhcp, dhcp4)
		}
		for _, d := range dhcp {
			if err := boot(iface.Name, d); err != nil {
				log.Printf("Could not boot from %s: %v", iface.Name, err)
			}
		}
	}

	log.Fatalln("Could not boot from any interfaces")
}

func retryableNetError(err error) bool {
	if err == nil {
		return false
	}
	switch err := err.(type) {
	case net.Error:
		if err.Timeout() {
			return true
		}
	}
	return false
}

func retryableHTTPError(resp *http.Response) bool {
	if resp == nil {
		return false
	}
	if resp.StatusCode == 500 || resp.StatusCode == 502 {
		return true
	}
	return false
}

func boot(ifname string, dhcp dhcpFunc) error {
	var (
		netconf  *netboot.NetConf
		bootfile string
		err      error
	)
	if *skipDHCP {
		log.Print("Skipping DHCP")
	} else {
		// send a netboot request via DHCP
		netconf, bootfile, err = dhcp(ifname)
		if err != nil {
			return fmt.Errorf("DHCPv6: netboot request for interface %s failed: %v", ifname, err)
		}
		debug("DHCP: network configuration: %+v", netconf)
		if !*dryRun {
			log.Printf("DHCP: configuring network interface %s with %v", ifname, netconf)
			if err = netboot.ConfigureInterface(ifname, netconf); err != nil {
				return fmt.Errorf("DHCP: cannot configure interface %s: %v", ifname, err)
			}
		}
		if *overrideNetbootURL != "" {
			bootfile = *overrideNetbootURL
		}
		log.Printf("DHCP: boot file for interface %s is %s", ifname, bootfile)
	}
	if *overrideNetbootURL != "" {
		bootfile = *overrideNetbootURL
	}
	debug("DHCP: boot file URL is %s", bootfile)
	// check for supported schemes
	scheme, err := getScheme(bootfile)
	if err != nil {
		return fmt.Errorf("DHCP: cannot get scheme from URL: %v", err)
	}
	if scheme == "" {
		return errors.New("DHCP: no valid scheme found in URL")
	}

	client, err := getClientForBootfile(bootfile)
	if err != nil {
		return fmt.Errorf("DHCP: cannot get client for %s: %v", bootfile, err)
	}
	log.Printf("DHCP: fetching boot file URL: %s", bootfile)

	var resp *http.Response
	for attempt := 0; attempt < maxHTTPAttempts; attempt++ {
		log.Printf("netboot: attempt %d for http.Get", attempt+1)
		req, err := http.NewRequest(http.MethodGet, bootfile, nil)
		if err != nil {
			return fmt.Errorf("could not build request for %s: %v", bootfile, err)
		}
		resp, err = client.Do(req)
		if err != nil && retryableNetError(err) || retryableHTTPError(resp) {
			time.Sleep(retryInterval)
			continue
		}
		if err == nil {
			break
		}
		return fmt.Errorf("DHCP: http.Get of %s failed: %v", bootfile, err)
	}
	// FIXME this will not be called if something fails after this point
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return fmt.Errorf("status code is not 200 OK: %d", resp.StatusCode)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("DHCP: cannot read boot file from the network: %v", err)
	}
	crypto.TryMeasureData(crypto.BootConfig, body, bootfile)
	u, err := url.Parse(bootfile)
	if err != nil {
		return fmt.Errorf("DHCP: cannot parse URL %s: %v", bootfile, err)
	}
	// extract file name component
	if strings.HasSuffix(u.Path, "/") {
		return fmt.Errorf("invalid file path, cannot end with '/': %s", u.Path)
	}
	filename := filepath.Base(u.Path)
	if filename == "." || filename == "" {
		return fmt.Errorf("invalid empty file name extracted from file path %s", u.Path)
	}
	if err = ioutil.WriteFile(filename, body, 0400); err != nil {
		return fmt.Errorf("DHCP: cannot write to file %s: %v", filename, err)
	}
	debug("DHCP: saved boot file to %s", filename)
	if !*dryRun {
		log.Printf("DHCP: kexec'ing into %s", filename)
		kernel, err := os.OpenFile(filename, os.O_RDONLY, 0)
		if err != nil {
			return fmt.Errorf("DHCP: cannot open file %s: %v", filename, err)
		}
		if err = kexec.FileLoad(kernel, nil /* ramfs */, "" /* cmdline */); err != nil {
			return fmt.Errorf("DHCP: kexec.FileLoad failed: %v", err)
		}
		if err = kexec.Reboot(); err != nil {
			return fmt.Errorf("DHCP: kexec.Reboot failed: %v", err)
		}
	}
	return nil
}

func getScheme(urlstring string) (string, error) {
	u, err := url.Parse(urlstring)
	if err != nil {
		return "", err
	}
	scheme := strings.ToLower(u.Scheme)
	if scheme != "http" && scheme != "https" {
		return "", fmt.Errorf("URL scheme '%s' must be http or https", scheme)
	}
	return scheme, nil
}

func loadCaCerts() (*x509.CertPool, error) {
	rootCAs, err := x509.SystemCertPool()
	if err != nil {
		return nil, err
	}
	if rootCAs == nil {
		debug("certs: rootCAs == nil")
		rootCAs = x509.NewCertPool()
	}
	caCerts, err := ioutil.ReadFile(*caCertFile)
	if err != nil {
		return nil, fmt.Errorf("could not find cert file '%v' - %v", *caCertFile, err)
	}
	// TODO: Decide if this should also support compressed certs
	// Might be better to have a generic compressed config API
	if ok := rootCAs.AppendCertsFromPEM(caCerts); !ok {
		debug("Failed to append CA Certs from %s, using system certs only", *caCertFile)
	} else {
		debug("CA certs appended from PEM")
	}
	return rootCAs, nil

}

func getClientForBootfile(bootfile string) (*http.Client, error) {
	var client *http.Client
	scheme, err := getScheme(bootfile)
	if err != nil {
		return nil, err
	}

	switch scheme {
	case "https":
		var config *tls.Config
		if *skipCertVerify {
			config = &tls.Config{
				InsecureSkipVerify: true,
			}
		} else if *caCertFile != "" {
			rootCAs, err := loadCaCerts()
			if err != nil {
				return nil, err
			}
			config = &tls.Config{
				RootCAs: rootCAs,
			}
		}
		tr := &http.Transport{TLSClientConfig: config}
		client = &http.Client{Transport: tr}
		debug("https client setup (use certs from VPD: %t, skipCertVerify %t)",
			*skipCertVerify, *caCertFile != "")
	case "http":
		client = &http.Client{}
		debug("http client setup")
	default:
		return nil, fmt.Errorf("Scheme %s is unsupported", scheme)
	}
	return client, nil
}

type dhcpFunc func(string) (*netboot.NetConf, string, error)

func dhcp6(ifname string) (*netboot.NetConf, string, error) {
	log.Printf("Trying to obtain a DHCPv6 lease on %s", ifname)
	modifiers := []dhcpv6.Modifier{
		dhcpv6.WithArchType(iana.EFI_X86_64),
	}
	if *userClass != "" {
		modifiers = append(modifiers, dhcpv6.WithUserClass([]byte(*userClass)))
	}
	conversation, err := netboot.RequestNetbootv6(ifname, time.Duration(*readTimeout)*time.Second, *dhcpRetries, modifiers...)
	for _, m := range conversation {
		debug(m.Summary())
	}
	if err != nil {
		return nil, "", fmt.Errorf("DHCPv6: netboot request for interface %s failed: %v", ifname, err)
	}
	return netboot.ConversationToNetconf(conversation)
}

func dhcp4(ifname string) (*netboot.NetConf, string, error) {
	log.Printf("Trying to obtain a DHCPv4 lease on %s", ifname)
	var modifiers []dhcpv4.Modifier
	if *userClass != "" {
		modifiers = append(modifiers, dhcpv4.WithUserClass(*userClass, false))
	}
	conversation, err := netboot.RequestNetbootv4(ifname, time.Duration(*readTimeout)*time.Second, *dhcpRetries, modifiers...)
	for _, m := range conversation {
		debug(m.Summary())
	}
	if err != nil {
		return nil, "", fmt.Errorf("DHCPv4: netboot request for interface %s failed: %v", ifname, err)
	}
	return netboot.ConversationToNetconfv4(conversation)
}
<|endoftext|>


<commit_before>// Copyright 2017 Microsoft. All rights reserved.
// MIT License

package netlink

import (
	"fmt"
	"net"

	"golang.org/x/sys/unix"
)

// Link types.
const (
	LINK_TYPE_BRIDGE = "bridge"
	LINK_TYPE_VETH   = "veth"
	LINK_TYPE_IPVLAN = "ipvlan"
	LINK_TYPE_DUMMY  = "dummy"
)

// IPVLAN link attributes.
type IPVlanMode uint16

const (
	IPVLAN_MODE_L2 IPVlanMode = iota
	IPVLAN_MODE_L3
	IPVLAN_MODE_L3S
	IPVLAN_MODE_MAX
)

// Link represents a network interface.
type Link interface {
	Info() *LinkInfo
}

// LinkInfo respresents the common properties of all network interfaces.
type LinkInfo struct {
	Type        string
	Name        string
	MTU         uint
	TxQLen      uint
	ParentIndex int
}

func (linkInfo *LinkInfo) Info() *LinkInfo {
	return linkInfo
}

// BridgeLink represents an ethernet bridge.
type BridgeLink struct {
	LinkInfo
}

// VEthLink represents a virtual ethernet network interface.
type VEthLink struct {
	LinkInfo
	PeerName string
}

// IPVlanLink represents an IPVlan network interface.
type IPVlanLink struct {
	LinkInfo
	Mode IPVlanMode
}

// DummyLink represents a dummy network interface.
type DummyLink struct {
	LinkInfo
}

// AddLink adds a new network interface of a specified type.
func AddLink(link Link) error {
	var info *LinkInfo
	info = link.Info()

	if info.Name == "" || info.Type == "" {
		return fmt.Errorf("Invalid link name or type")
	}

	s, err := getSocket()
	if err != nil {
		return err
	}

	req := newRequest(unix.RTM_NEWLINK, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)

	ifInfo := newIfInfoMsg()
	req.addPayload(ifInfo)

	// Set interface name.
	attrIfName := newAttributeStringZ(unix.IFLA_IFNAME, info.Name)
	req.addPayload(attrIfName)

	// Set MTU.
	if info.MTU > 0 {
		req.addPayload(newAttributeUint32(unix.IFLA_MTU, uint32(info.MTU)))
	}

	// Set transmission queue length.
	if info.TxQLen > 0 {
		req.addPayload(newAttributeUint32(unix.IFLA_TXQLEN, uint32(info.TxQLen)))
	}

	// Set parent interface index.
	if info.ParentIndex != 0 {
		req.addPayload(newAttributeUint32(unix.IFLA_LINK, uint32(info.ParentIndex)))
	}

	// Set link info.
	attrLinkInfo := newAttribute(unix.IFLA_LINKINFO, nil)
	attrLinkInfo.addNested(newAttributeString(IFLA_INFO_KIND, info.Type))

	// Set link type-specific attributes.
	if veth, ok := link.(*VEthLink); ok {
		// Set VEth attributes.
		attrData := newAttribute(IFLA_INFO_DATA, nil)

		attrPeer := newAttribute(VETH_INFO_PEER, nil)
		attrPeer.addNested(newIfInfoMsg())
		attrPeer.addNested(newAttributeStringZ(unix.IFLA_IFNAME, veth.PeerName))
		attrData.addNested(attrPeer)

		attrLinkInfo.addNested(attrData)

	} else if ipvlan, ok := link.(*IPVlanLink); ok {
		// Set IPVlan attributes.
		attrData := newAttribute(IFLA_INFO_DATA, nil)
		attrData.addNested(newAttributeUint16(IFLA_IPVLAN_MODE, uint16(ipvlan.Mode)))

		attrLinkInfo.addNested(attrData)
	}

	req.addPayload(attrLinkInfo)

	return s.sendAndWaitForAck(req)
}

// DeleteLink deletes a network interface.
func DeleteLink(name string) error {
	if name == "" {
		return fmt.Errorf("Invalid link name")
	}

	s, err := getSocket()
	if err != nil {
		return err
	}

	iface, err := net.InterfaceByName(name)
	if err != nil {
		return err
	}

	req := newRequest(unix.RTM_DELLINK, unix.NLM_F_ACK)

	ifInfo := newIfInfoMsg()
	ifInfo.Index = int32(iface.Index)
	req.addPayload(ifInfo)

	return s.sendAndWaitForAck(req)
}

// SetLinkName sets the name of a network interface.
func SetLinkName(name string, newName string) error {
	s, err := getSocket()
	if err != nil {
		return err
	}

	iface, err := net.InterfaceByName(name)
	if err != nil {
		return err
	}

	req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)

	ifInfo := newIfInfoMsg()
	ifInfo.Type = unix.RTM_SETLINK
	ifInfo.Index = int32(iface.Index)
	ifInfo.Flags = unix.NLM_F_REQUEST
	ifInfo.Change = DEFAULT_CHANGE
	req.addPayload(ifInfo)

	attrName := newAttributeString(unix.IFLA_IFNAME, newName)
	req.addPayload(attrName)

	return s.sendAndWaitForAck(req)
}

// SetLinkState sets the operational state of a network interface.
func SetLinkState(name string, up bool) error {
	s, err := getSocket()
	if err != nil {
		return err
	}

	iface, err := net.InterfaceByName(name)
	if err != nil {
		return err
	}

	req := newRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK)

	ifInfo := newIfInfoMsg()
	ifInfo.Type = unix.RTM_SETLINK
	ifInfo.Index = int32(iface.Index)

	if up {
		ifInfo.Flags = unix.IFF_UP
		ifInfo.Change = unix.IFF_UP
	} else {
		ifInfo.Flags = 0 & ^unix.IFF_UP
		ifInfo.Change = DEFAULT_CHANGE
	}

	req.addPayload(ifInfo)

	return s.sendAndWaitForAck(req)
}

// SetLinkMaster sets the master (upper) device of a network interface.
func SetLinkMaster(name string, master string) error {
	s, err := getSocket()
	if err != nil {
		return err
	}

	iface, err := net.InterfaceByName(name)
	if err != nil {
		return err
	}

	var masterIndex uint32
	if master != "" {
		masterIface, err := net.InterfaceByName(master)
		if err != nil {
			return err
		}
		masterIndex = uint32(masterIface.Index)
	}

	req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)

	ifInfo := newIfInfoMsg()
	ifInfo.Type = unix.RTM_SETLINK
	ifInfo.Index = int32(iface.Index)
	ifInfo.Flags = unix.NLM_F_REQUEST
	ifInfo.Change = DEFAULT_CHANGE
	req.addPayload(ifInfo)

	attrMaster := newAttributeUint32(unix.IFLA_MASTER, masterIndex)
	req.addPayload(attrMaster)

	return s.sendAndWaitForAck(req)
}

// SetLinkNetNs sets the network namespace of a network interface.
func SetLinkNetNs(name string, fd uintptr) error {
	s, err := getSocket()
	if err != nil {
		return err
	}

	iface, err := net.InterfaceByName(name)
	if err != nil {
		return err
	}

	req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)

	ifInfo := newIfInfoMsg()
	ifInfo.Type = unix.RTM_SETLINK
	ifInfo.Index = int32(iface.Index)
	ifInfo.Flags = unix.NLM_F_REQUEST
	ifInfo.Change = DEFAULT_CHANGE
	req.addPayload(ifInfo)

	attrNetNs := newAttributeUint32(IFLA_NET_NS_FD, uint32(fd))
	req.addPayload(attrNetNs)

	return s.sendAndWaitForAck(req)
}

// SetLinkAddress sets the link layer hardware address of a network interface.
func SetLinkAddress(ifName string, hwAddress net.HardwareAddr) error {
	s, err := getSocket()
	if err != nil {
		return err
	}

	iface, err := net.InterfaceByName(ifName)
	if err != nil {
		return err
	}

	req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)

	ifInfo := newIfInfoMsg()
	ifInfo.Type = unix.RTM_SETLINK
	ifInfo.Index = int32(iface.Index)
	ifInfo.Flags = unix.NLM_F_REQUEST
	ifInfo.Change = DEFAULT_CHANGE
	req.addPayload(ifInfo)

	req.addPayload(newAttribute(unix.IFLA_ADDRESS, hwAddress))

	return s.sendAndWaitForAck(req)
}
<commit_msg>Added support for creating links in up state<commit_after>// Copyright 2017 Microsoft. All rights reserved.
// MIT License

package netlink

import (
	"fmt"
	"net"

	"golang.org/x/sys/unix"
)

// Link types.
const (
	LINK_TYPE_BRIDGE = "bridge"
	LINK_TYPE_VETH   = "veth"
	LINK_TYPE_IPVLAN = "ipvlan"
	LINK_TYPE_DUMMY  = "dummy"
)

// IPVLAN link attributes.
type IPVlanMode uint16

const (
	IPVLAN_MODE_L2 IPVlanMode = iota
	IPVLAN_MODE_L3
	IPVLAN_MODE_L3S
	IPVLAN_MODE_MAX
)

// Link represents a network interface.
type Link interface {
	Info() *LinkInfo
}

// LinkInfo respresents the common properties of all network interfaces.
type LinkInfo struct {
	Type        string
	Name        string
	Flags       net.Flags
	MTU         uint
	TxQLen      uint
	ParentIndex int
}

func (linkInfo *LinkInfo) Info() *LinkInfo {
	return linkInfo
}

// BridgeLink represents an ethernet bridge.
type BridgeLink struct {
	LinkInfo
}

// VEthLink represents a virtual ethernet network interface.
type VEthLink struct {
	LinkInfo
	PeerName string
}

// IPVlanLink represents an IPVlan network interface.
type IPVlanLink struct {
	LinkInfo
	Mode IPVlanMode
}

// DummyLink represents a dummy network interface.
type DummyLink struct {
	LinkInfo
}

// AddLink adds a new network interface of a specified type.
func AddLink(link Link) error {
	var info *LinkInfo
	info = link.Info()

	if info.Name == "" || info.Type == "" {
		return fmt.Errorf("Invalid link name or type")
	}

	s, err := getSocket()
	if err != nil {
		return err
	}

	req := newRequest(unix.RTM_NEWLINK, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)

	// Set interface information.
	ifInfo := newIfInfoMsg()

	// Set interface flags.
	if info.Flags&net.FlagUp != 0 {
		ifInfo.Change = unix.IFF_UP
		ifInfo.Flags = unix.IFF_UP
	}
	req.addPayload(ifInfo)

	// Set interface name.
	attrIfName := newAttributeStringZ(unix.IFLA_IFNAME, info.Name)
	req.addPayload(attrIfName)

	// Set MTU.
	if info.MTU > 0 {
		req.addPayload(newAttributeUint32(unix.IFLA_MTU, uint32(info.MTU)))
	}

	// Set transmission queue length.
	if info.TxQLen > 0 {
		req.addPayload(newAttributeUint32(unix.IFLA_TXQLEN, uint32(info.TxQLen)))
	}

	// Set parent interface index.
	if info.ParentIndex != 0 {
		req.addPayload(newAttributeUint32(unix.IFLA_LINK, uint32(info.ParentIndex)))
	}

	// Set link info.
	attrLinkInfo := newAttribute(unix.IFLA_LINKINFO, nil)
	attrLinkInfo.addNested(newAttributeString(IFLA_INFO_KIND, info.Type))

	// Set link type-specific attributes.
	if veth, ok := link.(*VEthLink); ok {
		// Set VEth attributes.
		attrData := newAttribute(IFLA_INFO_DATA, nil)

		attrPeer := newAttribute(VETH_INFO_PEER, nil)
		attrPeer.addNested(newIfInfoMsg())
		attrPeer.addNested(newAttributeStringZ(unix.IFLA_IFNAME, veth.PeerName))
		attrData.addNested(attrPeer)

		attrLinkInfo.addNested(attrData)

	} else if ipvlan, ok := link.(*IPVlanLink); ok {
		// Set IPVlan attributes.
		attrData := newAttribute(IFLA_INFO_DATA, nil)
		attrData.addNested(newAttributeUint16(IFLA_IPVLAN_MODE, uint16(ipvlan.Mode)))

		attrLinkInfo.addNested(attrData)
	}

	req.addPayload(attrLinkInfo)

	return s.sendAndWaitForAck(req)
}

// DeleteLink deletes a network interface.
func DeleteLink(name string) error {
	if name == "" {
		return fmt.Errorf("Invalid link name")
	}

	s, err := getSocket()
	if err != nil {
		return err
	}

	iface, err := net.InterfaceByName(name)
	if err != nil {
		return err
	}

	req := newRequest(unix.RTM_DELLINK, unix.NLM_F_ACK)

	ifInfo := newIfInfoMsg()
	ifInfo.Index = int32(iface.Index)
	req.addPayload(ifInfo)

	return s.sendAndWaitForAck(req)
}

// SetLinkName sets the name of a network interface.
func SetLinkName(name string, newName string) error {
	s, err := getSocket()
	if err != nil {
		return err
	}

	iface, err := net.InterfaceByName(name)
	if err != nil {
		return err
	}

	req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)

	ifInfo := newIfInfoMsg()
	ifInfo.Type = unix.RTM_SETLINK
	ifInfo.Index = int32(iface.Index)
	ifInfo.Flags = unix.NLM_F_REQUEST
	ifInfo.Change = DEFAULT_CHANGE
	req.addPayload(ifInfo)

	attrName := newAttributeString(unix.IFLA_IFNAME, newName)
	req.addPayload(attrName)

	return s.sendAndWaitForAck(req)
}

// SetLinkState sets the operational state of a network interface.
func SetLinkState(name string, up bool) error {
	s, err := getSocket()
	if err != nil {
		return err
	}

	iface, err := net.InterfaceByName(name)
	if err != nil {
		return err
	}

	req := newRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK)

	ifInfo := newIfInfoMsg()
	ifInfo.Type = unix.RTM_SETLINK
	ifInfo.Index = int32(iface.Index)

	if up {
		ifInfo.Flags = unix.IFF_UP
		ifInfo.Change = unix.IFF_UP
	} else {
		ifInfo.Flags = 0 & ^unix.IFF_UP
		ifInfo.Change = DEFAULT_CHANGE
	}

	req.addPayload(ifInfo)

	return s.sendAndWaitForAck(req)
}

// SetLinkMaster sets the master (upper) device of a network interface.
func SetLinkMaster(name string, master string) error {
	s, err := getSocket()
	if err != nil {
		return err
	}

	iface, err := net.InterfaceByName(name)
	if err != nil {
		return err
	}

	var masterIndex uint32
	if master != "" {
		masterIface, err := net.InterfaceByName(master)
		if err != nil {
			return err
		}
		masterIndex = uint32(masterIface.Index)
	}

	req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)

	ifInfo := newIfInfoMsg()
	ifInfo.Type = unix.RTM_SETLINK
	ifInfo.Index = int32(iface.Index)
	ifInfo.Flags = unix.NLM_F_REQUEST
	ifInfo.Change = DEFAULT_CHANGE
	req.addPayload(ifInfo)

	attrMaster := newAttributeUint32(unix.IFLA_MASTER, masterIndex)
	req.addPayload(attrMaster)

	return s.sendAndWaitForAck(req)
}

// SetLinkNetNs sets the network namespace of a network interface.
func SetLinkNetNs(name string, fd uintptr) error {
	s, err := getSocket()
	if err != nil {
		return err
	}

	iface, err := net.InterfaceByName(name)
	if err != nil {
		return err
	}

	req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)

	ifInfo := newIfInfoMsg()
	ifInfo.Type = unix.RTM_SETLINK
	ifInfo.Index = int32(iface.Index)
	ifInfo.Flags = unix.NLM_F_REQUEST
	ifInfo.Change = DEFAULT_CHANGE
	req.addPayload(ifInfo)

	attrNetNs := newAttributeUint32(IFLA_NET_NS_FD, uint32(fd))
	req.addPayload(attrNetNs)

	return s.sendAndWaitForAck(req)
}

// SetLinkAddress sets the link layer hardware address of a network interface.
func SetLinkAddress(ifName string, hwAddress net.HardwareAddr) error {
	s, err := getSocket()
	if err != nil {
		return err
	}

	iface, err := net.InterfaceByName(ifName)
	if err != nil {
		return err
	}

	req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)

	ifInfo := newIfInfoMsg()
	ifInfo.Type = unix.RTM_SETLINK
	ifInfo.Index = int32(iface.Index)
	ifInfo.Flags = unix.NLM_F_REQUEST
	ifInfo.Change = DEFAULT_CHANGE
	req.addPayload(ifInfo)

	req.addPayload(newAttribute(unix.IFLA_ADDRESS, hwAddress))

	return s.sendAndWaitForAck(req)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	serial "github.com/tarm/serial"
	"log"
	"os"
	"time"
)

type Scale struct {
}

func (scale Scale) Open() (port *serial.Port, err error) {
	c := &serial.Config{Name: "/dev/ttyUSB0", Baud: 9600}
	port, err = serial.OpenPort(c)
	return
}

func (scale Scale) Read(port *serial.Port) (value string) {
	port.Write([]byte("IP\r\n"))
	scanner := bufio.NewScanner(port)
	scanner.Scan()

	value = scanner.Text()
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
	}
	return
}

func main() {
	scale := Scale{}
	port, err := scale.Open()
	if err != nil {
		log.Fatal(err)
	}
	for {
		value := scale.Read(port)
		fmt.Println(time.Now(), value)
	}
}
<commit_msg>run the reader in a separate loop<commit_after>package main

import (
	"bufio"
	"fmt"
	serial "github.com/tarm/serial"
	"log"
	"strconv"
	"strings"
	"time"
)

type Scale struct {
}

type Datum struct {
	time   time.Time
	weight float64
	unit   string
}

func (scale Scale) Open() (port *serial.Port, err error) {
	c := &serial.Config{Name: "/dev/ttyUSB0", Baud: 9600}
	port, err = serial.OpenPort(c)
	return
}

func (scale Scale) Read(port *serial.Port) (value string, err error) {
	port.Write([]byte("IP\r\n"))
	scanner := bufio.NewScanner(port)
	scanner.Scan()
	value = scanner.Text()
	err = scanner.Err()
	return
}

func (scale Scale) Reader(c chan Datum) {
	port, err := scale.Open()
	if err != nil {
		log.Fatal(err)
	}
	for {
		v, err := scale.Read(port)
		if err != nil {
			log.Fatal(err)
		}
		value := strings.Split(strings.Trim(v, " "), " ")
		weight, err := strconv.ParseFloat(value[0], 64)
		if err != nil {
			log.Fatal(err)
		}

		d := Datum{
			time:   time.Now(),
			weight: weight,
			unit:   value[1],
		}

		// fmt.Println(d)
		c <- d
	}
}

func main() {
	c := make(chan Datum)
	scale := Scale{}
	go scale.Reader(c)
	for {
		d := <-c
		fmt.Println(d.time, d.weight, d.unit)
	}
}
<|endoftext|>


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

// Package starter provides a single function that starts up servers for a
// mounttable and a device manager that is mounted on it.
package starter

import (
	"encoding/base64"
	"net"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"v.io/v23"
	"v.io/v23/context"
	"v.io/v23/naming"
	"v.io/v23/rpc"
	"v.io/v23/security"
	"v.io/v23/verror"
	"v.io/x/ref/lib/xrpc"
	"v.io/x/ref/runtime/factories/roaming"
	"v.io/x/ref/services/debug/debuglib"
	"v.io/x/ref/services/device/deviced/internal/impl"
	"v.io/x/ref/services/device/deviced/internal/versioning"
	"v.io/x/ref/services/device/internal/config"
	"v.io/x/ref/services/internal/pathperms"
	"v.io/x/ref/services/mounttable/mounttablelib"
)

const pkgPath = "v.io/x/ref/services/device/deviced/internal/starter"

var (
	errCantSaveInfo      = verror.Register(pkgPath+".errCantSaveInfo", verror.NoRetry, "{1:}{2:} failed to save info{:_}")
	errBadPort           = verror.Register(pkgPath+".errBadPort", verror.NoRetry, "{1:}{2:} invalid port{:_}")
	errCantCreateProxy   = verror.Register(pkgPath+".errCantCreateProxy", verror.NoRetry, "{1:}{2:} Failed to create proxy{:_}")
	errNoEndpointToClaim = verror.Register(pkgPath+".errNoEndpointToClaim", verror.NoRetry, "{1:}{2:} failed to find an endpoint for claiming{:_}")
)

type NamespaceArgs struct {
	Name            string         // Name to publish the mounttable service under (after claiming).
	ListenSpec      rpc.ListenSpec // ListenSpec for the server.
	PermissionsFile string         // Path to the Permissions file used by the mounttable.
	PersistenceDir  string         // Path to the directory holding persistent acls.
	// Name in the local neighborhood on which to make the mounttable
	// visible. If empty, the mounttable will not be visible in the local
	// neighborhood.
	Neighborhood string
}

type DeviceArgs struct {
	Name            string         // Name to publish the device service under (after claiming).
	ListenSpec      rpc.ListenSpec // ListenSpec for the device server.
	ConfigState     *config.State  // Configuration for the device.
	TestMode        bool           // Whether the device is running in test mode or not.
	RestartCallback func()         // Callback invoked when the device service is restarted.
	PairingToken    string         // PairingToken that a claimer needs to provide.
}

func (d *DeviceArgs) name(mt string) string {
	if d.Name != "" {
		return d.Name
	}
	return naming.Join(mt, "devmgr")
}

type ProxyArgs struct {
	Port int
}

type Args struct {
	Namespace NamespaceArgs
	Device    DeviceArgs
	Proxy     ProxyArgs

	// If true, the global namespace will be made available on the
	// mounttable server under "global/".
	MountGlobalNamespaceInLocalNamespace bool
}

// Start creates servers for the mounttable and device services and links them together.
//
// Returns the object name for the claimable service (empty if already claimed),
// a callback to be invoked to shutdown the services on success, or an error on
// failure.
func Start(ctx *context.T, args Args) (string, func(), error) {
	// Is this binary compatible with the state on disk?
	if err := versioning.CheckCompatibility(ctx, args.Device.ConfigState.Root); err != nil {
		return "", nil, err
	}
	// In test mode, we skip writing the info file to disk, and we skip
	// attempting to start the claimable service: the device must have been
	// claimed already to enable updates anyway, and checking for perms in
	// NewClaimableDispatcher needlessly prints a perms signature
	// verification error to the logs.
	if args.Device.TestMode {
		cleanup, err := startClaimedDevice(ctx, args)
		return "", cleanup, err
	}

	// TODO(caprita): use some mechanism (a file lock or presence of entry
	// in mounttable) to ensure only one device manager is running in an
	// installation?
	mi := &impl.ManagerInfo{
		Pid: os.Getpid(),
	}
	if err := impl.SaveManagerInfo(filepath.Join(args.Device.ConfigState.Root, "device-manager"), mi); err != nil {
		return "", nil, verror.New(errCantSaveInfo, ctx, err)
	}

	// If the device has not yet been claimed, start the mounttable and
	// claimable service and wait for it to be claimed.
	// Once a device is claimed, close any previously running servers and
	// start a new mounttable and device service.
	claimable, claimed := impl.NewClaimableDispatcher(ctx, args.Device.ConfigState, args.Device.PairingToken)
	if claimable == nil {
		// Device has already been claimed, bypass claimable service
		// stage.
		cleanup, err := startClaimedDevice(ctx, args)
		return "", cleanup, err
	}
	epName, stopClaimable, err := startClaimableDevice(ctx, claimable, args)
	if err != nil {
		return "", nil, err
	}
	stop := make(chan struct{})
	stopped := make(chan struct{})
	go waitToBeClaimedAndStartClaimedDevice(ctx, stopClaimable, claimed, stop, stopped, args)
	return epName, func() {
		close(stop)
		<-stopped
	}, nil
}

func startClaimableDevice(ctx *context.T, dispatcher rpc.Dispatcher, args Args) (string, func(), error) {
	// TODO(caprita,ashankar): We create a context with a new stream manager
	// that we can cancel once the device has been claimed. This gets around
	// the following issue: if we publish the claimable server to the local
	// mounttable, and then (following claim) we restart the mounttable
	// server on the same port, we fail to publish the device service to the
	// (new) mounttable server (Mount fails with "VC handshake failed:
	// remote end closed VC(VCs not accepted)".  Presumably, something to do
	// with caching connections (following the claim, the mounttable comes
	// back on the same port as before, and the client-side of the mount
	// gets confused trying to reuse the old connection and doesn't attempt
	// to create a new connection).  We should get to the bottom of it.
	ctx, cancel := context.WithCancel(ctx)
	var err error
	if ctx, err = v23.WithNewStreamManager(ctx); err != nil {
		cancel()
		return "", nil, err
	}
	ctx = v23.WithListenSpec(ctx, args.Device.ListenSpec)
	server, err := xrpc.NewDispatchingServer(ctx, "", dispatcher)
	if err != nil {
		cancel()
		return "", nil, err
	}
	shutdown := func() {
		ctx.Infof("Stopping claimable server...")
		server.Stop()
		ctx.Infof("Stopped claimable server.")
		cancel()
	}
	endpoints := server.Status().Endpoints
	publicKey, err := v23.GetPrincipal(ctx).PublicKey().MarshalBinary()
	if err != nil {
		shutdown()
		return "", nil, err
	}
	var epName string
	if args.Device.ListenSpec.Proxy != "" {
		for {
			p := server.Status().Proxies
			if len(p) == 0 {
				ctx.Infof("Waiting for proxy address to appear...")
				time.Sleep(time.Second)
				continue
			}
			epName = p[0].Endpoint.Name()
			ctx.Infof("Proxied address: %s", epName)
			break
		}
	} else {
		if len(endpoints) == 0 {
			return "", nil, verror.New(errNoEndpointToClaim, ctx, err)
		}
		epName = endpoints[0].Name()
	}
	ctx.Infof("Unclaimed device manager (%v) with public_key: %s", epName, base64.URLEncoding.EncodeToString(publicKey))
	ctx.FlushLog()
	return epName, shutdown, nil
}

func waitToBeClaimedAndStartClaimedDevice(ctx *context.T, stopClaimable func(), claimed, stop <-chan struct{}, stopped chan<- struct{}, args Args) {
	// Wait for either the claimable service to complete, or be stopped
	defer close(stopped)
	select {
	case <-claimed:
		stopClaimable()
	case <-stop:
		stopClaimable()
		return
	}
	shutdown, err := startClaimedDevice(ctx, args)
	if err != nil {
		ctx.Errorf("Failed to start device service after it was claimed: %v", err)
		v23.GetAppCycle(ctx).Stop(ctx)
		return
	}
	defer shutdown()
	<-stop // Wait to be stopped
}

func startClaimedDevice(ctx *context.T, args Args) (func(), error) {
	permStore := pathperms.NewPathStore(ctx)
	permsDir := impl.PermsDir(args.Device.ConfigState)
	debugAuth, err := pathperms.NewHierarchicalAuthorizer(permsDir, permsDir, permStore, nil)
	if err != nil {
		return nil, err
	}

	debugDisp := debuglib.NewDispatcher(debugAuth)

	ctx = v23.WithReservedNameDispatcher(ctx, debugDisp)

	mtName, stopMT, err := startMounttable(ctx, args.Namespace)
	if err != nil {
		ctx.Errorf("Failed to start mounttable service: %v", err)
		return nil, err
	}
	// TODO(caprita): We link in a proxy server into the device manager so that we
	// can bootstrap with install-local before we can install an actual proxy app.
	// Once support is added to the RPC layer to allow install-local to serve on
	// the same connection it established to the device manager (see TODO in
	// v.io/x/ref/services/device/device/local_install.go), we can get rid of this
	// local proxy altogether.
	stopProxy, err := startProxyServer(ctx, args.Proxy, mtName)
	if err != nil {
		ctx.Errorf("Failed to start proxy service: %v", err)
		stopMT()
		return nil, err
	}
	stopDevice, err := startDeviceServer(ctx, args.Device, mtName, permStore)
	if err != nil {
		ctx.Errorf("Failed to start device service: %v", err)
		stopProxy()
		stopMT()
		return nil, err
	}
	if args.MountGlobalNamespaceInLocalNamespace {
		mountGlobalNamespaceInLocalNamespace(ctx, mtName)
	}

	impl.InvokeCallback(ctx, args.Device.ConfigState.Name)

	return func() {
		stopDevice()
		stopProxy()
		stopMT()
	}, nil
}

func startProxyServer(ctx *context.T, p ProxyArgs, localMT string) (func(), error) {
	switch port := p.Port; {
	case port == 0:
		return func() {}, nil
	case port < 0:
		return nil, verror.New(errBadPort, ctx, port)
	}
	port := strconv.Itoa(p.Port)
	protocol, addr := "tcp", net.JoinHostPort("", port)
	// Attempt to get a publicly accessible address for the proxy to publish
	// under.
	ls := v23.GetListenSpec(ctx)
	ls.Addrs = rpc.ListenAddrs{{protocol, addr}}
	// TODO(ashankar): Revisit this choice of security.AllowEveryone
	// See: https://v.io/i/387
	shutdown, ep, err := roaming.NewProxy(ctx, ls, security.AllowEveryone())
	if err != nil {
		return nil, verror.New(errCantCreateProxy, ctx, err)
	}
	ctx.Infof("Local proxy (%v)", ep.Name())
	return func() {
		ctx.Infof("Stopping proxy...")
		shutdown()
		ctx.Infof("Stopped proxy.")
	}, nil
}

func startMounttable(ctx *context.T, n NamespaceArgs) (string, func(), error) {
	mtName, stopMT, err := mounttablelib.StartServers(ctx, n.ListenSpec, n.Name, n.Neighborhood, n.PermissionsFile, n.PersistenceDir, "mounttable")
	if err != nil {
		ctx.Errorf("mounttablelib.StartServers(%#v) failed: %v", n, err)
	} else {
		ctx.Infof("Local mounttable (%v) published as %q", mtName, n.Name)
	}
	return mtName, func() {
		ctx.Infof("Stopping mounttable...")
		stopMT()
		ctx.Infof("Stopped mounttable.")
	}, err
}

// startDeviceServer creates an rpc.Server and sets it up to server the Device service.
//
// ls: ListenSpec for the server
// configState: configuration for the Device service dispatcher
// mt: Object address of the mounttable
// dm: Name to publish the device service under
// testMode: whether the service is to be run in test mode
// restarted: callback invoked when the device manager is restarted.
//
// Returns:
// (1) Function to be called to force the service to shutdown
// (2) Any errors in starting the service (in which case, (1) will be nil)
func startDeviceServer(ctx *context.T, args DeviceArgs, mt string, permStore *pathperms.PathStore) (shutdown func(), err error) {
	server, err := v23.NewServer(ctx)
	if err != nil {
		return nil, err
	}
	shutdown = func() { server.Stop() }
	endpoints, err := server.Listen(args.ListenSpec)
	if err != nil {
		shutdown()
		return nil, err
	}
	args.ConfigState.Name = endpoints[0].Name()

	dispatcher, dShutdown, err := impl.NewDispatcher(ctx, args.ConfigState, mt, args.TestMode, args.RestartCallback, permStore)
	if err != nil {
		shutdown()
		return nil, err
	}

	shutdown = func() {
		// TODO(caprita): Capture the Dying state by feeding it back to
		// the dispatcher and exposing it in Status.
		ctx.Infof("Stopping device server...")
		server.Stop()
		dShutdown()
		ctx.Infof("Stopped device.")
	}
	if err := server.ServeDispatcher(args.name(mt), dispatcher); err != nil {
		shutdown()
		return nil, err
	}
	ctx.Infof("Device manager (%v) published as %v", args.ConfigState.Name, args.name(mt))
	return shutdown, nil
}

func mountGlobalNamespaceInLocalNamespace(ctx *context.T, localMT string) {
	ns := v23.GetNamespace(ctx)
	for _, root := range ns.Roots() {
		go func(r string) {
			for {
				err := ns.Mount(ctx, naming.Join(localMT, "global"), r, 0 /* forever */, naming.ServesMountTable(true))
				if err == nil {
					break
				}
				ctx.Infof("Failed to Mount global namespace: %v", err)
				time.Sleep(time.Second)
			}
		}(root)
	}
}
<commit_msg>service/device/deviced/internal/starter: more logging<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package starter provides a single function that starts up servers for a
// mounttable and a device manager that is mounted on it.
package starter

import (
	"encoding/base64"
	"net"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"v.io/v23"
	"v.io/v23/context"
	"v.io/v23/naming"
	"v.io/v23/rpc"
	"v.io/v23/security"
	"v.io/v23/verror"
	"v.io/x/ref/lib/xrpc"
	"v.io/x/ref/runtime/factories/roaming"
	"v.io/x/ref/services/debug/debuglib"
	"v.io/x/ref/services/device/deviced/internal/impl"
	"v.io/x/ref/services/device/deviced/internal/versioning"
	"v.io/x/ref/services/device/internal/config"
	"v.io/x/ref/services/internal/pathperms"
	"v.io/x/ref/services/mounttable/mounttablelib"
)

const pkgPath = "v.io/x/ref/services/device/deviced/internal/starter"

var (
	errCantSaveInfo      = verror.Register(pkgPath+".errCantSaveInfo", verror.NoRetry, "{1:}{2:} failed to save info{:_}")
	errBadPort           = verror.Register(pkgPath+".errBadPort", verror.NoRetry, "{1:}{2:} invalid port{:_}")
	errCantCreateProxy   = verror.Register(pkgPath+".errCantCreateProxy", verror.NoRetry, "{1:}{2:} Failed to create proxy{:_}")
	errNoEndpointToClaim = verror.Register(pkgPath+".errNoEndpointToClaim", verror.NoRetry, "{1:}{2:} failed to find an endpoint for claiming{:_}")
)

type NamespaceArgs struct {
	Name            string         // Name to publish the mounttable service under (after claiming).
	ListenSpec      rpc.ListenSpec // ListenSpec for the server.
	PermissionsFile string         // Path to the Permissions file used by the mounttable.
	PersistenceDir  string         // Path to the directory holding persistent acls.
	// Name in the local neighborhood on which to make the mounttable
	// visible. If empty, the mounttable will not be visible in the local
	// neighborhood.
	Neighborhood string
}

type DeviceArgs struct {
	Name            string         // Name to publish the device service under (after claiming).
	ListenSpec      rpc.ListenSpec // ListenSpec for the device server.
	ConfigState     *config.State  // Configuration for the device.
	TestMode        bool           // Whether the device is running in test mode or not.
	RestartCallback func()         // Callback invoked when the device service is restarted.
	PairingToken    string         // PairingToken that a claimer needs to provide.
}

func (d *DeviceArgs) name(mt string) string {
	if d.Name != "" {
		return d.Name
	}
	return naming.Join(mt, "devmgr")
}

type ProxyArgs struct {
	Port int
}

type Args struct {
	Namespace NamespaceArgs
	Device    DeviceArgs
	Proxy     ProxyArgs

	// If true, the global namespace will be made available on the
	// mounttable server under "global/".
	MountGlobalNamespaceInLocalNamespace bool
}

// Start creates servers for the mounttable and device services and links them together.
//
// Returns the object name for the claimable service (empty if already claimed),
// a callback to be invoked to shutdown the services on success, or an error on
// failure.
func Start(ctx *context.T, args Args) (string, func(), error) {
	// Is this binary compatible with the state on disk?
	if err := versioning.CheckCompatibility(ctx, args.Device.ConfigState.Root); err != nil {
		return "", nil, err
	}
	// In test mode, we skip writing the info file to disk, and we skip
	// attempting to start the claimable service: the device must have been
	// claimed already to enable updates anyway, and checking for perms in
	// NewClaimableDispatcher needlessly prints a perms signature
	// verification error to the logs.
	if args.Device.TestMode {
		cleanup, err := startClaimedDevice(ctx, args)
		return "", cleanup, err
	}

	// TODO(caprita): use some mechanism (a file lock or presence of entry
	// in mounttable) to ensure only one device manager is running in an
	// installation?
	mi := &impl.ManagerInfo{
		Pid: os.Getpid(),
	}
	if err := impl.SaveManagerInfo(filepath.Join(args.Device.ConfigState.Root, "device-manager"), mi); err != nil {
		return "", nil, verror.New(errCantSaveInfo, ctx, err)
	}

	// If the device has not yet been claimed, start the mounttable and
	// claimable service and wait for it to be claimed.
	// Once a device is claimed, close any previously running servers and
	// start a new mounttable and device service.
	claimable, claimed := impl.NewClaimableDispatcher(ctx, args.Device.ConfigState, args.Device.PairingToken)
	if claimable == nil {
		// Device has already been claimed, bypass claimable service
		// stage.
		cleanup, err := startClaimedDevice(ctx, args)
		return "", cleanup, err
	}
	epName, stopClaimable, err := startClaimableDevice(ctx, claimable, args)
	if err != nil {
		return "", nil, err
	}
	stop := make(chan struct{})
	stopped := make(chan struct{})
	go waitToBeClaimedAndStartClaimedDevice(ctx, stopClaimable, claimed, stop, stopped, args)
	return epName, func() {
		close(stop)
		<-stopped
	}, nil
}

func startClaimableDevice(ctx *context.T, dispatcher rpc.Dispatcher, args Args) (string, func(), error) {
	// TODO(caprita,ashankar): We create a context with a new stream manager
	// that we can cancel once the device has been claimed. This gets around
	// the following issue: if we publish the claimable server to the local
	// mounttable, and then (following claim) we restart the mounttable
	// server on the same port, we fail to publish the device service to the
	// (new) mounttable server (Mount fails with "VC handshake failed:
	// remote end closed VC(VCs not accepted)".  Presumably, something to do
	// with caching connections (following the claim, the mounttable comes
	// back on the same port as before, and the client-side of the mount
	// gets confused trying to reuse the old connection and doesn't attempt
	// to create a new connection).  We should get to the bottom of it.
	ctx, cancel := context.WithCancel(ctx)
	var err error
	if ctx, err = v23.WithNewStreamManager(ctx); err != nil {
		cancel()
		return "", nil, err
	}
	ctx = v23.WithListenSpec(ctx, args.Device.ListenSpec)
	server, err := xrpc.NewDispatchingServer(ctx, "", dispatcher)
	if err != nil {
		cancel()
		return "", nil, err
	}
	shutdown := func() {
		ctx.Infof("Stopping claimable server...")
		server.Stop()
		ctx.Infof("Stopped claimable server.")
		cancel()
	}
	endpoints := server.Status().Endpoints
	publicKey, err := v23.GetPrincipal(ctx).PublicKey().MarshalBinary()
	if err != nil {
		shutdown()
		return "", nil, err
	}
	var epName string
	if args.Device.ListenSpec.Proxy != "" {
		for {
			p := server.Status().Proxies
			if len(p) == 0 {
				ctx.Infof("Waiting for proxy address to appear...")
				time.Sleep(time.Second)
				continue
			}
			epName = p[0].Endpoint.Name()
			ctx.Infof("Proxied address: %s", epName)
			break
		}
	} else {
		if len(endpoints) == 0 {
			return "", nil, verror.New(errNoEndpointToClaim, ctx, err)
		}
		epName = endpoints[0].Name()
	}
	ctx.Infof("Unclaimed device manager (%v) with public_key: %s", epName, base64.URLEncoding.EncodeToString(publicKey))
	ctx.FlushLog()
	return epName, shutdown, nil
}

func waitToBeClaimedAndStartClaimedDevice(ctx *context.T, stopClaimable func(), claimed, stop <-chan struct{}, stopped chan<- struct{}, args Args) {
	// Wait for either the claimable service to complete, or be stopped
	defer close(stopped)
	select {
	case <-claimed:
		stopClaimable()
	case <-stop:
		stopClaimable()
		return
	}
	shutdown, err := startClaimedDevice(ctx, args)
	if err != nil {
		ctx.Errorf("Failed to start device service after it was claimed: %v", err)
		v23.GetAppCycle(ctx).Stop(ctx)
		return
	}
	defer shutdown()
	<-stop // Wait to be stopped
}

func startClaimedDevice(ctx *context.T, args Args) (func(), error) {
	ctx.Infof("Starting claimed device services...")
	permStore := pathperms.NewPathStore(ctx)
	permsDir := impl.PermsDir(args.Device.ConfigState)
	debugAuth, err := pathperms.NewHierarchicalAuthorizer(permsDir, permsDir, permStore, nil)
	if err != nil {
		return nil, err
	}

	debugDisp := debuglib.NewDispatcher(debugAuth)

	ctx = v23.WithReservedNameDispatcher(ctx, debugDisp)

	ctx.Infof("Starting mount table...")
	mtName, stopMT, err := startMounttable(ctx, args.Namespace)
	if err != nil {
		ctx.Errorf("Failed to start mounttable service: %v", err)
		return nil, err
	} else {
		ctx.Infof("Started mount table.")
	}
	// TODO(caprita): We link in a proxy server into the device manager so that we
	// can bootstrap with install-local before we can install an actual proxy app.
	// Once support is added to the RPC layer to allow install-local to serve on
	// the same connection it established to the device manager (see TODO in
	// v.io/x/ref/services/device/device/local_install.go), we can get rid of this
	// local proxy altogether.
	ctx.Infof("Starting proxy service...")
	stopProxy, err := startProxyServer(ctx, args.Proxy, mtName)
	if err != nil {
		ctx.Errorf("Failed to start proxy service: %v", err)
		stopMT()
		return nil, err
	} else {
		ctx.Infof("Started proxy service.")
	}
	ctx.Infof("Starting device service...")
	stopDevice, err := startDeviceServer(ctx, args.Device, mtName, permStore)
	if err != nil {
		ctx.Errorf("Failed to start device service: %v", err)
		stopProxy()
		stopMT()
		return nil, err
	} else {
		ctx.Infof("Started device service.")
	}
	if args.MountGlobalNamespaceInLocalNamespace {
		ctx.Infof("Mounting %v ...", mtName)
		mountGlobalNamespaceInLocalNamespace(ctx, mtName)
		ctx.Infof("Mounted %v", mtName)
	}

	impl.InvokeCallback(ctx, args.Device.ConfigState.Name)

	ctx.Infof("Started claimed device services.")
	return func() {
		stopDevice()
		stopProxy()
		stopMT()
	}, nil
}

func startProxyServer(ctx *context.T, p ProxyArgs, localMT string) (func(), error) {
	switch port := p.Port; {
	case port == 0:
		return func() {}, nil
	case port < 0:
		return nil, verror.New(errBadPort, ctx, port)
	}
	port := strconv.Itoa(p.Port)
	protocol, addr := "tcp", net.JoinHostPort("", port)
	// Attempt to get a publicly accessible address for the proxy to publish
	// under.
	ls := v23.GetListenSpec(ctx)
	ls.Addrs = rpc.ListenAddrs{{protocol, addr}}
	// TODO(ashankar): Revisit this choice of security.AllowEveryone
	// See: https://v.io/i/387
	shutdown, ep, err := roaming.NewProxy(ctx, ls, security.AllowEveryone())
	if err != nil {
		return nil, verror.New(errCantCreateProxy, ctx, err)
	}
	ctx.Infof("Local proxy (%v)", ep.Name())
	return func() {
		ctx.Infof("Stopping proxy...")
		shutdown()
		ctx.Infof("Stopped proxy.")
	}, nil
}

func startMounttable(ctx *context.T, n NamespaceArgs) (string, func(), error) {
	mtName, stopMT, err := mounttablelib.StartServers(ctx, n.ListenSpec, n.Name, n.Neighborhood, n.PermissionsFile, n.PersistenceDir, "mounttable")
	if err != nil {
		ctx.Errorf("mounttablelib.StartServers(%#v) failed: %v", n, err)
	} else {
		ctx.Infof("Local mounttable (%v) published as %q", mtName, n.Name)
	}
	return mtName, func() {
		ctx.Infof("Stopping mounttable...")
		stopMT()
		ctx.Infof("Stopped mounttable.")
	}, err
}

// startDeviceServer creates an rpc.Server and sets it up to server the Device service.
//
// ls: ListenSpec for the server
// configState: configuration for the Device service dispatcher
// mt: Object address of the mounttable
// dm: Name to publish the device service under
// testMode: whether the service is to be run in test mode
// restarted: callback invoked when the device manager is restarted.
//
// Returns:
// (1) Function to be called to force the service to shutdown
// (2) Any errors in starting the service (in which case, (1) will be nil)
func startDeviceServer(ctx *context.T, args DeviceArgs, mt string, permStore *pathperms.PathStore) (shutdown func(), err error) {
	server, err := v23.NewServer(ctx)
	if err != nil {
		return nil, err
	}
	shutdown = func() { server.Stop() }
	endpoints, err := server.Listen(args.ListenSpec)
	if err != nil {
		shutdown()
		return nil, err
	}
	args.ConfigState.Name = endpoints[0].Name()

	dispatcher, dShutdown, err := impl.NewDispatcher(ctx, args.ConfigState, mt, args.TestMode, args.RestartCallback, permStore)
	if err != nil {
		shutdown()
		return nil, err
	}

	shutdown = func() {
		// TODO(caprita): Capture the Dying state by feeding it back to
		// the dispatcher and exposing it in Status.
		ctx.Infof("Stopping device server...")
		server.Stop()
		dShutdown()
		ctx.Infof("Stopped device.")
	}
	if err := server.ServeDispatcher(args.name(mt), dispatcher); err != nil {
		shutdown()
		return nil, err
	}
	ctx.Infof("Device manager (%v) published as %v", args.ConfigState.Name, args.name(mt))
	return shutdown, nil
}

func mountGlobalNamespaceInLocalNamespace(ctx *context.T, localMT string) {
	ns := v23.GetNamespace(ctx)
	for _, root := range ns.Roots() {
		go func(r string) {
			for {
				err := ns.Mount(ctx, naming.Join(localMT, "global"), r, 0 /* forever */, naming.ServesMountTable(true))
				if err == nil {
					break
				}
				ctx.Infof("Failed to Mount global namespace: %v", err)
				time.Sleep(time.Second)
			}
		}(root)
	}
}
<|endoftext|>


<commit_before>package git_pipeline_test

import (
	"time"

	"github.com/concourse/testflight/guidserver"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("A pipeline with git resources", func() {
	It("triggers when the repo changes", func() {
		By("building the initial commit")
		guid1 := gitServer.Commit()
		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement(guid1))

		By("building another commit")
		guid2 := gitServer.Commit()
		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement(guid2))
	})

	It("performs output conditions correctly", func() {
		By("executing the build when a commit is made")
		committedGuid := gitServer.Commit()
		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement(committedGuid))

		masterSHA := gitServer.RevParse("master")
		Ω(masterSHA).ShouldNot(BeEmpty())

		By("performing on: [success] outputs on success")
		Eventually(func() string {
			return successGitServer.RevParse("success")
		}, 30*time.Second, 1*time.Second).Should(Equal(masterSHA))

		By("performing on: [failure] outputs on failure")
		Eventually(func() string {
			return failureGitServer.RevParse("failure")
		}, 30*time.Second, 1*time.Second).Should(Equal(masterSHA))

		By("not performing on: [success] outputs on failure")
		Consistently(func() string {
			return noUpdateGitServer.RevParse("no-update")
		}, 10*time.Second, 1*time.Second).Should(BeEmpty())

		By("always performs ensure on: [success]")
		Consistently(func() string {
			return ensureSuccessGitServer.RevParse("ensure-success")
		}, 10*time.Second, 1*time.Second).Should(Equal(masterSHA))

		By("always performs ensure on: [failure]")
		Consistently(func() string {
			return ensureFailureGitServer.RevParse("ensure-failure")
		}, 10*time.Second, 1*time.Second).Should(Equal(masterSHA))
	})

	It("performs build matrixes correctly", func() {
		By("executing the build when a commit is made")
		committedGuid := gitServer.Commit()

		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement("passing-unit-1/file passing-unit-2/file " + committedGuid))

		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement("failed " + committedGuid))
	})
})
<commit_msg>fix race: use Eventually to wait for ensure hooks<commit_after>package git_pipeline_test

import (
	"time"

	"github.com/concourse/testflight/guidserver"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("A pipeline with git resources", func() {
	It("triggers when the repo changes", func() {
		By("building the initial commit")
		guid1 := gitServer.Commit()
		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement(guid1))

		By("building another commit")
		guid2 := gitServer.Commit()
		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement(guid2))
	})

	It("performs output conditions correctly", func() {
		By("executing the build when a commit is made")
		committedGuid := gitServer.Commit()
		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement(committedGuid))

		masterSHA := gitServer.RevParse("master")
		Ω(masterSHA).ShouldNot(BeEmpty())

		By("performing on: [success] outputs on success")
		Eventually(func() string {
			return successGitServer.RevParse("success")
		}, 30*time.Second, 1*time.Second).Should(Equal(masterSHA))

		By("performing on: [failure] outputs on failure")
		Eventually(func() string {
			return failureGitServer.RevParse("failure")
		}, 30*time.Second, 1*time.Second).Should(Equal(masterSHA))

		By("not performing on: [success] outputs on failure")
		Consistently(func() string {
			return noUpdateGitServer.RevParse("no-update")
		}, 10*time.Second, 1*time.Second).Should(BeEmpty())

		By("always performs ensure on: [success]")
		Eventually(func() string {
			return ensureSuccessGitServer.RevParse("ensure-success")
		}, 30*time.Second, 1*time.Second).Should(Equal(masterSHA))

		By("always performs ensure on: [failure]")
		Eventually(func() string {
			return ensureFailureGitServer.RevParse("ensure-failure")
		}, 30*time.Second, 1*time.Second).Should(Equal(masterSHA))
	})

	It("performs build matrixes correctly", func() {
		By("executing the build when a commit is made")
		committedGuid := gitServer.Commit()

		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement("passing-unit-1/file passing-unit-2/file " + committedGuid))

		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement("failed " + committedGuid))
	})
})
<|endoftext|>


<commit_before>package models

import (
	"gopkg.in/mgo.v2/bson"
)

type Group struct {
	Id                             bson.ObjectId            `bson:"_id" json:"-"`
	Body                           string                   `bson:"body" json:"body"`
	Title                          string                   `bson:"title" json:"title"`
	Slug                           string                   `bson:"slug" json:"slug"`
	Privacy                        string                   `bson:"privacy" json:"privacy"`
	Visibility                     string                   `bson:"visibility" json:"visibility"`
	SocialApiChannelId             string                   `bson:"socialApiChannelId" json:"socialApiChannelId"`
	SocialApiAnnouncementChannelId string                   `bson:"socialApiAnnouncementChannelId" json:"socialApiAnnouncementChannelId"`
	SocialApiDefaultChannelId      string                   `bson:"socialApiDefaultChannelId" json:"socialApiDefaultChannelId"`
	Parent                         []map[string]interface{} `bson:"parent" json:"parent"`
	Customize                      map[string]interface{}   `bson:"customize" json:"customize"`
	Counts                         map[string]interface{}   `bson:"counts" json:"counts"`
	Migration                      string                   `bson:"migration,omitempty" json:"migration"`
	StackTemplate                  []string                 `bson:"stackTemplates,omitempty" json:"stackTemplates"`
	// DefaultChannels holds the default channels for a group, when a user joins
	// to this group, participants will be automatically added to regarding
	// channels
	DefaultChannels []string `bson:"defaultChannels,omitempty" json:"defaultChannels"`
	Payment         Payment  `bson:"payment" json:"payment"`
}

type Payment struct {
	Subscription Subscription
	Customer     Customer
}

type Subscription struct {
	// Allowed values are "trialing", "active", "past_due", "canceled", "unpaid".
	State string `bson:"state" json:"state"`
	ID    string `bson:"id" json:"id"`
}

type Customer struct {
	ID string `bson:"id" json:"id"`
}
<commit_msg>go/model: change state to status<commit_after>package models

import (
	"gopkg.in/mgo.v2/bson"
)

type Group struct {
	Id                             bson.ObjectId            `bson:"_id" json:"-"`
	Body                           string                   `bson:"body" json:"body"`
	Title                          string                   `bson:"title" json:"title"`
	Slug                           string                   `bson:"slug" json:"slug"`
	Privacy                        string                   `bson:"privacy" json:"privacy"`
	Visibility                     string                   `bson:"visibility" json:"visibility"`
	SocialApiChannelId             string                   `bson:"socialApiChannelId" json:"socialApiChannelId"`
	SocialApiAnnouncementChannelId string                   `bson:"socialApiAnnouncementChannelId" json:"socialApiAnnouncementChannelId"`
	SocialApiDefaultChannelId      string                   `bson:"socialApiDefaultChannelId" json:"socialApiDefaultChannelId"`
	Parent                         []map[string]interface{} `bson:"parent" json:"parent"`
	Customize                      map[string]interface{}   `bson:"customize" json:"customize"`
	Counts                         map[string]interface{}   `bson:"counts" json:"counts"`
	Migration                      string                   `bson:"migration,omitempty" json:"migration"`
	StackTemplate                  []string                 `bson:"stackTemplates,omitempty" json:"stackTemplates"`
	// DefaultChannels holds the default channels for a group, when a user joins
	// to this group, participants will be automatically added to regarding
	// channels
	DefaultChannels []string `bson:"defaultChannels,omitempty" json:"defaultChannels"`
	Payment         Payment  `bson:"payment" json:"payment"`
}

type Payment struct {
	Subscription Subscription
	Customer     Customer
}

type Subscription struct {
	// Allowed values are "trialing", "active", "past_due", "canceled", "unpaid".
	Status string `bson:"status" json:"status"`
	ID     string `bson:"id" json:"id"`
}

type Customer struct {
	ID string `bson:"id" json:"id"`
}
<|endoftext|>