<commit_before>package archive

import (
	"fmt"
	"io"
	"path"
	"time"

	"crypto"
	"crypto/sha512"

	"golang.org/x/crypto/openpgp"
	"golang.org/x/crypto/openpgp/clearsign"
	"golang.org/x/crypto/openpgp/packet"

	"pault.ag/go/blobstore"
	"pault.ag/go/debian/control"
	"pault.ag/go/debian/dependency"
	"pault.ag/go/debian/transput"
)

// Archive {{{

// Core Archive abstrcation. This contains helpers to write out package files,
// as well as handles creating underlying abstractions (such as Suites).
type Archive struct {
	store      blobstore.Store
	signingKey *openpgp.Entity
}

// Create a new Archive at the given `root` on the filesystem, with the
// openpgp.Entity `signer` (an Entity which contains an OpenPGP Private
// Key).
//
// This interface is intended to *write* Archives, not *read* them. Extra
// steps must be taken to load an Archive over the network, and attention
// must be paid when handling the Cryptographic chain of trust.
func New(path string, signer *openpgp.Entity) (*Archive, error) {
	store, err := blobstore.Load(path)
	if err != nil {
		return nil, err
	}

	return &Archive{
		store:      *store,
		signingKey: signer,
	}, nil
}

// Use the default backend to remove any unlinked files from the Blob store.
//
// If files you care about are not linked onto the stage, they will be removed
// by the garbage collector. Decruft only when you're sure the stage has been
// set.
func (a Archive) Decruft() error {
	return a.store.GC(blobstore.DumbGarbageCollector{})
}

// Given a list of objects, link them to the keyed paths.
func (a Archive) Link(blobs StagedChanges) error {
	for path, obj := range blobs {
		if err := a.store.Link(obj, path); err != nil {
			return err
		}
	}
	return nil
}

// Create a new Release object from a Suite, passing off the Name, Description
// and constructing the rest of the goodies.
//
// This will be an entirely empty object, without anything read off disk.
func newRelease(suite Suite) (*Release, error) {
	when := time.Now()

	var validUntil string = ""
	if suite.features.Duration != "" {
		duration, err := time.ParseDuration(suite.features.Duration)
		if err != nil {
			return nil, err
		}
		validUntil = when.Add(duration).Format(time.RFC1123Z)
	}

	release := Release{
		Suite:       suite.Name,
		Description: suite.Description,
		ValidUntil:  validUntil,
		Origin:      suite.Origin,
		Label:       suite.Label,
		Version:     suite.Version,
	}
	release.Date = when.Format(time.RFC1123Z)
	release.Architectures = []dependency.Arch{}
	release.Components = []string{}
	release.SHA256 = []control.SHA256FileHash{}
	release.SHA1 = []control.SHA1FileHash{}
	release.SHA512 = []control.SHA512FileHash{}
	release.MD5Sum = []control.MD5FileHash{}
	return &release, nil
}

// This is a set of file changes ready to be passed to `Link` to link in.
// Basically, this maps file paths to blobstore objects, which will be
// swapped in all at once. This allows errors to avoid mutating state in
// the archive.
type StagedChanges map[string]blobstore.Object

// Engross a Suite for signing and final commit into the blobstore. This
// will return handle(s) to the signed and ready Objects, fit for passage
// to Link.
//
// This will contain all the related Packages and Release files.
func (a Archive) Engross(suite Suite) (StagedChanges, error) {
	release, err := newRelease(suite)
	if err != nil {
		return nil, err
	}

	files := StagedChanges{}
	arches := map[dependency.Arch]bool{}

	for name, component := range suite.components {
		release.Components = append(release.Components, name)
		for arch, writer := range component.packageWriters {
			arches[arch] = true

			// For each Binary entry, do the same as above (todo: someone
			// DRY this out a bit. I'm too lazy.

			suitePath := path.Join(name, fmt.Sprintf("binary-%s", arch),
				"Packages")

			obj, err := a.store.Commit(*writer.handle)
			if err != nil {
				return nil, err
			}

			for _, hasher := range writer.hashers {
				fileHash := control.FileHashFromHasher(suitePath, *hasher)
				release.AddHash(fileHash)
			}

			filePath := path.Join("dists", suite.Name, suitePath)
			files[filePath] = *obj
		}
	}

	for arch, _ := range arches {
		release.Architectures = append(release.Architectures, arch)
	}

	/* Now, let's do some magic */

	// Now, let's write out the Release file (and sign it normally)
	obj, sig, err := suite.archive.encodeSigned(release)
	if err != nil {
		return nil, err
	}

	filePath := path.Join("dists", suite.Name, "Release")
	files[filePath] = *obj
	files[fmt.Sprintf("%s.gpg", filePath)] = *sig

	// Ditto with the clearsigned version (Should we merge the two above?)
	obj, err = suite.archive.encodeClearsigned(release)
	if err != nil {
		return nil, err
	}

	files[path.Join("dists", suite.Name, "InRelease")] = *obj

	return files, nil
}

// Given a control.Marshal'able object, encode it to the blobstore, while
// also clearsigning the data.
func (a Archive) encodeClearsigned(data interface{}) (*blobstore.Object, error) {

	if a.signingKey == nil {
		return nil, fmt.Errorf("No signing key loaded")
	}

	fd, err := a.store.Create()
	if err != nil {
		return nil, err
	}

	defer fd.Close()
	wc, err := clearsign.Encode(fd, a.signingKey.PrivateKey, nil)
	if err != nil {
		return nil, err
	}

	encoder, err := control.NewEncoder(wc)
	if err != nil {
		return nil, err
	}

	if err := encoder.Encode(data); err != nil {
		return nil, err
	}

	if err := wc.Close(); err != nil {
		return nil, err
	}

	return a.store.Commit(*fd)
}

// Given a control.Marshal'able object, encode it to the blobstore, while
// also doing a detached OpenPGP signature. The objects returned (in order)
// are data, commited to the blobstore, the signature for that object, commited
// to the blobstore, and any error(s), finally.
func (a Archive) encodeSigned(data interface{}) (*blobstore.Object, *blobstore.Object, error) {
	/* Right, so, the trick here is that we secretly call out to encode,
	 * but tap it with a pipe into the signing code */

	if a.signingKey == nil {
		return nil, nil, fmt.Errorf("No signing key loaded")
	}

	signature, err := a.store.Create()
	if err != nil {
		return nil, nil, err
	}
	defer signature.Close()

	hash := sha512.New()

	obj, err := a.encode(data, hash)
	if err != nil {
		return nil, nil, err
	}

	sig := new(packet.Signature)
	sig.SigType = packet.SigTypeBinary
	sig.PubKeyAlgo = a.signingKey.PrivateKey.PubKeyAlgo

	sig.Hash = crypto.SHA512

	sig.CreationTime = new(packet.Config).Now()
	sig.IssuerKeyId = &(a.signingKey.PrivateKey.KeyId)

	err = sig.Sign(hash, a.signingKey.PrivateKey, nil)
	if err != nil {
		return nil, nil, err
	}

	if err := sig.Serialize(signature); err != nil {
		return nil, nil, err
	}

	sigObj, err := a.store.Commit(*signature)
	if err != nil {
		return nil, nil, err
	}

	return obj, sigObj, nil

}

// Encode a given control.Marshal'able object into the Blobstore, and return
// a handle to its object.
//
// The optinal argument `tap` will be written to as the object gets sent into
// the blobstore. This may be useful if you wish to have a copy of the data
// going into the store.
func (a Archive) encode(data interface{}, tap io.Writer) (*blobstore.Object, error) {
	fd, err := a.store.Create()
	if err != nil {
		return nil, err
	}

	var writer io.Writer = fd
	if tap != nil {
		writer = io.MultiWriter(fd, tap)
	}

	encoder, err := control.NewEncoder(writer)
	if err != nil {
		return nil, err
	}

	if err := encoder.Encode(data); err != nil {
		return nil, err
	}

	return a.store.Commit(*fd)
}

// }}}

// Suite {{{

// Abstraction to handle writing data into a Suite. This is a write-only
// target, and is not intended to read a Release file.
//
// This contains no state read off disk, and is purely for writing to.
type Suite struct {
	control.Paragraph

	archive *Archive

	Name        string `control:"Suite"`
	Description string
	Origin      string
	Label       string
	Version     string

	Pool       Pool                  `control:"-"`
	components map[string]*Component `control:"-"`

	features struct {
		Hashes   []string
		Duration string
	} `control:"-"`
}

// Get a handle to write a given Suite from an Archive.
// The suite will be entirely blank, and attributes will not be
// read from the existing files, if any.
func (a Archive) Suite(name string) (*Suite, error) {
	suite := Suite{
		Name:       name,
		archive:    &a,
		components: map[string]*Component{},
	}

	suite.Pool = Pool{store: a.store, suite: &suite}
	suite.features.Hashes = []string{"sha256", "sha1", "sha512"}
	suite.features.Duration = "240h"

	return &suite, nil
}

// Get or create a Component for a given Suite. If no such Component
// has been created so far, this will create a new object, otherwise
// it will return the existing entry.
//
// This contains no state read off disk, and is purely for writing to.
func (s Suite) Component(name string) (*Component, error) {
	if _, ok := s.components[name]; !ok {
		comp, err := newComponent(&s)
		if err != nil {
			return nil, err
		}
		s.components[name] = comp
		return comp, nil
	}
	el := s.components[name]
	return el, nil
}

// }}}

// Component {{{

// Small wrapper to represent a Component of a Suite, which, at its core
// is simply a set of Indexes to be written to.
//
// This contains no state read off disk, and is purely for writing to.
type Component struct {
	suite          *Suite
	packageWriters map[dependency.Arch]*IndexWriter
}

// Create a new Component, configured for use.
func newComponent(suite *Suite) (*Component, error) {
	return &Component{
		suite:          suite,
		packageWriters: map[dependency.Arch]*IndexWriter{},
	}, nil
}

// Get a given IndexWriter for an arch, or create one if none exists.
func (c *Component) getWriter(arch dependency.Arch) (*IndexWriter, error) {
	if _, ok := c.packageWriters[arch]; !ok {
		writer, err := newIndexWriter(c.suite)
		if err != nil {
			return nil, err
		}
		c.packageWriters[arch] = writer
	}
	return c.packageWriters[arch], nil
}

// Add a given Package to a Package List. Under the hood, this will
// get or create a IndexWriter, and invoke the .Add method on the
// Package Writer.
func (c *Component) AddPackage(pkg Package) error {
	writer, err := c.getWriter(pkg.Architecture)
	if err != nil {
		return err
	}
	return writer.Add(pkg)
}

// }}}

// IndexWriter {{{

// This writer represents a Package list - which is to say, a list of
// binary .deb files, for a particular Architecture, in a particular Component
// in a particular Suite, in a particular Archive.
//
// This is not an encapsulation to store the entire Index in memory, rather,
// it's a wrapper to help write Package entries into the Index.
type IndexWriter struct {
	archive *Archive

	handle  *blobstore.Writer
	closer  func() error
	encoder *control.Encoder

	hashers []*transput.Hasher
}

func getHashers(suite *Suite) (io.Writer, []*transput.Hasher, error) {
	ret := []*transput.Hasher{}
	writers := []io.Writer{}

	for _, algo := range suite.features.Hashes {
		hasher, err := transput.NewHasher(algo)
		if err != nil {
			return nil, nil, err
		}
		writers = append(writers, hasher)
		ret = append(ret, hasher)
	}

	return io.MultiWriter(writers...), ret, nil
}

// given a Suite, create a new Package Writer, configured with
// the appropriate Hashing, and targeting a new file blob in the
// underlying blobstore.
func newIndexWriter(suite *Suite) (*IndexWriter, error) {
	handle, err := suite.archive.store.Create()
	if err != nil {
		return nil, err
	}

	writer, hashers, err := getHashers(suite)
	if err != nil {
		return nil, err
	}

	encoder, err := control.NewEncoder(io.MultiWriter(writer, handle))
	if err != nil {
		handle.Close()
		return nil, err
	}

	return &IndexWriter{
		archive: suite.archive,
		closer:  handle.Close,
		encoder: encoder,
		handle:  handle,
		hashers: hashers,
	}, nil
}

// Write a Package entry into the Packages index.
func (p IndexWriter) Add(data interface{}) error {
	return p.encoder.Encode(data)
}

// }}}

// vim: foldmethod=marker
<commit_msg>Rename to ArchiveState to be more clear<commit_after>package archive

import (
	"fmt"
	"io"
	"path"
	"time"

	"crypto"
	"crypto/sha512"

	"golang.org/x/crypto/openpgp"
	"golang.org/x/crypto/openpgp/clearsign"
	"golang.org/x/crypto/openpgp/packet"

	"pault.ag/go/blobstore"
	"pault.ag/go/debian/control"
	"pault.ag/go/debian/dependency"
	"pault.ag/go/debian/transput"
)

// Archive {{{

// Core Archive abstrcation. This contains helpers to write out package files,
// as well as handles creating underlying abstractions (such as Suites).
type Archive struct {
	store      blobstore.Store
	signingKey *openpgp.Entity
}

// Create a new Archive at the given `root` on the filesystem, with the
// openpgp.Entity `signer` (an Entity which contains an OpenPGP Private
// Key).
//
// This interface is intended to *write* Archives, not *read* them. Extra
// steps must be taken to load an Archive over the network, and attention
// must be paid when handling the Cryptographic chain of trust.
func New(path string, signer *openpgp.Entity) (*Archive, error) {
	store, err := blobstore.Load(path)
	if err != nil {
		return nil, err
	}

	return &Archive{
		store:      *store,
		signingKey: signer,
	}, nil
}

// Use the default backend to remove any unlinked files from the Blob store.
//
// If files you care about are not linked onto the stage, they will be removed
// by the garbage collector. Decruft only when you're sure the stage has been
// set.
func (a Archive) Decruft() error {
	return a.store.GC(blobstore.DumbGarbageCollector{})
}

// Get the current state of files in the Archive, which is an underlying
// call to the blobstore Paths function.
func (a Archive) State() (ArchiveState, error) {
	return a.store.Paths()
}

// Given a list of objects, link them to the keyed paths.
func (a Archive) Link(blobs ArchiveState) error {
	for path, obj := range blobs {
		if err := a.store.Link(obj, path); err != nil {
			return err
		}
	}
	return nil
}

// Create a new Release object from a Suite, passing off the Name, Description
// and constructing the rest of the goodies.
//
// This will be an entirely empty object, without anything read off disk.
func newRelease(suite Suite) (*Release, error) {
	when := time.Now()

	var validUntil string = ""
	if suite.features.Duration != "" {
		duration, err := time.ParseDuration(suite.features.Duration)
		if err != nil {
			return nil, err
		}
		validUntil = when.Add(duration).Format(time.RFC1123Z)
	}

	release := Release{
		Suite:       suite.Name,
		Description: suite.Description,
		ValidUntil:  validUntil,
		Origin:      suite.Origin,
		Label:       suite.Label,
		Version:     suite.Version,
	}
	release.Date = when.Format(time.RFC1123Z)
	release.Architectures = []dependency.Arch{}
	release.Components = []string{}
	release.SHA256 = []control.SHA256FileHash{}
	release.SHA1 = []control.SHA1FileHash{}
	release.SHA512 = []control.SHA512FileHash{}
	release.MD5Sum = []control.MD5FileHash{}
	return &release, nil
}

// This is a set of file changes ready to be passed to `Link` to link in.
// Basically, this maps file paths to blobstore objects, which will be
// swapped in all at once. This allows errors to avoid mutating state in
// the archive.
type ArchiveState map[string]blobstore.Object

// Engross a Suite for signing and final commit into the blobstore. This
// will return handle(s) to the signed and ready Objects, fit for passage
// to Link.
//
// This will contain all the related Packages and Release files.
func (a Archive) Engross(suite Suite) (ArchiveState, error) {
	release, err := newRelease(suite)
	if err != nil {
		return nil, err
	}

	files := ArchiveState{}
	arches := map[dependency.Arch]bool{}

	for name, component := range suite.components {
		release.Components = append(release.Components, name)
		for arch, writer := range component.packageWriters {
			arches[arch] = true

			// For each Binary entry, do the same as above (todo: someone
			// DRY this out a bit. I'm too lazy.

			suitePath := path.Join(name, fmt.Sprintf("binary-%s", arch),
				"Packages")

			obj, err := a.store.Commit(*writer.handle)
			if err != nil {
				return nil, err
			}

			for _, hasher := range writer.hashers {
				fileHash := control.FileHashFromHasher(suitePath, *hasher)
				release.AddHash(fileHash)
			}

			filePath := path.Join("dists", suite.Name, suitePath)
			files[filePath] = *obj
		}
	}

	for arch, _ := range arches {
		release.Architectures = append(release.Architectures, arch)
	}

	/* Now, let's do some magic */

	// Now, let's write out the Release file (and sign it normally)
	obj, sig, err := suite.archive.encodeSigned(release)
	if err != nil {
		return nil, err
	}

	filePath := path.Join("dists", suite.Name, "Release")
	files[filePath] = *obj
	files[fmt.Sprintf("%s.gpg", filePath)] = *sig

	// Ditto with the clearsigned version (Should we merge the two above?)
	obj, err = suite.archive.encodeClearsigned(release)
	if err != nil {
		return nil, err
	}

	files[path.Join("dists", suite.Name, "InRelease")] = *obj

	return files, nil
}

// Given a control.Marshal'able object, encode it to the blobstore, while
// also clearsigning the data.
func (a Archive) encodeClearsigned(data interface{}) (*blobstore.Object, error) {

	if a.signingKey == nil {
		return nil, fmt.Errorf("No signing key loaded")
	}

	fd, err := a.store.Create()
	if err != nil {
		return nil, err
	}

	defer fd.Close()
	wc, err := clearsign.Encode(fd, a.signingKey.PrivateKey, nil)
	if err != nil {
		return nil, err
	}

	encoder, err := control.NewEncoder(wc)
	if err != nil {
		return nil, err
	}

	if err := encoder.Encode(data); err != nil {
		return nil, err
	}

	if err := wc.Close(); err != nil {
		return nil, err
	}

	return a.store.Commit(*fd)
}

// Given a control.Marshal'able object, encode it to the blobstore, while
// also doing a detached OpenPGP signature. The objects returned (in order)
// are data, commited to the blobstore, the signature for that object, commited
// to the blobstore, and any error(s), finally.
func (a Archive) encodeSigned(data interface{}) (*blobstore.Object, *blobstore.Object, error) {
	/* Right, so, the trick here is that we secretly call out to encode,
	 * but tap it with a pipe into the signing code */

	if a.signingKey == nil {
		return nil, nil, fmt.Errorf("No signing key loaded")
	}

	signature, err := a.store.Create()
	if err != nil {
		return nil, nil, err
	}
	defer signature.Close()

	hash := sha512.New()

	obj, err := a.encode(data, hash)
	if err != nil {
		return nil, nil, err
	}

	sig := new(packet.Signature)
	sig.SigType = packet.SigTypeBinary
	sig.PubKeyAlgo = a.signingKey.PrivateKey.PubKeyAlgo

	sig.Hash = crypto.SHA512

	sig.CreationTime = new(packet.Config).Now()
	sig.IssuerKeyId = &(a.signingKey.PrivateKey.KeyId)

	err = sig.Sign(hash, a.signingKey.PrivateKey, nil)
	if err != nil {
		return nil, nil, err
	}

	if err := sig.Serialize(signature); err != nil {
		return nil, nil, err
	}

	sigObj, err := a.store.Commit(*signature)
	if err != nil {
		return nil, nil, err
	}

	return obj, sigObj, nil

}

// Encode a given control.Marshal'able object into the Blobstore, and return
// a handle to its object.
//
// The optinal argument `tap` will be written to as the object gets sent into
// the blobstore. This may be useful if you wish to have a copy of the data
// going into the store.
func (a Archive) encode(data interface{}, tap io.Writer) (*blobstore.Object, error) {
	fd, err := a.store.Create()
	if err != nil {
		return nil, err
	}

	var writer io.Writer = fd
	if tap != nil {
		writer = io.MultiWriter(fd, tap)
	}

	encoder, err := control.NewEncoder(writer)
	if err != nil {
		return nil, err
	}

	if err := encoder.Encode(data); err != nil {
		return nil, err
	}

	return a.store.Commit(*fd)
}

// }}}

// Suite {{{

// Abstraction to handle writing data into a Suite. This is a write-only
// target, and is not intended to read a Release file.
//
// This contains no state read off disk, and is purely for writing to.
type Suite struct {
	control.Paragraph

	archive *Archive

	Name        string `control:"Suite"`
	Description string
	Origin      string
	Label       string
	Version     string

	Pool       Pool                  `control:"-"`
	components map[string]*Component `control:"-"`

	features struct {
		Hashes   []string
		Duration string
	} `control:"-"`
}

// Get a handle to write a given Suite from an Archive.
// The suite will be entirely blank, and attributes will not be
// read from the existing files, if any.
func (a Archive) Suite(name string) (*Suite, error) {
	suite := Suite{
		Name:       name,
		archive:    &a,
		components: map[string]*Component{},
	}

	suite.Pool = Pool{store: a.store, suite: &suite}
	suite.features.Hashes = []string{"sha256", "sha1", "sha512"}
	suite.features.Duration = "240h"

	return &suite, nil
}

// Get or create a Component for a given Suite. If no such Component
// has been created so far, this will create a new object, otherwise
// it will return the existing entry.
//
// This contains no state read off disk, and is purely for writing to.
func (s Suite) Component(name string) (*Component, error) {
	if _, ok := s.components[name]; !ok {
		comp, err := newComponent(&s)
		if err != nil {
			return nil, err
		}
		s.components[name] = comp
		return comp, nil
	}
	el := s.components[name]
	return el, nil
}

// }}}

// Component {{{

// Small wrapper to represent a Component of a Suite, which, at its core
// is simply a set of Indexes to be written to.
//
// This contains no state read off disk, and is purely for writing to.
type Component struct {
	suite          *Suite
	packageWriters map[dependency.Arch]*IndexWriter
}

// Create a new Component, configured for use.
func newComponent(suite *Suite) (*Component, error) {
	return &Component{
		suite:          suite,
		packageWriters: map[dependency.Arch]*IndexWriter{},
	}, nil
}

// Get a given IndexWriter for an arch, or create one if none exists.
func (c *Component) getWriter(arch dependency.Arch) (*IndexWriter, error) {
	if _, ok := c.packageWriters[arch]; !ok {
		writer, err := newIndexWriter(c.suite)
		if err != nil {
			return nil, err
		}
		c.packageWriters[arch] = writer
	}
	return c.packageWriters[arch], nil
}

// Add a given Package to a Package List. Under the hood, this will
// get or create a IndexWriter, and invoke the .Add method on the
// Package Writer.
func (c *Component) AddPackage(pkg Package) error {
	writer, err := c.getWriter(pkg.Architecture)
	if err != nil {
		return err
	}
	return writer.Add(pkg)
}

// }}}

// IndexWriter {{{

// This writer represents a Package list - which is to say, a list of
// binary .deb files, for a particular Architecture, in a particular Component
// in a particular Suite, in a particular Archive.
//
// This is not an encapsulation to store the entire Index in memory, rather,
// it's a wrapper to help write Package entries into the Index.
type IndexWriter struct {
	archive *Archive

	handle  *blobstore.Writer
	closer  func() error
	encoder *control.Encoder

	hashers []*transput.Hasher
}

func getHashers(suite *Suite) (io.Writer, []*transput.Hasher, error) {
	ret := []*transput.Hasher{}
	writers := []io.Writer{}

	for _, algo := range suite.features.Hashes {
		hasher, err := transput.NewHasher(algo)
		if err != nil {
			return nil, nil, err
		}
		writers = append(writers, hasher)
		ret = append(ret, hasher)
	}

	return io.MultiWriter(writers...), ret, nil
}

// given a Suite, create a new Package Writer, configured with
// the appropriate Hashing, and targeting a new file blob in the
// underlying blobstore.
func newIndexWriter(suite *Suite) (*IndexWriter, error) {
	handle, err := suite.archive.store.Create()
	if err != nil {
		return nil, err
	}

	writer, hashers, err := getHashers(suite)
	if err != nil {
		return nil, err
	}

	encoder, err := control.NewEncoder(io.MultiWriter(writer, handle))
	if err != nil {
		handle.Close()
		return nil, err
	}

	return &IndexWriter{
		archive: suite.archive,
		closer:  handle.Close,
		encoder: encoder,
		handle:  handle,
		hashers: hashers,
	}, nil
}

// Write a Package entry into the Packages index.
func (p IndexWriter) Add(data interface{}) error {
	return p.encoder.Encode(data)
}

// }}}

// vim: foldmethod=marker
<|endoftext|>


<commit_before>// Copyright 2017 The solid-mon-rpi 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 sensors exposes sensor data.
package sensors

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"math"
	"time"

	"github.com/go-daq/smbus"
	"github.com/go-daq/smbus/sensor/adc101x"
	"github.com/go-daq/smbus/sensor/at30tse75x"
	"github.com/go-daq/smbus/sensor/bme280"
	"github.com/go-daq/smbus/sensor/hts221"
	"github.com/go-daq/smbus/sensor/tsl2591"
	"gonum.org/v1/plot/plotter"
)

type Sensors struct {
	Timestamp time.Time         `json:"timestamp"`
	Sensors   []Data            `json:"sensors"`
	Labels    map[string][]Type `json:"labels"`
}

type Data struct {
	Name  string  `json:"name"`
	Type  Type    `json:"type"`
	Value float64 `json:"value"`
}

// Type describes the type of data sensor (H,P,T,L,V)
type Type uint8

const (
	InvalidType Type = iota
	Humidity
	Pressure
	Temperature
	Luminosity
	Voltage
)

func (t Type) String() string {
	switch t {
	case InvalidType:
		return "invalid"
	case Humidity:
		return "humidity"
	case Pressure:
		return "pressure"
	case Temperature:
		return "temperature"
	case Luminosity:
		return "luminosity"
	case Voltage:
		return "voltage"
	}
	panic(fmt.Errorf("unknown sensor type %d", t))
}

func (t Type) MarshalJSON() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := json.NewEncoder(buf).Encode(t.String())
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// mux maps an I2C channel id to an action register
var mux = [...]uint8{
	0: 0x01,
	1: 0x02,
	2: 0x04,
	3: 0x08,
	4: 0x10,
	5: 0x20,
	6: 0x40,
	7: 0x80,
}

func New(bus *smbus.Conn, addr uint8, descr []Descr) (Sensors, error) {
	data := Sensors{
		Timestamp: time.Now().UTC(),
		Labels:    make(map[string][]Type, len(descr)),
	}
	for _, d := range descr {
		switch d := d.(type) {
		case *DescrADC101x:
			device := ADC101x{}
			if d.Base.I2CAddr == 0 {
				d.Base.I2CAddr = adc101x.DefaultI2CAddr
			}
			err := device.read(bus, d.Base.I2CAddr, addr, mux[d.Base.ChanID], 1024, 3.3)
			if err != nil {
				return data, err
			}
			data.Sensors = append(data.Sensors, Data{
				Name:  d.Descr().Name,
				Type:  Voltage,
				Value: device.Voltage,
			})
			data.Labels[d.Descr().Name] = []Type{Voltage}

		case *DescrAT30TSE:
			device := At30tse75x{}
			if d.I2CAddr == 0 {
				d.I2CAddr = at30tse75x.DefaultI2CAddr
			}
			err := device.read(bus, d.I2CAddr, addr, mux[d.ChanID])
			if err != nil {
				return data, err
			}
			data.Sensors = append(data.Sensors, Data{
				Name:  d.Name,
				Type:  Temperature,
				Value: device.Temp,
			})
			data.Labels[d.Name] = []Type{Temperature}

		case *DescrHTS221:
			device := Hts221{}
			err := device.read(bus, addr, mux[d.ChanID])
			if err != nil {
				return data, err
			}
			data.Sensors = append(data.Sensors, Data{
				Name:  d.Name,
				Type:  Humidity,
				Value: device.Humi,
			})
			data.Sensors = append(data.Sensors, Data{
				Name:  d.Name,
				Type:  Temperature,
				Value: device.Temp,
			})
			data.Labels[d.Name] = []Type{Humidity, Temperature}

		case *DescrBME280:
			device := Bme280{}
			err := device.read(bus, d.I2CAddr, addr, mux[d.ChanID])
			if err != nil {
				return data, err
			}
			data.Sensors = append(data.Sensors, Data{
				Name:  d.Name,
				Type:  Pressure,
				Value: device.Pres,
			})
			data.Labels[d.Name] = append(data.Labels[d.Name], Pressure)

		case *DescrOnBoard:
			{
				device := Bme280{}
				err := device.read(bus, d.I2CAddr, addr, mux[d.ChanID])
				if err != nil {
					return data, err
				}
				data.Sensors = append(data.Sensors, Data{
					Name:  d.Name,
					Type:  Pressure,
					Value: device.Pres,
				})
				data.Labels[d.Name] = append(data.Labels[d.Name], Pressure)
			}
			{
				device := Tsl2591{}
				err := device.read(bus, addr, mux[d.ChanID])
				if err != nil {
					return data, err
				}
				data.Sensors = append(data.Sensors, Data{
					Name:  d.Name,
					Type:  Luminosity,
					Value: device.Lux,
				})
				data.Labels[d.Name] = append(data.Labels[d.Name], Luminosity)
			}
		}
	}
	return data, nil
}

type Tsl2591 struct {
	Lux  float64 `json:"lux"`
	Full uint16  `json:"full"`
	IR   uint16  `json:"ir"`
}

func (tsl *Tsl2591) read(bus *smbus.Conn, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		log.Printf("tsl-write-reg error: %v", err)
		return err
	}

	dev, err := tsl2591.Open(bus, tsl2591.Addr, tsl2591.IntegTime100ms, tsl2591.GainLow)
	if err != nil {
		log.Printf("tsl-open-bus error: %v", err)
		return err
	}

	full, ir, err := dev.FullLuminosity()
	if err != nil {
		log.Printf("tsl-sample error: %v", err)
		return err
	}

	tsl.Lux = dev.Lux(full, ir)
	tsl.Full = full
	tsl.IR = ir

	return err
}

type Bme280 struct {
	Temp float64 `json:"temp"`
	Hum  float64 `json:"humi"`
	Pres float64 `json:"pres"`
}

func (bme *Bme280) read(bus *smbus.Conn, i2c, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		log.Printf("write-reg error: %v", err)
		return err
	}

	if i2c == 0 {
		i2c = bme280.I2CAddr
	}
	dev, err := bme280.Open(bus, i2c, bme280.OpSample8)
	if err != nil {
		log.Printf("open-bus error (i2c-addr=0x%x): %v", i2c, err)
		return err
	}

	h, p, t, err := dev.Sample()
	if err != nil {
		log.Printf("sample error: %v", err)
		return err
	}

	const HPa = 1.0 / 100.0
	bme.Hum = h
	bme.Pres = p * HPa
	bme.Temp = t

	return err
}

type ADC101x struct {
	Count   int     `json:"adc"`
	Voltage float64 `json:"voltage"`
}

func (adc *ADC101x) read(bus *smbus.Conn, i2c, daddr uint8, ch uint8, frange int, vdd float64) error {
	err := bus.WriteReg(daddr, 0x04, ch)
	if err != nil {
		log.Printf("adc101x-write-reg error: %v", err)
		return err
	}

	dev, err := adc101x.Open(bus, ch, frange, vdd)
	if err != nil {
		log.Printf("adc101x-open-bus error: %v", err)
		return err
	}

	count, err := dev.ADC()
	if err != nil {
		return err
	}

	voltage, err := dev.Voltage()
	if err != nil {
		return err
	}

	adc.Count = count
	adc.Voltage = voltage
	return nil
}

type At30tse75x struct {
	Temp float64 `json:"temp"`
}

func (at30 *At30tse75x) read(bus *smbus.Conn, i2c, daddr uint8, ch uint8) error {
	err := bus.WriteReg(daddr, 0x04, ch)
	if err != nil {
		log.Printf("at30tse-write-reg error: %v", err)
		return err
	}

	dev, err := at30tse75x.Open(
		bus,
		at30tse75x.I2CAddr(i2c),
		at30tse75x.DevAddr(daddr),
		at30tse75x.EEPROM(4),
	)
	if err != nil {
		log.Printf("at30tse-open-bus error: %v", err)
		return err
	}

	t, err := dev.T()
	if err != nil {
		log.Printf("at30tse-sample error: %v", err)
		return err
	}
	at30.Temp = t
	return nil
}

type Hts221 struct {
	Temp float64 `json:"temp"`
	Humi float64 `json:"humi"`
}

func (hts *Hts221) read(bus *smbus.Conn, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		log.Printf("hts221-write-reg error: %v", err)
		return err
	}

	dev, err := hts221.Open(bus, hts221.SlaveAddr)
	if err != nil {
		log.Printf("hts221-open-bus error: %v", err)
		return err
	}

	h, t, err := dev.Sample()
	if err != nil {
		log.Printf("hts221-sample error: %v", err)
		return err
	}
	hts.Temp = t
	hts.Humi = h
	return nil
}

type Table []Sensors

func (tbl Table) Data(typ Type, label string) (float64, float64, plotter.XYs) {
	min := +math.MaxFloat64
	max := -math.MaxFloat64
	data := make(plotter.XYs, len(tbl))
	for i, v := range tbl {
		for _, sensor := range v.Sensors {
			if sensor.Type != typ || sensor.Name != label {
				continue
			}
			data[i].X = float64(v.Timestamp.UTC().Unix())
			data[i].Y = sensor.Value
			min = math.Min(min, sensor.Value)
			max = math.Max(max, sensor.Value)
		}
	}
	return min, max, data
}

func (tbl Table) Labels(typ Type) []string {
	var labels []string
	for k, v := range tbl[0].Labels {
		for _, t := range v {
			if typ == t {
				labels = append(labels, k)
			}
		}
	}
	return labels
}
<commit_msg>sensors: fix i2c-addr typo for adc101x<commit_after>// Copyright 2017 The solid-mon-rpi 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 sensors exposes sensor data.
package sensors

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"math"
	"time"

	"github.com/go-daq/smbus"
	"github.com/go-daq/smbus/sensor/adc101x"
	"github.com/go-daq/smbus/sensor/at30tse75x"
	"github.com/go-daq/smbus/sensor/bme280"
	"github.com/go-daq/smbus/sensor/hts221"
	"github.com/go-daq/smbus/sensor/tsl2591"
	"gonum.org/v1/plot/plotter"
)

type Sensors struct {
	Timestamp time.Time         `json:"timestamp"`
	Sensors   []Data            `json:"sensors"`
	Labels    map[string][]Type `json:"labels"`
}

type Data struct {
	Name  string  `json:"name"`
	Type  Type    `json:"type"`
	Value float64 `json:"value"`
}

// Type describes the type of data sensor (H,P,T,L,V)
type Type uint8

const (
	InvalidType Type = iota
	Humidity
	Pressure
	Temperature
	Luminosity
	Voltage
)

func (t Type) String() string {
	switch t {
	case InvalidType:
		return "invalid"
	case Humidity:
		return "humidity"
	case Pressure:
		return "pressure"
	case Temperature:
		return "temperature"
	case Luminosity:
		return "luminosity"
	case Voltage:
		return "voltage"
	}
	panic(fmt.Errorf("unknown sensor type %d", t))
}

func (t Type) MarshalJSON() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := json.NewEncoder(buf).Encode(t.String())
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// mux maps an I2C channel id to an action register
var mux = [...]uint8{
	0: 0x01,
	1: 0x02,
	2: 0x04,
	3: 0x08,
	4: 0x10,
	5: 0x20,
	6: 0x40,
	7: 0x80,
}

func New(bus *smbus.Conn, addr uint8, descr []Descr) (Sensors, error) {
	data := Sensors{
		Timestamp: time.Now().UTC(),
		Labels:    make(map[string][]Type, len(descr)),
	}
	for _, d := range descr {
		switch d := d.(type) {
		case *DescrADC101x:
			device := ADC101x{}
			if d.Base.I2CAddr == 0 {
				d.Base.I2CAddr = adc101x.DefaultI2CAddr
			}
			err := device.read(bus, d.Base.I2CAddr, addr, mux[d.Base.ChanID], 1024, 3.3)
			if err != nil {
				return data, err
			}
			data.Sensors = append(data.Sensors, Data{
				Name:  d.Descr().Name,
				Type:  Voltage,
				Value: device.Voltage,
			})
			data.Labels[d.Descr().Name] = []Type{Voltage}

		case *DescrAT30TSE:
			device := At30tse75x{}
			if d.I2CAddr == 0 {
				d.I2CAddr = at30tse75x.DefaultI2CAddr
			}
			err := device.read(bus, d.I2CAddr, addr, mux[d.ChanID])
			if err != nil {
				return data, err
			}
			data.Sensors = append(data.Sensors, Data{
				Name:  d.Name,
				Type:  Temperature,
				Value: device.Temp,
			})
			data.Labels[d.Name] = []Type{Temperature}

		case *DescrHTS221:
			device := Hts221{}
			err := device.read(bus, addr, mux[d.ChanID])
			if err != nil {
				return data, err
			}
			data.Sensors = append(data.Sensors, Data{
				Name:  d.Name,
				Type:  Humidity,
				Value: device.Humi,
			})
			data.Sensors = append(data.Sensors, Data{
				Name:  d.Name,
				Type:  Temperature,
				Value: device.Temp,
			})
			data.Labels[d.Name] = []Type{Humidity, Temperature}

		case *DescrBME280:
			device := Bme280{}
			err := device.read(bus, d.I2CAddr, addr, mux[d.ChanID])
			if err != nil {
				return data, err
			}
			data.Sensors = append(data.Sensors, Data{
				Name:  d.Name,
				Type:  Pressure,
				Value: device.Pres,
			})
			data.Labels[d.Name] = append(data.Labels[d.Name], Pressure)

		case *DescrOnBoard:
			{
				device := Bme280{}
				err := device.read(bus, d.I2CAddr, addr, mux[d.ChanID])
				if err != nil {
					return data, err
				}
				data.Sensors = append(data.Sensors, Data{
					Name:  d.Name,
					Type:  Pressure,
					Value: device.Pres,
				})
				data.Labels[d.Name] = append(data.Labels[d.Name], Pressure)
			}
			{
				device := Tsl2591{}
				err := device.read(bus, addr, mux[d.ChanID])
				if err != nil {
					return data, err
				}
				data.Sensors = append(data.Sensors, Data{
					Name:  d.Name,
					Type:  Luminosity,
					Value: device.Lux,
				})
				data.Labels[d.Name] = append(data.Labels[d.Name], Luminosity)
			}
		}
	}
	return data, nil
}

type Tsl2591 struct {
	Lux  float64 `json:"lux"`
	Full uint16  `json:"full"`
	IR   uint16  `json:"ir"`
}

func (tsl *Tsl2591) read(bus *smbus.Conn, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		log.Printf("tsl-write-reg error: %v", err)
		return err
	}

	dev, err := tsl2591.Open(bus, tsl2591.Addr, tsl2591.IntegTime100ms, tsl2591.GainLow)
	if err != nil {
		log.Printf("tsl-open-bus error: %v", err)
		return err
	}

	full, ir, err := dev.FullLuminosity()
	if err != nil {
		log.Printf("tsl-sample error: %v", err)
		return err
	}

	tsl.Lux = dev.Lux(full, ir)
	tsl.Full = full
	tsl.IR = ir

	return err
}

type Bme280 struct {
	Temp float64 `json:"temp"`
	Hum  float64 `json:"humi"`
	Pres float64 `json:"pres"`
}

func (bme *Bme280) read(bus *smbus.Conn, i2c, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		log.Printf("write-reg error: %v", err)
		return err
	}

	if i2c == 0 {
		i2c = bme280.I2CAddr
	}
	dev, err := bme280.Open(bus, i2c, bme280.OpSample8)
	if err != nil {
		log.Printf("open-bus error (i2c-addr=0x%x): %v", i2c, err)
		return err
	}

	h, p, t, err := dev.Sample()
	if err != nil {
		log.Printf("sample error: %v", err)
		return err
	}

	const HPa = 1.0 / 100.0
	bme.Hum = h
	bme.Pres = p * HPa
	bme.Temp = t

	return err
}

type ADC101x struct {
	Count   int     `json:"adc"`
	Voltage float64 `json:"voltage"`
}

func (adc *ADC101x) read(bus *smbus.Conn, i2c, daddr uint8, ch uint8, frange int, vdd float64) error {
	err := bus.WriteReg(daddr, 0x04, ch)
	if err != nil {
		log.Printf("adc101x-write-reg error: %v", err)
		return err
	}

	dev, err := adc101x.Open(bus, i2c, frange, vdd)
	if err != nil {
		log.Printf("adc101x-open-bus error: %v", err)
		return err
	}

	count, err := dev.ADC()
	if err != nil {
		return err
	}

	voltage, err := dev.Voltage()
	if err != nil {
		return err
	}

	adc.Count = count
	adc.Voltage = voltage
	return nil
}

type At30tse75x struct {
	Temp float64 `json:"temp"`
}

func (at30 *At30tse75x) read(bus *smbus.Conn, i2c, daddr uint8, ch uint8) error {
	err := bus.WriteReg(daddr, 0x04, ch)
	if err != nil {
		log.Printf("at30tse-write-reg error: %v", err)
		return err
	}

	dev, err := at30tse75x.Open(
		bus,
		at30tse75x.I2CAddr(i2c),
		at30tse75x.DevAddr(daddr),
		at30tse75x.EEPROM(4),
	)
	if err != nil {
		log.Printf("at30tse-open-bus error: %v", err)
		return err
	}

	t, err := dev.T()
	if err != nil {
		log.Printf("at30tse-sample error: %v", err)
		return err
	}
	at30.Temp = t
	return nil
}

type Hts221 struct {
	Temp float64 `json:"temp"`
	Humi float64 `json:"humi"`
}

func (hts *Hts221) read(bus *smbus.Conn, addr uint8, ch uint8) error {
	err := bus.WriteReg(addr, 0x04, ch)
	if err != nil {
		log.Printf("hts221-write-reg error: %v", err)
		return err
	}

	dev, err := hts221.Open(bus, hts221.SlaveAddr)
	if err != nil {
		log.Printf("hts221-open-bus error: %v", err)
		return err
	}

	h, t, err := dev.Sample()
	if err != nil {
		log.Printf("hts221-sample error: %v", err)
		return err
	}
	hts.Temp = t
	hts.Humi = h
	return nil
}

type Table []Sensors

func (tbl Table) Data(typ Type, label string) (float64, float64, plotter.XYs) {
	min := +math.MaxFloat64
	max := -math.MaxFloat64
	data := make(plotter.XYs, len(tbl))
	for i, v := range tbl {
		for _, sensor := range v.Sensors {
			if sensor.Type != typ || sensor.Name != label {
				continue
			}
			data[i].X = float64(v.Timestamp.UTC().Unix())
			data[i].Y = sensor.Value
			min = math.Min(min, sensor.Value)
			max = math.Max(max, sensor.Value)
		}
	}
	return min, max, data
}

func (tbl Table) Labels(typ Type) []string {
	var labels []string
	for k, v := range tbl[0].Labels {
		for _, t := range v {
			if typ == t {
				labels = append(labels, k)
			}
		}
	}
	return labels
}
<|endoftext|>


<commit_before>package github

type webhook struct {
	Ref     string `json:"ref"`
	Action  string `json:"action"`
	Deleted bool   `json:"deleted"`

	Head struct {
		ID        string `json:"id"`
		URL       string `json:"url"`
		Message   string `json:"message"`
		Timestamp string `json:"timestamp"`

		Author struct {
			Name     string `json:"name"`
			Email    string `json:"name"`
			Username string `json:"username"`
		} `json:"author"`

		Committer struct {
			Name     string `json:"name"`
			Email    string `json:"name"`
			Username string `json:"username"`
		} `json:"committer"`
	} `json:"head_commit"`

	Sender struct {
		Login  string `json:"login"`
		Avatar string `json:"avatar_url"`
	} `json:"sender"`

	// repository details
	Repo struct {
		Owner struct {
			Login string `json:"login"`
			Name  string `json:"name"`
		} `json:"owner"`

		Name          string `json:"name"`
		FullName      string `json:"full_name"`
		Language      string `json:"language"`
		Private       bool   `json:"private"`
		HTMLURL       string `json:"html_url"`
		CloneURL      string `json:"clone_url"`
		DefaultBranch string `json:"default_branch"`
	} `json:"repository"`

	// deployment hook details
	Deployment struct {
		ID   int64  `json:"id"`
		Sha  string `json:"sha"`
		Ref  string `json:"ref"`
		Task string `json:"task"`
		Env  string `json:"environment"`
		URL  string `json:"url"`
		Desc string `json:"description"`
	} `json:"deployment"`

	// pull request details
	PullRequest struct {
		Number  int    `json:"number"`
		State   string `json:"state"`
		Title   string `json:"title"`
		HTMLURL string `json:"html_url"`

		User struct {
			Login  string `json:"login"`
			Avatar string `json:"avatar_url"`
		} `json:"user"`

		Base struct {
			Ref string `json:"ref"`
		} `json:"base"`

		Head struct {
			SHA  string `json:"sha"`
			Ref  string `json:"ref"`
			Repo struct {
				CloneURL string `json:"clone_url"`
			} `json:"repo"`
		} `json:"head"`
	} `json:"pull_request"`
}
<commit_msg>Github: fix webhook struct tags<commit_after>package github

type webhook struct {
	Ref     string `json:"ref"`
	Action  string `json:"action"`
	Deleted bool   `json:"deleted"`

	Head struct {
		ID        string `json:"id"`
		URL       string `json:"url"`
		Message   string `json:"message"`
		Timestamp string `json:"timestamp"`

		Author struct {
			Name     string `json:"name"`
			Email    string `json:"email"`
			Username string `json:"username"`
		} `json:"author"`

		Committer struct {
			Name     string `json:"name"`
			Email    string `json:"email"`
			Username string `json:"username"`
		} `json:"committer"`
	} `json:"head_commit"`

	Sender struct {
		Login  string `json:"login"`
		Avatar string `json:"avatar_url"`
	} `json:"sender"`

	// repository details
	Repo struct {
		Owner struct {
			Login string `json:"login"`
			Name  string `json:"name"`
		} `json:"owner"`

		Name          string `json:"name"`
		FullName      string `json:"full_name"`
		Language      string `json:"language"`
		Private       bool   `json:"private"`
		HTMLURL       string `json:"html_url"`
		CloneURL      string `json:"clone_url"`
		DefaultBranch string `json:"default_branch"`
	} `json:"repository"`

	// deployment hook details
	Deployment struct {
		ID   int64  `json:"id"`
		Sha  string `json:"sha"`
		Ref  string `json:"ref"`
		Task string `json:"task"`
		Env  string `json:"environment"`
		URL  string `json:"url"`
		Desc string `json:"description"`
	} `json:"deployment"`

	// pull request details
	PullRequest struct {
		Number  int    `json:"number"`
		State   string `json:"state"`
		Title   string `json:"title"`
		HTMLURL string `json:"html_url"`

		User struct {
			Login  string `json:"login"`
			Avatar string `json:"avatar_url"`
		} `json:"user"`

		Base struct {
			Ref string `json:"ref"`
		} `json:"base"`

		Head struct {
			SHA  string `json:"sha"`
			Ref  string `json:"ref"`
			Repo struct {
				CloneURL string `json:"clone_url"`
			} `json:"repo"`
		} `json:"head"`
	} `json:"pull_request"`
}
<|endoftext|>


<commit_before>package ast

import (
	"fmt"
	"io"

	"github.com/DeedleFake/wdte/ast/internal/pgen"
	"github.com/DeedleFake/wdte/scanner"
)

func Parse(r io.Reader) (ast Node, err error) {
	ast = &NTerm{NTerm: "script"}
	s := scanner.New(r)
	g := tokenStack{"EOF", pgen.NTerm("script")}

	more := s.Scan()
	cur := ast.(*NTerm)
	for len(g) > 0 {
		gtok := g.Pop()
		if gtok == nil {
			cur = cur.Parent().(*NTerm)
			continue
		}
		if gtok == "EOF" {
			if more {
				return nil, parseError(s, fmt.Errorf("EOF expected, but found %v", s.Tok().Type))
			}

			return ast, nil
		}
		if !more {
			if err = s.Err(); err != nil {
				return nil, parseError(s, err)
			}

			return nil, parseError(s, fmt.Errorf("Unexpected EOF"))
		}

		switch gtok := gtok.(type) {
		case pgen.Term:
			if !tokensEqual(s.Tok(), gtok) {
				return nil, parseError(s, fmt.Errorf("Expected %v (<%v>), but found %v", gtok, cur.NTerm, s.Tok().Val))
			}

			val := s.Tok().Val
			cur.AddChild(&Term{
				Term: gtok,
				p:    cur,
				v:    val,
			})
			more = s.Scan()

		case pgen.NTerm:
			rule := pgen.Table[pgen.Lookup{Term: toPGenTerm(s.Tok()), NTerm: gtok}]
			if rule == nil {
				return nil, parseError(s, fmt.Errorf("No rule for (%v, <%v>)", toPGenTerm(s.Tok()), gtok))
			}

			g.PushRule(rule)

			child := &NTerm{
				NTerm: gtok,
				p:     cur,
			}
			cur.AddChild(child)
			cur = child

		case pgen.Epsilon:
			cur.AddChild(&Epsilon{
				p: cur,
			})
		}
	}

	panic("This should be unreachable.")
}

func tokensEqual(stok scanner.Token, gtok pgen.Token) bool {
	switch gtok := gtok.(type) {
	case pgen.Term:
		return (gtok.Type == stok.Type) && ((stok.Type != scanner.Keyword) || (gtok.Keyword == stok.Val))
	}

	panic(fmt.Errorf("Tried to compare non-terminal: %#v", gtok))
}

func toPGenTerm(tok scanner.Token) pgen.Term {
	var keyword string
	if tok.Type == scanner.Keyword {
		keyword = fmt.Sprintf("%v", tok.Val)
	}

	return pgen.Term{
		Type:    tok.Type,
		Keyword: keyword,
	}
}

type ParseError struct {
	Line, Col int
	Err       error
}

func parseError(s *scanner.Scanner, err error) ParseError {
	line, col := s.Pos()
	return ParseError{
		Line: line, Col: col,
		Err: err,
	}
}

func (err ParseError) Error() string {
	return fmt.Sprintf("%v:%v: %v", err.Line, err.Col, err.Err)
}
<commit_msg>Revert "ast: Move return into loop."<commit_after>package ast

import (
	"fmt"
	"io"

	"github.com/DeedleFake/wdte/ast/internal/pgen"
	"github.com/DeedleFake/wdte/scanner"
)

func Parse(r io.Reader) (ast Node, err error) {
	ast = &NTerm{NTerm: "script"}
	s := scanner.New(r)
	g := tokenStack{"EOF", pgen.NTerm("script")}

	more := s.Scan()
	cur := ast.(*NTerm)
	for len(g) > 0 {
		gtok := g.Pop()
		if gtok == nil {
			cur = cur.Parent().(*NTerm)
			continue
		}
		if gtok == "EOF" {
			if more {
				return nil, parseError(s, fmt.Errorf("EOF expected, but found %v", s.Tok().Type))
			}
			break
		}
		if !more {
			if err = s.Err(); err != nil {
				return nil, parseError(s, err)
			}

			return nil, parseError(s, fmt.Errorf("Unexpected EOF"))
		}

		switch gtok := gtok.(type) {
		case pgen.Term:
			if !tokensEqual(s.Tok(), gtok) {
				return nil, parseError(s, fmt.Errorf("Expected %v (<%v>), but found %v", gtok, cur.NTerm, s.Tok().Val))
			}

			val := s.Tok().Val
			cur.AddChild(&Term{
				Term: gtok,
				p:    cur,
				v:    val,
			})
			more = s.Scan()

		case pgen.NTerm:
			rule := pgen.Table[pgen.Lookup{Term: toPGenTerm(s.Tok()), NTerm: gtok}]
			if rule == nil {
				return nil, parseError(s, fmt.Errorf("No rule for (%v, <%v>)", toPGenTerm(s.Tok()), gtok))
			}

			g.PushRule(rule)

			child := &NTerm{
				NTerm: gtok,
				p:     cur,
			}
			cur.AddChild(child)
			cur = child

		case pgen.Epsilon:
			cur.AddChild(&Epsilon{
				p: cur,
			})
		}
	}

	return ast, nil
}

func tokensEqual(stok scanner.Token, gtok pgen.Token) bool {
	switch gtok := gtok.(type) {
	case pgen.Term:
		return (gtok.Type == stok.Type) && ((stok.Type != scanner.Keyword) || (gtok.Keyword == stok.Val))
	}

	panic(fmt.Errorf("Tried to compare non-terminal: %#v", gtok))
}

func toPGenTerm(tok scanner.Token) pgen.Term {
	var keyword string
	if tok.Type == scanner.Keyword {
		keyword = fmt.Sprintf("%v", tok.Val)
	}

	return pgen.Term{
		Type:    tok.Type,
		Keyword: keyword,
	}
}

type ParseError struct {
	Line, Col int
	Err       error
}

func parseError(s *scanner.Scanner, err error) ParseError {
	line, col := s.Pos()
	return ParseError{
		Line: line, Col: col,
		Err: err,
	}
}

func (err ParseError) Error() string {
	return fmt.Sprintf("%v:%v: %v", err.Line, err.Col, err.Err)
}
<|endoftext|>


<commit_before>// Copyright 2018 Jigsaw Operations LLC
//
// 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
//
//     https://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 shadowsocks

import (
	"errors"
	"fmt"
	"net"
	"runtime/debug"
	"time"

	"github.com/Jigsaw-Code/outline-ss-server/metrics"
	logging "github.com/op/go-logging"

	onet "github.com/Jigsaw-Code/outline-ss-server/net"

	"sync"

	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
	"github.com/shadowsocks/go-shadowsocks2/socks"
)

const udpBufSize = 64 * 1024

// Wrapper for logger.Debugf during UDP proxying.
func debugUDP(tag string, template string, val interface{}) {
	// This is an optimization to reduce unnecessary allocations due to an interaction
	// between Go's inlining/escape analysis and varargs functions like logger.Debugf.
	if logger.IsEnabledFor(logging.DEBUG) {
		logger.Debugf("UDP(%s): "+template, tag, val)
	}
}

func debugUDPAddr(addr net.Addr, template string, val interface{}) {
	if logger.IsEnabledFor(logging.DEBUG) {
		// Avoid calling addr.String() unless debugging is enabled.
		debugUDP(addr.String(), template, val)
	}
}

// upack decrypts src into dst. It tries each cipher until it finds one that authenticates
// correctly. dst and src must not overlap.
func unpack(clientIP net.IP, dst, src []byte, cipherList CipherList) ([]byte, string, shadowaead.Cipher, error) {
	// Try each cipher until we find one that authenticates successfully. This assumes that all ciphers are AEAD.
	// We snapshot the list because it may be modified while we use it.
	for ci, entry := range cipherList.SnapshotForClientIP(clientIP) {
		id, cipher := entry.Value.(*CipherEntry).ID, entry.Value.(*CipherEntry).Cipher
		buf, err := shadowaead.Unpack(dst, src, cipher)
		if err != nil {
			debugUDP(id, "Failed to unpack: %v", err)
			continue
		}
		debugUDP(id, "Found cipher at index %d", ci)
		// Move the active cipher to the front, so that the search is quicker next time.
		cipherList.MarkUsedByClientIP(entry, clientIP)
		return buf, id, cipher, nil
	}
	return nil, "", nil, errors.New("could not find valid cipher")
}

type udpService struct {
	clientConn     net.PacketConn
	natTimeout     time.Duration
	ciphers        CipherList
	m              metrics.ShadowsocksMetrics
	isRunning      bool
	checkAllowedIP func(net.IP) *onet.ConnectionError
}

// NewUDPService creates a UDPService
func NewUDPService(clientConn net.PacketConn, natTimeout time.Duration, cipherList CipherList, m metrics.ShadowsocksMetrics) UDPService {
	return &udpService{clientConn: clientConn, natTimeout: natTimeout, ciphers: cipherList, m: m, checkAllowedIP: onet.RequirePublicIP}
}

// UDPService is a UDP shadowsocks service that can be started and stopped.
type UDPService interface {
	Start()
	Stop() error
}

// Listen on addr for encrypted packets and basically do UDP NAT.
// We take the ciphers as a pointer because it gets replaced on config updates.
func (s *udpService) Start() {
	defer s.clientConn.Close()

	nm := newNATmap(s.natTimeout, s.m)
	cipherBuf := make([]byte, udpBufSize)
	textBuf := make([]byte, udpBufSize)

	s.isRunning = true
	for s.isRunning {
		func() (connError *onet.ConnectionError) {
			defer func() {
				if r := recover(); r != nil {
					logger.Errorf("Panic in UDP loop: %v", r)
					debug.PrintStack()
				}
			}()
			clientLocation := ""
			keyID := ""
			var clientProxyBytes, proxyTargetBytes int
			var timeToCipher time.Duration
			defer func() {
				status := "OK"
				if connError != nil {
					logger.Debugf("UDP Error: %v: %v", connError.Message, connError.Cause)
					status = connError.Status
				}
				s.m.AddUDPPacketFromClient(clientLocation, keyID, status, clientProxyBytes, proxyTargetBytes, timeToCipher)
			}()
			clientProxyBytes, clientAddr, err := s.clientConn.ReadFrom(cipherBuf)
			if err != nil {
				if !s.isRunning {
					return nil
				}
				return onet.NewConnectionError("ERR_READ", "Failed to read from client", err)
			}
			if logger.IsEnabledFor(logging.DEBUG) {
				defer logger.Debugf("UDP(%v): done", clientAddr)
				debugUDPAddr(clientAddr, "Outbound packet has %d bytes", clientProxyBytes)
			}
			unpackStart := time.Now()
			ip := clientAddr.(*net.UDPAddr).IP
			buf, keyID, cipher, err := unpack(ip, textBuf, cipherBuf[:clientProxyBytes], s.ciphers)
			timeToCipher = time.Now().Sub(unpackStart)

			if err != nil {
				return onet.NewConnectionError("ERR_CIPHER", "Failed to upack data from client", err)
			}

			tgtAddr := socks.SplitAddr(buf)
			if tgtAddr == nil {
				return onet.NewConnectionError("ERR_READ_ADDRESS", "Failed to get target address", nil)
			}

			tgtUDPAddr, err := net.ResolveUDPAddr("udp", tgtAddr.String())
			if err != nil {
				return onet.NewConnectionError("ERR_RESOLVE_ADDRESS", fmt.Sprintf("Failed to resolve target address %v", tgtAddr), err)
			}
			if err := s.checkAllowedIP(tgtUDPAddr.IP); err != nil {
				return err
			}

			payload := buf[len(tgtAddr):]

			targetConn, clientLocation := nm.Get(clientAddr.String())
			if targetConn == nil {
				clientLocation, locErr := s.m.GetLocation(clientAddr)
				if locErr != nil {
					logger.Warningf("Failed location lookup: %v", locErr)
				}
				debugUDPAddr(clientAddr, "Got location \"%s\"", clientLocation)

				targetConn, err = net.ListenPacket("udp", "")
				if err != nil {
					return onet.NewConnectionError("ERR_CREATE_SOCKET", "Failed to create UDP socket", err)
				}
				nm.Add(clientAddr, s.clientConn, cipher, targetConn, clientLocation, keyID)
			}

			debugUDPAddr(clientAddr, "Proxy exit %v", targetConn.LocalAddr())
			proxyTargetBytes, err = targetConn.WriteTo(payload, tgtUDPAddr) // accept only UDPAddr despite the signature
			if err != nil {
				return onet.NewConnectionError("ERR_WRITE", "Failed to write to target", err)
			}
			return nil
		}()
	}
}

func (s *udpService) Stop() error {
	s.isRunning = false
	return s.clientConn.Close()
}

type natentry struct {
	conn net.PacketConn
	// We store the client location in the NAT map to avoid recomputing it
	// for every outbound packet in a UDP-based connection.
	clientLocation string
}

// Packet NAT table
type natmap struct {
	sync.RWMutex
	keyConn map[string]natentry
	timeout time.Duration
	metrics metrics.ShadowsocksMetrics
}

func newNATmap(timeout time.Duration, sm metrics.ShadowsocksMetrics) *natmap {
	m := &natmap{metrics: sm}
	m.keyConn = make(map[string]natentry)
	m.timeout = timeout
	return m
}

func (m *natmap) Get(key string) (net.PacketConn, string) {
	m.RLock()
	defer m.RUnlock()
	entry := m.keyConn[key]
	return entry.conn, entry.clientLocation
}

func (m *natmap) set(key string, pc net.PacketConn, clientLocation string) {
	m.Lock()
	defer m.Unlock()

	m.keyConn[key] = natentry{pc, clientLocation}
}

func (m *natmap) del(key string) net.PacketConn {
	m.Lock()
	defer m.Unlock()

	entry, ok := m.keyConn[key]
	if ok {
		delete(m.keyConn, key)
		return entry.conn
	}
	return nil
}

func (m *natmap) Add(clientAddr net.Addr, clientConn net.PacketConn, cipher shadowaead.Cipher, targetConn net.PacketConn, clientLocation, keyID string) {
	m.set(clientAddr.String(), targetConn, clientLocation)

	m.metrics.AddUDPNatEntry()
	go func() {
		timedCopy(clientAddr, clientConn, cipher, targetConn, m.timeout, clientLocation, keyID, m.metrics)
		m.metrics.RemoveUDPNatEntry()
		if pc := m.del(clientAddr.String()); pc != nil {
			pc.Close()
		}
	}()
}

// copy from src to dst at target with read timeout
func timedCopy(clientAddr net.Addr, clientConn net.PacketConn, cipher shadowaead.Cipher, targetConn net.PacketConn,
	timeout time.Duration, clientLocation, keyID string, sm metrics.ShadowsocksMetrics) {
	textBuf := make([]byte, udpBufSize)
	cipherBuf := make([]byte, udpBufSize)

	expired := false
	for !expired {
		var targetProxyBytes, proxyClientBytes int
		connError := func() (connError *onet.ConnectionError) {
			var (
				raddr net.Addr
				err   error
			)
			targetConn.SetReadDeadline(time.Now().Add(timeout))
			targetProxyBytes, raddr, err = targetConn.ReadFrom(textBuf)
			if err != nil {
				if netErr, ok := err.(net.Error); ok {
					if netErr.Timeout() {
						expired = true
						return nil
					}
				}
				return onet.NewConnectionError("ERR_READ", "Failed to read from target", err)
			}

			debugUDPAddr(clientAddr, "Got response from %v", raddr)
			srcAddr := socks.ParseAddr(raddr.String())
			// Shift data buffer to prepend with srcAddr.
			copy(textBuf[len(srcAddr):], textBuf[:targetProxyBytes])
			copy(textBuf, srcAddr)

			buf, err := shadowaead.Pack(cipherBuf, textBuf[:len(srcAddr)+targetProxyBytes], cipher)
			if err != nil {
				return onet.NewConnectionError("ERR_PACK", "Failed to pack data to client", err)
			}
			proxyClientBytes, err = clientConn.WriteTo(buf, clientAddr)
			if err != nil {
				return onet.NewConnectionError("ERR_WRITE", "Failed to write to client", err)
			}
			return nil
		}()
		status := "OK"
		if connError != nil {
			logger.Debugf("UDP Error: %v: %v", connError.Message, connError.Cause)
			status = connError.Status
		}
		sm.AddUDPPacketFromTarget(clientLocation, keyID, status, targetProxyBytes, proxyClientBytes)
	}
}
<commit_msg>Use logger.Debugf directly inside the if-block<commit_after>// Copyright 2018 Jigsaw Operations LLC
//
// 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
//
//     https://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 shadowsocks

import (
	"errors"
	"fmt"
	"net"
	"runtime/debug"
	"time"

	"github.com/Jigsaw-Code/outline-ss-server/metrics"
	logging "github.com/op/go-logging"

	onet "github.com/Jigsaw-Code/outline-ss-server/net"

	"sync"

	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
	"github.com/shadowsocks/go-shadowsocks2/socks"
)

const udpBufSize = 64 * 1024

// Wrapper for logger.Debugf during UDP proxying.
func debugUDP(tag string, template string, val interface{}) {
	// This is an optimization to reduce unnecessary allocations due to an interaction
	// between Go's inlining/escape analysis and varargs functions like logger.Debugf.
	if logger.IsEnabledFor(logging.DEBUG) {
		logger.Debugf("UDP(%s): "+template, tag, val)
	}
}

func debugUDPAddr(addr net.Addr, template string, val interface{}) {
	if logger.IsEnabledFor(logging.DEBUG) {
		// Avoid calling addr.String() unless debugging is enabled.
		debugUDP(addr.String(), template, val)
	}
}

// upack decrypts src into dst. It tries each cipher until it finds one that authenticates
// correctly. dst and src must not overlap.
func unpack(clientIP net.IP, dst, src []byte, cipherList CipherList) ([]byte, string, shadowaead.Cipher, error) {
	// Try each cipher until we find one that authenticates successfully. This assumes that all ciphers are AEAD.
	// We snapshot the list because it may be modified while we use it.
	for ci, entry := range cipherList.SnapshotForClientIP(clientIP) {
		id, cipher := entry.Value.(*CipherEntry).ID, entry.Value.(*CipherEntry).Cipher
		buf, err := shadowaead.Unpack(dst, src, cipher)
		if err != nil {
			debugUDP(id, "Failed to unpack: %v", err)
			continue
		}
		debugUDP(id, "Found cipher at index %d", ci)
		// Move the active cipher to the front, so that the search is quicker next time.
		cipherList.MarkUsedByClientIP(entry, clientIP)
		return buf, id, cipher, nil
	}
	return nil, "", nil, errors.New("could not find valid cipher")
}

type udpService struct {
	clientConn     net.PacketConn
	natTimeout     time.Duration
	ciphers        CipherList
	m              metrics.ShadowsocksMetrics
	isRunning      bool
	checkAllowedIP func(net.IP) *onet.ConnectionError
}

// NewUDPService creates a UDPService
func NewUDPService(clientConn net.PacketConn, natTimeout time.Duration, cipherList CipherList, m metrics.ShadowsocksMetrics) UDPService {
	return &udpService{clientConn: clientConn, natTimeout: natTimeout, ciphers: cipherList, m: m, checkAllowedIP: onet.RequirePublicIP}
}

// UDPService is a UDP shadowsocks service that can be started and stopped.
type UDPService interface {
	Start()
	Stop() error
}

// Listen on addr for encrypted packets and basically do UDP NAT.
// We take the ciphers as a pointer because it gets replaced on config updates.
func (s *udpService) Start() {
	defer s.clientConn.Close()

	nm := newNATmap(s.natTimeout, s.m)
	cipherBuf := make([]byte, udpBufSize)
	textBuf := make([]byte, udpBufSize)

	s.isRunning = true
	for s.isRunning {
		func() (connError *onet.ConnectionError) {
			defer func() {
				if r := recover(); r != nil {
					logger.Errorf("Panic in UDP loop: %v", r)
					debug.PrintStack()
				}
			}()
			clientLocation := ""
			keyID := ""
			var clientProxyBytes, proxyTargetBytes int
			var timeToCipher time.Duration
			defer func() {
				status := "OK"
				if connError != nil {
					logger.Debugf("UDP Error: %v: %v", connError.Message, connError.Cause)
					status = connError.Status
				}
				s.m.AddUDPPacketFromClient(clientLocation, keyID, status, clientProxyBytes, proxyTargetBytes, timeToCipher)
			}()
			clientProxyBytes, clientAddr, err := s.clientConn.ReadFrom(cipherBuf)
			if err != nil {
				if !s.isRunning {
					return nil
				}
				return onet.NewConnectionError("ERR_READ", "Failed to read from client", err)
			}
			if logger.IsEnabledFor(logging.DEBUG) {
				defer logger.Debugf("UDP(%v): done", clientAddr)
				logger.Debugf("UDP(%v): Outbound packet has %d bytes", clientAddr, clientProxyBytes)
			}
			unpackStart := time.Now()
			ip := clientAddr.(*net.UDPAddr).IP
			buf, keyID, cipher, err := unpack(ip, textBuf, cipherBuf[:clientProxyBytes], s.ciphers)
			timeToCipher = time.Now().Sub(unpackStart)

			if err != nil {
				return onet.NewConnectionError("ERR_CIPHER", "Failed to upack data from client", err)
			}

			tgtAddr := socks.SplitAddr(buf)
			if tgtAddr == nil {
				return onet.NewConnectionError("ERR_READ_ADDRESS", "Failed to get target address", nil)
			}

			tgtUDPAddr, err := net.ResolveUDPAddr("udp", tgtAddr.String())
			if err != nil {
				return onet.NewConnectionError("ERR_RESOLVE_ADDRESS", fmt.Sprintf("Failed to resolve target address %v", tgtAddr), err)
			}
			if err := s.checkAllowedIP(tgtUDPAddr.IP); err != nil {
				return err
			}

			payload := buf[len(tgtAddr):]

			targetConn, clientLocation := nm.Get(clientAddr.String())
			if targetConn == nil {
				clientLocation, locErr := s.m.GetLocation(clientAddr)
				if locErr != nil {
					logger.Warningf("Failed location lookup: %v", locErr)
				}
				debugUDPAddr(clientAddr, "Got location \"%s\"", clientLocation)

				targetConn, err = net.ListenPacket("udp", "")
				if err != nil {
					return onet.NewConnectionError("ERR_CREATE_SOCKET", "Failed to create UDP socket", err)
				}
				nm.Add(clientAddr, s.clientConn, cipher, targetConn, clientLocation, keyID)
			}

			debugUDPAddr(clientAddr, "Proxy exit %v", targetConn.LocalAddr())
			proxyTargetBytes, err = targetConn.WriteTo(payload, tgtUDPAddr) // accept only UDPAddr despite the signature
			if err != nil {
				return onet.NewConnectionError("ERR_WRITE", "Failed to write to target", err)
			}
			return nil
		}()
	}
}

func (s *udpService) Stop() error {
	s.isRunning = false
	return s.clientConn.Close()
}

type natentry struct {
	conn net.PacketConn
	// We store the client location in the NAT map to avoid recomputing it
	// for every outbound packet in a UDP-based connection.
	clientLocation string
}

// Packet NAT table
type natmap struct {
	sync.RWMutex
	keyConn map[string]natentry
	timeout time.Duration
	metrics metrics.ShadowsocksMetrics
}

func newNATmap(timeout time.Duration, sm metrics.ShadowsocksMetrics) *natmap {
	m := &natmap{metrics: sm}
	m.keyConn = make(map[string]natentry)
	m.timeout = timeout
	return m
}

func (m *natmap) Get(key string) (net.PacketConn, string) {
	m.RLock()
	defer m.RUnlock()
	entry := m.keyConn[key]
	return entry.conn, entry.clientLocation
}

func (m *natmap) set(key string, pc net.PacketConn, clientLocation string) {
	m.Lock()
	defer m.Unlock()

	m.keyConn[key] = natentry{pc, clientLocation}
}

func (m *natmap) del(key string) net.PacketConn {
	m.Lock()
	defer m.Unlock()

	entry, ok := m.keyConn[key]
	if ok {
		delete(m.keyConn, key)
		return entry.conn
	}
	return nil
}

func (m *natmap) Add(clientAddr net.Addr, clientConn net.PacketConn, cipher shadowaead.Cipher, targetConn net.PacketConn, clientLocation, keyID string) {
	m.set(clientAddr.String(), targetConn, clientLocation)

	m.metrics.AddUDPNatEntry()
	go func() {
		timedCopy(clientAddr, clientConn, cipher, targetConn, m.timeout, clientLocation, keyID, m.metrics)
		m.metrics.RemoveUDPNatEntry()
		if pc := m.del(clientAddr.String()); pc != nil {
			pc.Close()
		}
	}()
}

// copy from src to dst at target with read timeout
func timedCopy(clientAddr net.Addr, clientConn net.PacketConn, cipher shadowaead.Cipher, targetConn net.PacketConn,
	timeout time.Duration, clientLocation, keyID string, sm metrics.ShadowsocksMetrics) {
	textBuf := make([]byte, udpBufSize)
	cipherBuf := make([]byte, udpBufSize)

	expired := false
	for !expired {
		var targetProxyBytes, proxyClientBytes int
		connError := func() (connError *onet.ConnectionError) {
			var (
				raddr net.Addr
				err   error
			)
			targetConn.SetReadDeadline(time.Now().Add(timeout))
			targetProxyBytes, raddr, err = targetConn.ReadFrom(textBuf)
			if err != nil {
				if netErr, ok := err.(net.Error); ok {
					if netErr.Timeout() {
						expired = true
						return nil
					}
				}
				return onet.NewConnectionError("ERR_READ", "Failed to read from target", err)
			}

			debugUDPAddr(clientAddr, "Got response from %v", raddr)
			srcAddr := socks.ParseAddr(raddr.String())
			// Shift data buffer to prepend with srcAddr.
			copy(textBuf[len(srcAddr):], textBuf[:targetProxyBytes])
			copy(textBuf, srcAddr)

			buf, err := shadowaead.Pack(cipherBuf, textBuf[:len(srcAddr)+targetProxyBytes], cipher)
			if err != nil {
				return onet.NewConnectionError("ERR_PACK", "Failed to pack data to client", err)
			}
			proxyClientBytes, err = clientConn.WriteTo(buf, clientAddr)
			if err != nil {
				return onet.NewConnectionError("ERR_WRITE", "Failed to write to client", err)
			}
			return nil
		}()
		status := "OK"
		if connError != nil {
			logger.Debugf("UDP Error: %v: %v", connError.Message, connError.Cause)
			status = connError.Status
		}
		sm.AddUDPPacketFromTarget(clientLocation, keyID, status, targetProxyBytes, proxyClientBytes)
	}
}
<|endoftext|>


<commit_before>package enmime

import (
	"net/textproto"
	"strings"
)

// detectMultipartMessage returns true if the message has a recognized multipart Content-Type header
func detectMultipartMessage(root *Part) bool {
	// Parse top-level multipart
	ctype := root.Header.Get(hnContentType)
	mediatype, _, _, err := parseMediaType(ctype)
	if err != nil {
		return false
	}
	// According to rfc2046#section-5.1.7 all other multipart should
	// be treated as multipart/mixed
	return strings.HasPrefix(mediatype, ctMultipartPrefix)
}

// detectAttachmentHeader returns true, if the given header defines an attachment.  First it checks
// if the Content-Disposition header defines an attachement or inline attachment. If this test is
// false, the Content-Type header is checked for attachment, but not inline.  Email clients use
// inline for their text bodies.
//
// Valid Attachment-Headers:
//
//  - Content-Disposition: attachment; filename="frog.jpg"
//  - Content-Disposition: inline; filename="frog.jpg"
//  - Content-Type: attachment; filename="frog.jpg"
func detectAttachmentHeader(header textproto.MIMEHeader) bool {
	mediatype, params, _, _ := parseMediaType(header.Get(hnContentDisposition))
	if strings.ToLower(mediatype) == cdAttachment ||
		(strings.ToLower(mediatype) == cdInline && len(params) > 0) {
		return true
	}

	mediatype, _, _, _ = parseMediaType(header.Get(hnContentType))
	return strings.ToLower(mediatype) == cdAttachment
}

// detectTextHeader returns true, if the the MIME headers define a valid 'text/plain' or 'text/html'
// part.  If the emptyContentTypeIsPlain argument is set to true, a missing Content-Type header will
// result in a positive plain part detection.
func detectTextHeader(header textproto.MIMEHeader, emptyContentTypeIsText bool) bool {
	ctype := header.Get(hnContentType)
	if ctype == "" && emptyContentTypeIsText {
		return true
	}

	mediatype, _, _, err := parseMediaType(ctype)
	if err != nil {
		return false
	}
	switch mediatype {
	case ctTextPlain, ctTextHTML:
		return true
	}

	return false
}

// detectBinaryBody returns true if the mail header defines a binary body.
func detectBinaryBody(root *Part) bool {
	if detectTextHeader(root.Header, true) {
		return false
	}

	isBin := detectAttachmentHeader(root.Header)
	if !isBin {
		// This must be an attachment, if the Content-Type is not
		// 'text/plain' or 'text/html'.
		// Example:
		// Content-Type: application/pdf; name="doc.pdf"
		mediatype, _, _, _ := parseMediaType(root.Header.Get(hnContentType))
		mediatype = strings.ToLower(mediatype)
		if mediatype != ctTextPlain && mediatype != ctTextHTML {
			return true
		}
	}

	return isBin
}
<commit_msg>Updated "detectAttachmentHeader" doc comment to, more accurately, depict the function flow (#116)<commit_after>package enmime

import (
	"net/textproto"
	"strings"
)

// detectMultipartMessage returns true if the message has a recognized multipart Content-Type header
func detectMultipartMessage(root *Part) bool {
	// Parse top-level multipart
	ctype := root.Header.Get(hnContentType)
	mediatype, _, _, err := parseMediaType(ctype)
	if err != nil {
		return false
	}
	// According to rfc2046#section-5.1.7 all other multipart should
	// be treated as multipart/mixed
	return strings.HasPrefix(mediatype, ctMultipartPrefix)
}

// detectAttachmentHeader returns true, if the given header defines an attachment. First it checks
// if the Content-Disposition header defines either an attachment part or an inline part with at
// least one parameter. If this test is false, the Content-Type header is checked for attachment,
// but not inline. Email clients use inline for their text bodies.
//
// Valid Attachment-Headers:
//
//  - Content-Disposition: attachment; filename="frog.jpg"
//  - Content-Disposition: inline; filename="frog.jpg"
//  - Content-Type: attachment; filename="frog.jpg"
func detectAttachmentHeader(header textproto.MIMEHeader) bool {
	mediatype, params, _, _ := parseMediaType(header.Get(hnContentDisposition))
	if strings.ToLower(mediatype) == cdAttachment ||
		(strings.ToLower(mediatype) == cdInline && len(params) > 0) {
		return true
	}

	mediatype, _, _, _ = parseMediaType(header.Get(hnContentType))
	return strings.ToLower(mediatype) == cdAttachment
}

// detectTextHeader returns true, if the the MIME headers define a valid 'text/plain' or 'text/html'
// part.  If the emptyContentTypeIsPlain argument is set to true, a missing Content-Type header will
// result in a positive plain part detection.
func detectTextHeader(header textproto.MIMEHeader, emptyContentTypeIsText bool) bool {
	ctype := header.Get(hnContentType)
	if ctype == "" && emptyContentTypeIsText {
		return true
	}

	mediatype, _, _, err := parseMediaType(ctype)
	if err != nil {
		return false
	}
	switch mediatype {
	case ctTextPlain, ctTextHTML:
		return true
	}

	return false
}

// detectBinaryBody returns true if the mail header defines a binary body.
func detectBinaryBody(root *Part) bool {
	if detectTextHeader(root.Header, true) {
		return false
	}

	isBin := detectAttachmentHeader(root.Header)
	if !isBin {
		// This must be an attachment, if the Content-Type is not
		// 'text/plain' or 'text/html'.
		// Example:
		// Content-Type: application/pdf; name="doc.pdf"
		mediatype, _, _, _ := parseMediaType(root.Header.Get(hnContentType))
		mediatype = strings.ToLower(mediatype)
		if mediatype != ctTextPlain && mediatype != ctTextHTML {
			return true
		}
	}

	return isBin
}
<|endoftext|>


<commit_before>package main

import (
  "fmt"

  "github.com/codegangsta/cli"
  "github.com/gongo/go-airplay"
)

// Show devices
func Devices(c *cli.Context) {
  for _, d := range airplay.Devices() {
    fmt.Println(d.Name)
  }
}
<commit_msg>Extract search device function<commit_after>package main

import (
	"fmt"

	"github.com/Tomohiro/go-airplay"
)

// Devices Returns AirPlay devices
func Devices() []airplay.Device {
	devices := airplay.Devices()
	if len(devices) == 0 {
		fmt.Println("air: AirPlay Devices not found")
	}

	return devices
}
<|endoftext|>


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

package nvim

import (
	"strconv"
	"strings"

	"github.com/garyburd/neovim-go/vim"
)

// Loclist represents an item in a quickfix list.
type ErrorlistData struct {
	// Buffer number
	Bufnr int `msgpack:"bufnr,omitempty"`

	// Name of a file; only used when bufnr is not present or it is invalid.
	FileName string `msgpack:"filename,omitempty"`

	// Line number in the file.
	LNum int `msgpack:"lnum,omitempty"`

	// Column number (first column is 1).
	Col int `msgpack:"col,omitempty"`

	// When Vcol is != 0,  Col is visual column.
	VCol int `msgpack:"vcol,omitempty"`

	// Error number.
	Nr int `msgpack:"nr,omitempty"`

	// Search pattern used to locate the error.
	Pattern string `msgpack:"pattern,omitempty"`

	// Description of the error.
	Text string `msgpack:"text,omitempty"`

	// Single-character error type, 'E', 'W', etc.
	Type string `msgpack:"type,omitempty"`

	// Valid is non-zero if this is a recognized error message.
	Valid int `msgpack:"valid,omitempty"`
}

func SetLoclist(p *vim.Pipeline, loclist []*ErrorlistData) error {
	// setloclist({nr}, {list} [, {action}])
	// Call(fname string, result interface{}, args ...interface{})
	if len(loclist) > 0 {
		p.Call("setloclist", nil, 0, loclist)
	} else {
		p.Command("lexpr ''")
	}

	return nil
}

func OpenLoclist(p *vim.Pipeline, w vim.Window, loclist []*ErrorlistData, keep bool) error {
	if len(loclist) > 0 {
		p.Command("lopen")
		if keep {
			p.SetCurrentWindow(w)
		}
		if err := p.Wait(); err != nil {
			return err
		}
	} else {
		p.Command("redraw!")
		p.Command("lclose")
		if err := p.Wait(); err != nil {
			return err
		}
	}

	return nil
}

func CloseLoclist(v *vim.Vim) error {
	return v.Command("lclose")
}

func SetQuickfix(p *vim.Pipeline, qflist []*ErrorlistData) error {
	p.Call("setqflist", nil, 0, qflist)

	return nil
}

func OpenOuickfix(p *vim.Pipeline, w vim.Window, keep bool) error {
	p.Command("copen")
	if keep {
		p.SetCurrentWindow(w)
	}
	if err := p.Wait(); err != nil {
		return err
	}

	return nil
}

func CloseQuickfix(v *vim.Vim) error {
	return v.Command("cclose")
}

func SplitPos(pos string) (string, int, int) {
	file := strings.Split(pos, ":")
	line, _ := strconv.ParseInt(file[1], 10, 64)
	col, _ := strconv.ParseInt(file[2], 10, 64)

	return file[0], int(line), int(col)
}
<commit_msg>Remove Window nr to SetQuickFix's setqflist<commit_after>// Copyright 2016 Koichi Shiraishi. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package nvim

import (
	"strconv"
	"strings"

	"github.com/garyburd/neovim-go/vim"
)

// Loclist represents an item in a quickfix list.
type ErrorlistData struct {
	// Buffer number
	Bufnr int `msgpack:"bufnr,omitempty"`

	// Name of a file; only used when bufnr is not present or it is invalid.
	FileName string `msgpack:"filename,omitempty"`

	// Line number in the file.
	LNum int `msgpack:"lnum,omitempty"`

	// Column number (first column is 1).
	Col int `msgpack:"col,omitempty"`

	// When Vcol is != 0,  Col is visual column.
	VCol int `msgpack:"vcol,omitempty"`

	// Error number.
	Nr int `msgpack:"nr,omitempty"`

	// Search pattern used to locate the error.
	Pattern string `msgpack:"pattern,omitempty"`

	// Description of the error.
	Text string `msgpack:"text,omitempty"`

	// Single-character error type, 'E', 'W', etc.
	Type string `msgpack:"type,omitempty"`

	// Valid is non-zero if this is a recognized error message.
	Valid int `msgpack:"valid,omitempty"`
}

func SetLoclist(p *vim.Pipeline, loclist []*ErrorlistData) error {
	// setloclist({nr}, {list} [, {action}])
	// Call(fname string, result interface{}, args ...interface{})
	if len(loclist) > 0 {
		p.Call("setloclist", nil, 0, loclist)
	} else {
		p.Command("lexpr ''")
	}

	return nil
}

func OpenLoclist(p *vim.Pipeline, w vim.Window, loclist []*ErrorlistData, keep bool) error {
	if len(loclist) > 0 {
		p.Command("lopen")
		if keep {
			p.SetCurrentWindow(w)
		}
		if err := p.Wait(); err != nil {
			return err
		}
	} else {
		p.Command("redraw!")
		p.Command("lclose")
		if err := p.Wait(); err != nil {
			return err
		}
	}

	return nil
}

func CloseLoclist(v *vim.Vim) error {
	return v.Command("lclose")
}

func SetQuickfix(p *vim.Pipeline, qflist []*ErrorlistData) error {
	p.Call("setqflist", nil, qflist)

	return nil
}

func OpenOuickfix(p *vim.Pipeline, w vim.Window, keep bool) error {
	p.Command("copen")
	if keep {
		p.SetCurrentWindow(w)
	}
	if err := p.Wait(); err != nil {
		return err
	}

	return nil
}

func CloseQuickfix(v *vim.Vim) error {
	return v.Command("cclose")
}

func SplitPos(pos string) (string, int, int) {
	file := strings.Split(pos, ":")
	line, _ := strconv.ParseInt(file[1], 10, 64)
	col, _ := strconv.ParseInt(file[2], 10, 64)

	return file[0], int(line), int(col)
}
<|endoftext|>


<commit_before>package device

import (
	"fmt"

	"github.com/pkg/errors"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	pcidev "github.com/lxc/lxd/lxd/device/pci"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/ip"
	"github.com/lxc/lxd/lxd/network"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
)

type nicPhysical struct {
	deviceCommon
}

// validateConfig checks the supplied config for correctness.
func (d *nicPhysical) validateConfig(instConf instance.ConfigReader) error {
	if !instanceSupported(instConf.Type(), instancetype.Container, instancetype.VM) {
		return ErrUnsupportedDevType
	}

	requiredFields := []string{"parent"}
	optionalFields := []string{
		"name",
		"maas.subnet.ipv4",
		"maas.subnet.ipv6",
		"boot.priority",
		"gvrp",
	}

	if instConf.Type() == instancetype.Container || instConf.Type() == instancetype.Any {
		optionalFields = append(optionalFields, "mtu", "hwaddr", "vlan")
	}

	err := d.config.Validate(nicValidationRules(requiredFields, optionalFields, instConf))
	if err != nil {
		return err
	}

	return nil
}

// validateEnvironment checks the runtime environment for correctness.
func (d *nicPhysical) validateEnvironment() error {
	if d.inst.Type() == instancetype.VM && shared.IsTrue(d.inst.ExpandedConfig()["migration.stateful"]) {
		return fmt.Errorf("Network physical devices cannot be used when migration.stateful is enabled")
	}

	if d.inst.Type() == instancetype.Container && d.config["name"] == "" {
		return fmt.Errorf("Requires name property to start")
	}

	if !shared.PathExists(fmt.Sprintf("/sys/class/net/%s", d.config["parent"])) {
		return fmt.Errorf("Parent device '%s' doesn't exist", d.config["parent"])
	}

	return nil
}

// Start is run when the device is added to a running instance or instance is starting up.
func (d *nicPhysical) Start() (*deviceConfig.RunConfig, error) {
	err := d.validateEnvironment()
	if err != nil {
		return nil, err
	}

	// Lock to avoid issues with containers starting in parallel.
	networkCreateSharedDeviceLock.Lock()
	defer networkCreateSharedDeviceLock.Unlock()

	saveData := make(map[string]string)

	revert := revert.New()
	defer revert.Fail()

	// pciIOMMUGroup, used for VM physical passthrough.
	var pciIOMMUGroup uint64

	// If VM, then try and load the vfio-pci module first.
	if d.inst.Type() == instancetype.VM {
		err = util.LoadModule("vfio-pci")
		if err != nil {
			return nil, errors.Wrapf(err, "Error loading %q module", "vfio-pci")
		}
	}

	// Record the host_name device used for restoration later.
	saveData["host_name"] = network.GetHostDevice(d.config["parent"], d.config["vlan"])

	if d.inst.Type() == instancetype.Container {
		statusDev, err := networkCreateVlanDeviceIfNeeded(d.state, d.config["parent"], saveData["host_name"], d.config["vlan"], shared.IsTrue(d.config["gvrp"]))
		if err != nil {
			return nil, err
		}

		// Record whether we created this device or not so it can be removed on stop.
		saveData["last_state.created"] = fmt.Sprintf("%t", statusDev != "existing")

		if shared.IsTrue(saveData["last_state.created"]) {
			revert.Add(func() {
				networkRemoveInterfaceIfNeeded(d.state, saveData["host_name"], d.inst, d.config["parent"], d.config["vlan"])
			})
		}

		// If we didn't create the device we should track various properties so we can restore them when the
		// instance is stopped or the device is detached.
		if !shared.IsTrue(saveData["last_state.created"]) {
			err = networkSnapshotPhysicalNic(saveData["host_name"], saveData)
			if err != nil {
				return nil, err
			}
		}

		// Set the MAC address.
		if d.config["hwaddr"] != "" {
			link := &ip.Link{Name: saveData["host_name"]}
			err := link.SetAddress(d.config["hwaddr"])
			if err != nil {
				return nil, fmt.Errorf("Failed to set the MAC address: %s", err)
			}
		}

		// Set the MTU.
		if d.config["mtu"] != "" {
			err = network.InterfaceSetMTU(saveData["host_name"], d.config["mtu"])
			if err != nil {
				return nil, err
			}
		}
	} else if d.inst.Type() == instancetype.VM {
		// Get PCI information about the network interface.
		ueventPath := fmt.Sprintf("/sys/class/net/%s/device/uevent", saveData["host_name"])
		pciDev, err := pcidev.ParseUeventFile(ueventPath)
		if err != nil {
			return nil, errors.Wrapf(err, "Failed to get PCI device info for %q", saveData["host_name"])
		}

		saveData["last_state.pci.slot.name"] = pciDev.SlotName
		saveData["last_state.pci.driver"] = pciDev.Driver

		pciIOMMUGroup, err = pcidev.DeviceIOMMUGroup(saveData["last_state.pci.slot.name"])
		if err != nil {
			return nil, err
		}

		err = pcidev.DeviceDriverOverride(pciDev, "vfio-pci")
		if err != nil {
			return nil, err
		}
	}

	err = d.volatileSet(saveData)
	if err != nil {
		return nil, err
	}

	runConf := deviceConfig.RunConfig{}
	runConf.NetworkInterface = []deviceConfig.RunConfigItem{
		{Key: "type", Value: "phys"},
		{Key: "name", Value: d.config["name"]},
		{Key: "flags", Value: "up"},
		{Key: "link", Value: saveData["host_name"]},
	}

	if d.inst.Type() == instancetype.VM {
		runConf.NetworkInterface = append(runConf.NetworkInterface,
			[]deviceConfig.RunConfigItem{
				{Key: "devName", Value: d.name},
				{Key: "pciSlotName", Value: saveData["last_state.pci.slot.name"]},
				{Key: "pciIOMMUGroup", Value: fmt.Sprintf("%d", pciIOMMUGroup)},
			}...)
	}

	revert.Success()
	return &runConf, nil
}

// Stop is run when the device is removed from the instance.
func (d *nicPhysical) Stop() (*deviceConfig.RunConfig, error) {
	v := d.volatileGet()
	runConf := deviceConfig.RunConfig{
		PostHooks: []func() error{d.postStop},
		NetworkInterface: []deviceConfig.RunConfigItem{
			{Key: "link", Value: v["host_name"]},
		},
	}

	return &runConf, nil
}

// postStop is run after the device is removed from the instance.
func (d *nicPhysical) postStop() error {
	defer d.volatileSet(map[string]string{
		"host_name":                "",
		"last_state.hwaddr":        "",
		"last_state.mtu":           "",
		"last_state.created":       "",
		"last_state.pci.slot.name": "",
		"last_state.pci.driver":    "",
	})

	v := d.volatileGet()

	// If VM physical pass through, unbind from vfio-pci and bind back to host driver.
	if d.inst.Type() == instancetype.VM && v["last_state.pci.slot.name"] != "" {
		vfioDev := pcidev.Device{
			Driver:   "vfio-pci",
			SlotName: v["last_state.pci.slot.name"],
		}

		err := pcidev.DeviceDriverOverride(vfioDev, v["last_state.pci.driver"])
		if err != nil {
			return err
		}
	} else if d.inst.Type() == instancetype.Container {
		hostName := network.GetHostDevice(d.config["parent"], d.config["vlan"])

		// This will delete the parent interface if we created it for VLAN parent.
		if shared.IsTrue(v["last_state.created"]) {
			err := networkRemoveInterfaceIfNeeded(d.state, hostName, d.inst, d.config["parent"], d.config["vlan"])
			if err != nil {
				return err
			}
		} else if v["last_state.pci.slot.name"] == "" {
			err := networkRestorePhysicalNic(hostName, v)
			if err != nil {
				return err
			}
		}
	}

	return nil
}
<commit_msg>lxd/device/nic/physical: SetMTU usage<commit_after>package device

import (
	"fmt"

	"github.com/pkg/errors"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	pcidev "github.com/lxc/lxd/lxd/device/pci"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/ip"
	"github.com/lxc/lxd/lxd/network"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
)

type nicPhysical struct {
	deviceCommon
}

// validateConfig checks the supplied config for correctness.
func (d *nicPhysical) validateConfig(instConf instance.ConfigReader) error {
	if !instanceSupported(instConf.Type(), instancetype.Container, instancetype.VM) {
		return ErrUnsupportedDevType
	}

	requiredFields := []string{"parent"}
	optionalFields := []string{
		"name",
		"maas.subnet.ipv4",
		"maas.subnet.ipv6",
		"boot.priority",
		"gvrp",
	}

	if instConf.Type() == instancetype.Container || instConf.Type() == instancetype.Any {
		optionalFields = append(optionalFields, "mtu", "hwaddr", "vlan")
	}

	err := d.config.Validate(nicValidationRules(requiredFields, optionalFields, instConf))
	if err != nil {
		return err
	}

	return nil
}

// validateEnvironment checks the runtime environment for correctness.
func (d *nicPhysical) validateEnvironment() error {
	if d.inst.Type() == instancetype.VM && shared.IsTrue(d.inst.ExpandedConfig()["migration.stateful"]) {
		return fmt.Errorf("Network physical devices cannot be used when migration.stateful is enabled")
	}

	if d.inst.Type() == instancetype.Container && d.config["name"] == "" {
		return fmt.Errorf("Requires name property to start")
	}

	if !shared.PathExists(fmt.Sprintf("/sys/class/net/%s", d.config["parent"])) {
		return fmt.Errorf("Parent device '%s' doesn't exist", d.config["parent"])
	}

	return nil
}

// Start is run when the device is added to a running instance or instance is starting up.
func (d *nicPhysical) Start() (*deviceConfig.RunConfig, error) {
	err := d.validateEnvironment()
	if err != nil {
		return nil, err
	}

	// Lock to avoid issues with containers starting in parallel.
	networkCreateSharedDeviceLock.Lock()
	defer networkCreateSharedDeviceLock.Unlock()

	saveData := make(map[string]string)

	revert := revert.New()
	defer revert.Fail()

	// pciIOMMUGroup, used for VM physical passthrough.
	var pciIOMMUGroup uint64

	// If VM, then try and load the vfio-pci module first.
	if d.inst.Type() == instancetype.VM {
		err = util.LoadModule("vfio-pci")
		if err != nil {
			return nil, errors.Wrapf(err, "Error loading %q module", "vfio-pci")
		}
	}

	// Record the host_name device used for restoration later.
	saveData["host_name"] = network.GetHostDevice(d.config["parent"], d.config["vlan"])

	if d.inst.Type() == instancetype.Container {
		statusDev, err := networkCreateVlanDeviceIfNeeded(d.state, d.config["parent"], saveData["host_name"], d.config["vlan"], shared.IsTrue(d.config["gvrp"]))
		if err != nil {
			return nil, err
		}

		// Record whether we created this device or not so it can be removed on stop.
		saveData["last_state.created"] = fmt.Sprintf("%t", statusDev != "existing")

		if shared.IsTrue(saveData["last_state.created"]) {
			revert.Add(func() {
				networkRemoveInterfaceIfNeeded(d.state, saveData["host_name"], d.inst, d.config["parent"], d.config["vlan"])
			})
		}

		// If we didn't create the device we should track various properties so we can restore them when the
		// instance is stopped or the device is detached.
		if !shared.IsTrue(saveData["last_state.created"]) {
			err = networkSnapshotPhysicalNic(saveData["host_name"], saveData)
			if err != nil {
				return nil, err
			}
		}

		// Set the MAC address.
		if d.config["hwaddr"] != "" {
			link := &ip.Link{Name: saveData["host_name"]}
			err := link.SetAddress(d.config["hwaddr"])
			if err != nil {
				return nil, fmt.Errorf("Failed to set the MAC address: %s", err)
			}
		}

		// Set the MTU.
		if d.config["mtu"] != "" {
			link := &ip.Link{Name: saveData["host_name"]}
			err := link.SetMTU(d.config["mtu"])
			if err != nil {
				return nil, errors.Wrapf(err, "Failed setting MTU %q on %q", d.config["mtu"], saveData["host_name"])
			}
		}
	} else if d.inst.Type() == instancetype.VM {
		// Get PCI information about the network interface.
		ueventPath := fmt.Sprintf("/sys/class/net/%s/device/uevent", saveData["host_name"])
		pciDev, err := pcidev.ParseUeventFile(ueventPath)
		if err != nil {
			return nil, errors.Wrapf(err, "Failed to get PCI device info for %q", saveData["host_name"])
		}

		saveData["last_state.pci.slot.name"] = pciDev.SlotName
		saveData["last_state.pci.driver"] = pciDev.Driver

		pciIOMMUGroup, err = pcidev.DeviceIOMMUGroup(saveData["last_state.pci.slot.name"])
		if err != nil {
			return nil, err
		}

		err = pcidev.DeviceDriverOverride(pciDev, "vfio-pci")
		if err != nil {
			return nil, err
		}
	}

	err = d.volatileSet(saveData)
	if err != nil {
		return nil, err
	}

	runConf := deviceConfig.RunConfig{}
	runConf.NetworkInterface = []deviceConfig.RunConfigItem{
		{Key: "type", Value: "phys"},
		{Key: "name", Value: d.config["name"]},
		{Key: "flags", Value: "up"},
		{Key: "link", Value: saveData["host_name"]},
	}

	if d.inst.Type() == instancetype.VM {
		runConf.NetworkInterface = append(runConf.NetworkInterface,
			[]deviceConfig.RunConfigItem{
				{Key: "devName", Value: d.name},
				{Key: "pciSlotName", Value: saveData["last_state.pci.slot.name"]},
				{Key: "pciIOMMUGroup", Value: fmt.Sprintf("%d", pciIOMMUGroup)},
			}...)
	}

	revert.Success()
	return &runConf, nil
}

// Stop is run when the device is removed from the instance.
func (d *nicPhysical) Stop() (*deviceConfig.RunConfig, error) {
	v := d.volatileGet()
	runConf := deviceConfig.RunConfig{
		PostHooks: []func() error{d.postStop},
		NetworkInterface: []deviceConfig.RunConfigItem{
			{Key: "link", Value: v["host_name"]},
		},
	}

	return &runConf, nil
}

// postStop is run after the device is removed from the instance.
func (d *nicPhysical) postStop() error {
	defer d.volatileSet(map[string]string{
		"host_name":                "",
		"last_state.hwaddr":        "",
		"last_state.mtu":           "",
		"last_state.created":       "",
		"last_state.pci.slot.name": "",
		"last_state.pci.driver":    "",
	})

	v := d.volatileGet()

	// If VM physical pass through, unbind from vfio-pci and bind back to host driver.
	if d.inst.Type() == instancetype.VM && v["last_state.pci.slot.name"] != "" {
		vfioDev := pcidev.Device{
			Driver:   "vfio-pci",
			SlotName: v["last_state.pci.slot.name"],
		}

		err := pcidev.DeviceDriverOverride(vfioDev, v["last_state.pci.driver"])
		if err != nil {
			return err
		}
	} else if d.inst.Type() == instancetype.Container {
		hostName := network.GetHostDevice(d.config["parent"], d.config["vlan"])

		// This will delete the parent interface if we created it for VLAN parent.
		if shared.IsTrue(v["last_state.created"]) {
			err := networkRemoveInterfaceIfNeeded(d.state, hostName, d.inst, d.config["parent"], d.config["vlan"])
			if err != nil {
				return err
			}
		} else if v["last_state.pci.slot.name"] == "" {
			err := networkRestorePhysicalNic(hostName, v)
			if err != nil {
				return err
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Package wstest provides a NewDialer function to test just the
// `http.Handler` that upgrades the connection to a websocket session.
// It runs the handler function in a goroutine without listening on
// any port. The returned `websocket.Dialer` then can be used to dial
// and communicate with the given handler.

package wstest

import (
	"bufio"
	"net"
	"net/http"
	"net/http/httptest"

	"github.com/gorilla/websocket"
	"github.com/posener/wstest/pipe"
)

type dialer struct {
	httptest.ResponseRecorder
	client net.Conn
	server net.Conn
}

// NewDialer creates a wstest dialer to an http.Handler which accepts websocket upgrades.
// This send an HTTP request to the http.Handler, and wait for the connection upgrade response.
// it runs the dialer's ServeHTTP function in a goroutine, so dialer can communicate with a
// client running on the current program flow
//
// h is an http.Handler that handles websocket connections.
// debugLog is a function for a log.Println-like function for printing everything that
// is passed over the connection. Can be set to nil if no logs are needed.
// It returns a *websocket.Dial struct, which can then be used to dial to the handler.
func NewDialer(h http.Handler, debugLog pipe.Println) *websocket.Dialer {
	c1, c2 := pipe.New(debugLog)
	conn := &dialer{client: c1, server: c2}

	// run the runServer in a goroutine, so when the Dial send the request to
	// the dialer on the connection, it will be parsed as an HTTPRequest and
	// sent to the Handler function.
	go conn.runServer(h)

	// use the websocket.NewDialer.Dial with the fake net.dialer to communicate with the dialer
	// the dialer gets the client which is the client side of the connection
	return &websocket.Dialer{NetDial: func(network, addr string) (net.Conn, error) { return conn.client, nil }}
}

// runServer reads the request sent on the connection to the dialer
// from the websocket.NewDialer.Dial function, and pass it to the dialer.
// once this is done, the communication is done on the wsConn
func (d *dialer) runServer(h http.Handler) {
	// read from the dialer connection the request sent by the dialer.Dial,
	// and use the handler to serve this request.
	req, err := http.ReadRequest(bufio.NewReader(d.server))
	if err != nil {
		return
	}
	h.ServeHTTP(d, req)
}

// Hijack the connection
func (d *dialer) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	// return to the dialer the dialer, which is the dialer side of the connection
	rw := bufio.NewReadWriter(bufio.NewReader(d.server), bufio.NewWriter(d.server))
	return d.server, rw, nil
}

// WriteHeader write HTTP header to the client and closes the connection
func (d *dialer) WriteHeader(code int) {
	r := http.Response{StatusCode: code}
	r.Write(d.server)
	d.server.Close()
}
<commit_msg>fix lint<commit_after>// Package wstest provides a NewDialer function to test just the
// `http.Handler` that upgrades the connection to a websocket session.
// It runs the handler function in a goroutine without listening on
// any port. The returned `websocket.Dialer` then can be used to dial
// and communicate with the given handler.
package wstest

import (
	"bufio"
	"net"
	"net/http"
	"net/http/httptest"

	"github.com/gorilla/websocket"
	"github.com/posener/wstest/pipe"
)

type dialer struct {
	httptest.ResponseRecorder
	client net.Conn
	server net.Conn
}

// NewDialer creates a wstest dialer to an http.Handler which accepts websocket upgrades.
// This send an HTTP request to the http.Handler, and wait for the connection upgrade response.
// it runs the dialer's ServeHTTP function in a goroutine, so dialer can communicate with a
// client running on the current program flow
//
// h is an http.Handler that handles websocket connections.
// debugLog is a function for a log.Println-like function for printing everything that
// is passed over the connection. Can be set to nil if no logs are needed.
// It returns a *websocket.Dial struct, which can then be used to dial to the handler.
func NewDialer(h http.Handler, debugLog pipe.Println) *websocket.Dialer {
	c1, c2 := pipe.New(debugLog)
	conn := &dialer{client: c1, server: c2}

	// run the runServer in a goroutine, so when the Dial send the request to
	// the dialer on the connection, it will be parsed as an HTTPRequest and
	// sent to the Handler function.
	go conn.runServer(h)

	// use the websocket.NewDialer.Dial with the fake net.dialer to communicate with the dialer
	// the dialer gets the client which is the client side of the connection
	return &websocket.Dialer{NetDial: func(network, addr string) (net.Conn, error) { return conn.client, nil }}
}

// runServer reads the request sent on the connection to the dialer
// from the websocket.NewDialer.Dial function, and pass it to the dialer.
// once this is done, the communication is done on the wsConn
func (d *dialer) runServer(h http.Handler) {
	// read from the dialer connection the request sent by the dialer.Dial,
	// and use the handler to serve this request.
	req, err := http.ReadRequest(bufio.NewReader(d.server))
	if err != nil {
		return
	}
	h.ServeHTTP(d, req)
}

// Hijack the connection
func (d *dialer) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	// return to the dialer the dialer, which is the dialer side of the connection
	rw := bufio.NewReadWriter(bufio.NewReader(d.server), bufio.NewWriter(d.server))
	return d.server, rw, nil
}

// WriteHeader write HTTP header to the client and closes the connection
func (d *dialer) WriteHeader(code int) {
	r := http.Response{StatusCode: code}
	r.Write(d.server)
	d.server.Close()
}
<|endoftext|>