<commit_before>// +build linux,experimental

package plugin

import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/libcontainerd"
	"github.com/docker/docker/oci"
	"github.com/docker/docker/pkg/plugins"
	"github.com/docker/docker/pkg/system"
	"github.com/docker/docker/restartmanager"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/container"
	"github.com/opencontainers/specs/specs-go"
)

func (pm *Manager) enable(p *plugin, force bool) error {
	if p.PluginObj.Active && !force {
		return fmt.Errorf("plugin %s is already enabled", p.Name())
	}
	spec, err := pm.initSpec(p)
	if err != nil {
		return err
	}

	p.restartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
	if err := pm.containerdClient.Create(p.PluginObj.ID, libcontainerd.Spec(*spec), libcontainerd.WithRestartManager(p.restartManager)); err != nil { // POC-only
		if err := p.restartManager.Cancel(); err != nil {
			logrus.Errorf("enable: restartManager.Cancel failed due to %v", err)
		}
		return err
	}

	socket := p.PluginObj.Manifest.Interface.Socket
	p.client, err = plugins.NewClient("unix://"+filepath.Join(p.runtimeSourcePath, socket), nil)
	if err != nil {
		if err := p.restartManager.Cancel(); err != nil {
			logrus.Errorf("enable: restartManager.Cancel failed due to %v", err)
		}
		return err
	}

	pm.Lock() // fixme: lock single record
	p.PluginObj.Active = true
	pm.save()
	pm.Unlock()

	for _, typ := range p.PluginObj.Manifest.Interface.Types {
		if handler := pm.handlers[typ.String()]; handler != nil {
			handler(p.Name(), p.Client())
		}
	}

	return nil
}

func (pm *Manager) restore(p *plugin) error {
	p.restartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
	return pm.containerdClient.Restore(p.PluginObj.ID, libcontainerd.WithRestartManager(p.restartManager))
}

func (pm *Manager) initSpec(p *plugin) (*specs.Spec, error) {
	s := oci.DefaultSpec()

	rootfs := filepath.Join(pm.libRoot, p.PluginObj.ID, "rootfs")
	s.Root = specs.Root{
		Path:     rootfs,
		Readonly: false, // TODO: all plugins should be readonly? settable in manifest?
	}

	mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
		Source:      &p.runtimeSourcePath,
		Destination: defaultPluginRuntimeDestination,
		Type:        "bind",
		Options:     []string{"rbind", "rshared"},
	})
	for _, mount := range mounts {
		m := specs.Mount{
			Destination: mount.Destination,
			Type:        mount.Type,
			Options:     mount.Options,
		}
		// TODO: if nil, then it's required and user didn't set it
		if mount.Source != nil {
			m.Source = *mount.Source
		}
		if m.Source != "" && m.Type == "bind" {
			fi, err := os.Lstat(filepath.Join(rootfs, string(os.PathSeparator), m.Destination)) // TODO: followsymlinks
			if err != nil {
				return nil, err
			}
			if fi.IsDir() {
				if err := os.MkdirAll(m.Source, 0700); err != nil {
					return nil, err
				}
			}
		}
		s.Mounts = append(s.Mounts, m)
	}

	envs := make([]string, 1, len(p.PluginObj.Config.Env)+1)
	envs[0] = "PATH=" + system.DefaultPathEnv
	envs = append(envs, p.PluginObj.Config.Env...)

	args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...)
	cwd := p.PluginObj.Manifest.Workdir
	if len(cwd) == 0 {
		cwd = "/"
	}
	s.Process = specs.Process{
		Terminal: false,
		Args:     args,
		Cwd:      cwd,
		Env:      envs,
	}

	return &s, nil
}

func (pm *Manager) disable(p *plugin) error {
	if !p.PluginObj.Active {
		return fmt.Errorf("plugin %s is already disabled", p.Name())
	}
	if err := p.restartManager.Cancel(); err != nil {
		logrus.Error(err)
	}
	if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
		logrus.Error(err)
	}
	os.RemoveAll(p.runtimeSourcePath)
	pm.Lock() // fixme: lock single record
	defer pm.Unlock()
	p.PluginObj.Active = false
	pm.save()
	return nil
}

// Shutdown stops all plugins and called during daemon shutdown.
func (pm *Manager) Shutdown() {
	pm.RLock()
	defer pm.RUnlock()

	pm.shutdown = true
	for _, p := range pm.plugins {
		if pm.liveRestore && p.PluginObj.Active {
			logrus.Debug("Plugin active when liveRestore is set, skipping shutdown")
			continue
		}
		if p.restartManager != nil {
			if err := p.restartManager.Cancel(); err != nil {
				logrus.Error(err)
			}
		}
		if pm.containerdClient != nil && p.PluginObj.Active {
			p.exitChan = make(chan bool)
			err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGTERM))
			if err != nil {
				logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
			} else {
				select {
				case <-p.exitChan:
					logrus.Debug("Clean shutdown of plugin")
				case <-time.After(time.Second * 10):
					logrus.Debug("Force shutdown plugin")
					if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
						logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
					}
				}
			}
			close(p.exitChan)
		}
		if err := os.RemoveAll(p.runtimeSourcePath); err != nil {
			logrus.Errorf("Remove plugin runtime failed with error: %v", err)
		}
	}
}
<commit_msg>Debugging issue #25511<commit_after>// +build linux,experimental

package plugin

import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/libcontainerd"
	"github.com/docker/docker/oci"
	"github.com/docker/docker/pkg/plugins"
	"github.com/docker/docker/pkg/system"
	"github.com/docker/docker/restartmanager"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/container"
	"github.com/opencontainers/specs/specs-go"
)

func (pm *Manager) enable(p *plugin, force bool) error {
	if p.PluginObj.Active && !force {
		return fmt.Errorf("plugin %s is already enabled", p.Name())
	}
	spec, err := pm.initSpec(p)
	if err != nil {
		return err
	}

	p.restartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
	if err := pm.containerdClient.Create(p.PluginObj.ID, libcontainerd.Spec(*spec), libcontainerd.WithRestartManager(p.restartManager)); err != nil { // POC-only
		if err := p.restartManager.Cancel(); err != nil {
			logrus.Errorf("enable: restartManager.Cancel failed due to %v", err)
		}
		return err
	}

	socket := p.PluginObj.Manifest.Interface.Socket
	p.client, err = plugins.NewClient("unix://"+filepath.Join(p.runtimeSourcePath, socket), nil)
	if err != nil {
		if err := p.restartManager.Cancel(); err != nil {
			logrus.Errorf("enable: restartManager.Cancel failed due to %v", err)
		}
		return err
	}

	pm.Lock() // fixme: lock single record
	p.PluginObj.Active = true
	pm.save()
	pm.Unlock()

	for _, typ := range p.PluginObj.Manifest.Interface.Types {
		if handler := pm.handlers[typ.String()]; handler != nil {
			handler(p.Name(), p.Client())
		}
	}

	return nil
}

func (pm *Manager) restore(p *plugin) error {
	p.restartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
	return pm.containerdClient.Restore(p.PluginObj.ID, libcontainerd.WithRestartManager(p.restartManager))
}

func (pm *Manager) initSpec(p *plugin) (*specs.Spec, error) {
	s := oci.DefaultSpec()

	rootfs := filepath.Join(pm.libRoot, p.PluginObj.ID, "rootfs")
	s.Root = specs.Root{
		Path:     rootfs,
		Readonly: false, // TODO: all plugins should be readonly? settable in manifest?
	}

	mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
		Source:      &p.runtimeSourcePath,
		Destination: defaultPluginRuntimeDestination,
		Type:        "bind",
		Options:     []string{"rbind", "rshared"},
	})
	for _, mount := range mounts {
		m := specs.Mount{
			Destination: mount.Destination,
			Type:        mount.Type,
			Options:     mount.Options,
		}
		// TODO: if nil, then it's required and user didn't set it
		if mount.Source != nil {
			m.Source = *mount.Source
		}

		if m.Source != "" && m.Type == "bind" {
			/* Debugging issue #25511: Volumes and other content created under the
			bind mount should be recursively propagated. rshared, not shared.
			This could be the reason for EBUSY during removal. Override options
			with rbind, rshared and see if CI errors are fixed. */
			m.Options = []string{"rbind", "rshared"}
			fi, err := os.Lstat(filepath.Join(rootfs, string(os.PathSeparator), m.Destination)) // TODO: followsymlinks
			if err != nil {
				return nil, err
			}
			if fi.IsDir() {
				if err := os.MkdirAll(m.Source, 0700); err != nil {
					return nil, err
				}
			}
		}
		s.Mounts = append(s.Mounts, m)
	}

	envs := make([]string, 1, len(p.PluginObj.Config.Env)+1)
	envs[0] = "PATH=" + system.DefaultPathEnv
	envs = append(envs, p.PluginObj.Config.Env...)

	args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...)
	cwd := p.PluginObj.Manifest.Workdir
	if len(cwd) == 0 {
		cwd = "/"
	}
	s.Process = specs.Process{
		Terminal: false,
		Args:     args,
		Cwd:      cwd,
		Env:      envs,
	}

	return &s, nil
}

func (pm *Manager) disable(p *plugin) error {
	if !p.PluginObj.Active {
		return fmt.Errorf("plugin %s is already disabled", p.Name())
	}
	if err := p.restartManager.Cancel(); err != nil {
		logrus.Error(err)
	}
	if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
		logrus.Error(err)
	}
	os.RemoveAll(p.runtimeSourcePath)
	pm.Lock() // fixme: lock single record
	defer pm.Unlock()
	p.PluginObj.Active = false
	pm.save()
	return nil
}

// Shutdown stops all plugins and called during daemon shutdown.
func (pm *Manager) Shutdown() {
	pm.RLock()
	defer pm.RUnlock()

	pm.shutdown = true
	for _, p := range pm.plugins {
		if pm.liveRestore && p.PluginObj.Active {
			logrus.Debug("Plugin active when liveRestore is set, skipping shutdown")
			continue
		}
		if p.restartManager != nil {
			if err := p.restartManager.Cancel(); err != nil {
				logrus.Error(err)
			}
		}
		if pm.containerdClient != nil && p.PluginObj.Active {
			p.exitChan = make(chan bool)
			err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGTERM))
			if err != nil {
				logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
			} else {
				select {
				case <-p.exitChan:
					logrus.Debug("Clean shutdown of plugin")
				case <-time.After(time.Second * 10):
					logrus.Debug("Force shutdown plugin")
					if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
						logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
					}
				}
			}
			close(p.exitChan)
		}
		if err := os.RemoveAll(p.runtimeSourcePath); err != nil {
			logrus.Errorf("Remove plugin runtime failed with error: %v", err)
		}
	}
}
<|endoftext|>


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

package vector

export Vector, New;

/*
	import vector "vector"
	v := vector.New();
	v.Insert(0, new(Foo));
	v.Append(new(Foo));
	v.Delete(0);
	for i := 0; i < v.Len(); i++ { f(v.At(i)); }
*/

type Element interface {
}

type Vector struct {
	nalloc int;
	nelem int;
	elem *[]Element;
}

// BUG: workaround for non-constant allocation.
// i must be a power of 10.
func Alloc(i int) *[]Element {
	print "Alloc ", i, "\n";
	switch i {
	case 1:
		return new([1]Element);
	case 10:
		return new([10]Element);
	case 100:
		return new([100]Element);
	case 1000:
		return new([1000]Element);
	}
	print "bad size ", i, "\n";
	panic "not known size\n";
}

func is_pow10(i int) bool {
	switch i {
	case 1, 10, 100, 1000:
		return true;
	}
	return false;
}

func New() *Vector {
	v := new(Vector);
	v.nelem = 0;
	v.nalloc = 1;
	v.elem = Alloc(v.nalloc);
	return v;
}

func (v *Vector) Len() int {
	return v.nelem;
}

func (v *Vector) At(i int) Element {
	if i < 0 || i >= v.nelem {
		//return nil;  // BUG
		panic "At out of range\n";
	}
	return v.elem[i];
}

func (v *Vector) Delete(i int) {
	if i < 0 || i >= v.nelem {
		panic "Delete out of range\n";
	}
	for j := i+1; j < v.nelem; j++ {
		v.elem[j-1] = v.elem[j];
	}
	v.nelem--;
	v.elem[v.nelem] = nil;
}

func (v *Vector) Insert(i int, e Element) {
	if i > v.nelem {
		panic "Del too large\n";
	}
	if v.nelem == v.nalloc && is_pow10(v.nalloc) {
		n := Alloc(v.nalloc * 10);
		for j := 0; j < v.nalloc; j++ {
			n[j] = v.elem[j];
		}
		v.elem = n;
		v.nalloc *= 10;
	}
	// make a hole
	for j := v.nelem; j > i; j-- {
		v.elem[j] = v.elem[j-1];
	}
	v.elem[i] = e;
	v.nelem++;
}

func (v *Vector) Append(e Element) {
	v.Insert(v.nelem, e);
}

/*
type I struct { val int; };  // BUG: can't be local;

func Test() {
	i0 := new(I); i0.val = 0;
	i1 := new(I); i1.val = 11;
	i2 := new(I); i2.val = 222;
	i3 := new(I); i3.val = 3333;
	i4 := new(I); i4.val = 44444;
	v := New();
	print "hi\n";
	v.Insert(0, i4);
	v.Insert(0, i3);
	v.Insert(0, i2);
	v.Insert(0, i1);
	v.Insert(0, i0);
	for i := 0; i < v.Len(); i++ {
		var x *I;
		x = v.At(i);
		print i, " ", x.val, "\n";  // BUG: can't use I(v.At(i))
	}
}

export Test;
*/
<commit_msg>remove noisy print<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package vector

export Vector, New;

/*
	import vector "vector"
	v := vector.New();
	v.Insert(0, new(Foo));
	v.Append(new(Foo));
	v.Delete(0);
	for i := 0; i < v.Len(); i++ { f(v.At(i)); }
*/

type Element interface {
}

type Vector struct {
	nalloc int;
	nelem int;
	elem *[]Element;
}

// BUG: workaround for non-constant allocation.
// i must be a power of 10.
func Alloc(i int) *[]Element {
	switch i {
	case 1:
		return new([1]Element);
	case 10:
		return new([10]Element);
	case 100:
		return new([100]Element);
	case 1000:
		return new([1000]Element);
	}
	print "bad size ", i, "\n";
	panic "not known size\n";
}

func is_pow10(i int) bool {
	switch i {
	case 1, 10, 100, 1000:
		return true;
	}
	return false;
}

func New() *Vector {
	v := new(Vector);
	v.nelem = 0;
	v.nalloc = 1;
	v.elem = Alloc(v.nalloc);
	return v;
}

func (v *Vector) Len() int {
	return v.nelem;
}

func (v *Vector) At(i int) Element {
	if i < 0 || i >= v.nelem {
		//return nil;  // BUG
		panic "At out of range\n";
	}
	return v.elem[i];
}

func (v *Vector) Delete(i int) {
	if i < 0 || i >= v.nelem {
		panic "Delete out of range\n";
	}
	for j := i+1; j < v.nelem; j++ {
		v.elem[j-1] = v.elem[j];
	}
	v.nelem--;
	v.elem[v.nelem] = nil;
}

func (v *Vector) Insert(i int, e Element) {
	if i > v.nelem {
		panic "Del too large\n";
	}
	if v.nelem == v.nalloc && is_pow10(v.nalloc) {
		n := Alloc(v.nalloc * 10);
		for j := 0; j < v.nalloc; j++ {
			n[j] = v.elem[j];
		}
		v.elem = n;
		v.nalloc *= 10;
	}
	// make a hole
	for j := v.nelem; j > i; j-- {
		v.elem[j] = v.elem[j-1];
	}
	v.elem[i] = e;
	v.nelem++;
}

func (v *Vector) Append(e Element) {
	v.Insert(v.nelem, e);
}

/*
type I struct { val int; };  // BUG: can't be local;

func Test() {
	i0 := new(I); i0.val = 0;
	i1 := new(I); i1.val = 11;
	i2 := new(I); i2.val = 222;
	i3 := new(I); i3.val = 3333;
	i4 := new(I); i4.val = 44444;
	v := New();
	print "hi\n";
	v.Insert(0, i4);
	v.Insert(0, i3);
	v.Insert(0, i2);
	v.Insert(0, i1);
	v.Insert(0, i0);
	for i := 0; i < v.Len(); i++ {
		var x *I;
		x = v.At(i);
		print i, " ", x.val, "\n";  // BUG: can't use I(v.At(i))
	}
}

export Test;
*/
<|endoftext|>


<commit_before>package libnetwork

import (
	"container/heap"
	"encoding/json"
	"fmt"
	"net"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/datastore"
	"github.com/docker/libnetwork/netlabel"
	"github.com/docker/libnetwork/types"
)

// Endpoint represents a logical connection between a network and a sandbox.
type Endpoint interface {
	// A system generated id for this endpoint.
	ID() string

	// Name returns the name of this endpoint.
	Name() string

	// Network returns the name of the network to which this endpoint is attached.
	Network() string

	// Join joins the sandbox to the endpoint and populates into the sandbox
	// the network resources allocated for the endpoint.
	Join(sandbox Sandbox, options ...EndpointOption) error

	// Leave detaches the network resources populated in the sandbox.
	Leave(sandbox Sandbox, options ...EndpointOption) error

	// Return certain operational data belonging to this endpoint
	Info() EndpointInfo

	// DriverInfo returns a collection of driver operational data related to this endpoint retrieved from the driver
	DriverInfo() (map[string]interface{}, error)

	// Delete and detaches this endpoint from the network.
	Delete() error
}

// EndpointOption is a option setter function type used to pass varios options to Network
// and Endpoint interfaces methods. The various setter functions of type EndpointOption are
// provided by libnetwork, they look like <Create|Join|Leave>Option[...](...)
type EndpointOption func(ep *endpoint)

type endpoint struct {
	name          string
	id            string
	network       *network
	iface         *endpointInterface
	joinInfo      *endpointJoinInfo
	sandboxID     string
	exposedPorts  []types.TransportPort
	generic       map[string]interface{}
	joinLeaveDone chan struct{}
	dbIndex       uint64
	dbExists      bool
	sync.Mutex
}

func (ep *endpoint) MarshalJSON() ([]byte, error) {
	ep.Lock()
	defer ep.Unlock()

	epMap := make(map[string]interface{})
	epMap["name"] = ep.name
	epMap["id"] = ep.id
	epMap["ep_iface"] = ep.iface
	epMap["exposed_ports"] = ep.exposedPorts
	epMap["generic"] = ep.generic
	epMap["sandbox"] = ep.sandboxID
	return json.Marshal(epMap)
}

func (ep *endpoint) UnmarshalJSON(b []byte) (err error) {
	ep.Lock()
	defer ep.Unlock()

	var epMap map[string]interface{}
	if err := json.Unmarshal(b, &epMap); err != nil {
		return err
	}
	ep.name = epMap["name"].(string)
	ep.id = epMap["id"].(string)

	ib, _ := json.Marshal(epMap["ep_iface"])
	json.Unmarshal(ib, ep.iface)

	tb, _ := json.Marshal(epMap["exposed_ports"])
	var tPorts []types.TransportPort
	json.Unmarshal(tb, &tPorts)
	ep.exposedPorts = tPorts

	cb, _ := json.Marshal(epMap["sandbox"])
	json.Unmarshal(cb, &ep.sandboxID)

	if epMap["generic"] != nil {
		ep.generic = epMap["generic"].(map[string]interface{})
	}
	return nil
}

func (ep *endpoint) ID() string {
	ep.Lock()
	defer ep.Unlock()

	return ep.id
}

func (ep *endpoint) Name() string {
	ep.Lock()
	defer ep.Unlock()

	return ep.name
}

func (ep *endpoint) Network() string {
	return ep.getNetwork().name
}

// endpoint Key structure : endpoint/network-id/endpoint-id
func (ep *endpoint) Key() []string {
	return []string{datastore.EndpointKeyPrefix, ep.getNetwork().id, ep.id}
}

func (ep *endpoint) KeyPrefix() []string {
	return []string{datastore.EndpointKeyPrefix, ep.getNetwork().id}
}

func (ep *endpoint) networkIDFromKey(key []string) (string, error) {
	// endpoint Key structure : endpoint/network-id/endpoint-id
	// it's an invalid key if the key doesn't have all the 3 key elements above
	if key == nil || len(key) < 3 || key[0] != datastore.EndpointKeyPrefix {
		return "", fmt.Errorf("invalid endpoint key : %v", key)
	}

	// network-id is placed at index=1. pls refer to endpoint.Key() method
	return key[1], nil
}

func (ep *endpoint) Value() []byte {
	b, err := json.Marshal(ep)
	if err != nil {
		return nil
	}
	return b
}

func (ep *endpoint) SetValue(value []byte) error {
	return json.Unmarshal(value, ep)
}

func (ep *endpoint) Index() uint64 {
	ep.Lock()
	defer ep.Unlock()
	return ep.dbIndex
}

func (ep *endpoint) SetIndex(index uint64) {
	ep.Lock()
	defer ep.Unlock()
	ep.dbIndex = index
	ep.dbExists = true
}

func (ep *endpoint) Exists() bool {
	ep.Lock()
	defer ep.Unlock()
	return ep.dbExists
}

func (ep *endpoint) processOptions(options ...EndpointOption) {
	ep.Lock()
	defer ep.Unlock()

	for _, opt := range options {
		if opt != nil {
			opt(ep)
		}
	}
}

// joinLeaveStart waits to ensure there are no joins or leaves in progress and
// marks this join/leave in progress without race
func (ep *endpoint) joinLeaveStart() {
	ep.Lock()
	defer ep.Unlock()

	for ep.joinLeaveDone != nil {
		joinLeaveDone := ep.joinLeaveDone
		ep.Unlock()

		select {
		case <-joinLeaveDone:
		}

		ep.Lock()
	}

	ep.joinLeaveDone = make(chan struct{})
}

// joinLeaveEnd marks the end of this join/leave operation and
// signals the same without race to other join and leave waiters
func (ep *endpoint) joinLeaveEnd() {
	ep.Lock()
	defer ep.Unlock()

	if ep.joinLeaveDone != nil {
		close(ep.joinLeaveDone)
		ep.joinLeaveDone = nil
	}
}

func (ep *endpoint) Join(sbox Sandbox, options ...EndpointOption) error {
	var err error

	if sbox == nil {
		return types.BadRequestErrorf("endpoint cannot be joined by nil container")
	}

	sb, ok := sbox.(*sandbox)
	if !ok {
		return types.BadRequestErrorf("not a valid Sandbox interface")
	}

	ep.joinLeaveStart()
	defer ep.joinLeaveEnd()

	ep.Lock()
	if ep.sandboxID != "" {
		ep.Unlock()
		return types.ForbiddenErrorf("a sandbox has already joined the endpoint")
	}

	ep.sandboxID = sbox.ID()
	ep.joinInfo = &endpointJoinInfo{}
	network := ep.network
	epid := ep.id
	ep.Unlock()
	defer func() {
		if err != nil {
			ep.Lock()
			ep.sandboxID = ""
			ep.Unlock()
		}
	}()

	network.Lock()
	driver := network.driver
	nid := network.id
	network.Unlock()

	ep.processOptions(options...)

	err = driver.Join(nid, epid, sbox.Key(), ep, sbox.Labels())
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			// Do not alter global err variable, it's needed by the previous defer
			if err := driver.Leave(nid, epid); err != nil {
				log.Warnf("driver leave failed while rolling back join: %v", err)
			}
		}
	}()

	address := ""
	if ip := ep.getFirstInterfaceAddress(); ip != nil {
		address = ip.String()
	}
	if err = sb.updateHostsFile(address, network.getSvcRecords()); err != nil {
		return err
	}

	if err = sb.updateDNS(ep.getNetwork().enableIPv6); err != nil {
		return err
	}

	if err = network.ctrlr.updateEndpointToStore(ep); err != nil {
		return err
	}

	sb.Lock()
	heap.Push(&sb.endpoints, ep)
	sb.Unlock()
	defer func() {
		if err != nil {
			for i, e := range sb.getConnectedEndpoints() {
				if e == ep {
					sb.Lock()
					heap.Remove(&sb.endpoints, i)
					sb.Unlock()
					return
				}
			}
		}
	}()

	if err = sb.populateNetworkResources(ep); err != nil {
		return err
	}
	return nil
}

func (ep *endpoint) hasInterface(iName string) bool {
	ep.Lock()
	defer ep.Unlock()

	return ep.iface != nil && ep.iface.srcName == iName
}

func (ep *endpoint) Leave(sbox Sandbox, options ...EndpointOption) error {
	ep.joinLeaveStart()
	defer ep.joinLeaveEnd()

	if sbox == nil || sbox.ID() == "" || sbox.Key() == "" {
		return types.BadRequestErrorf("invalid Sandbox passed to enpoint leave: %v", sbox)
	}

	sb, ok := sbox.(*sandbox)
	if !ok {
		return types.BadRequestErrorf("not a valid Sandbox interface")
	}

	ep.Lock()
	sid := ep.sandboxID
	ep.Unlock()

	if sid == "" {
		return types.ForbiddenErrorf("cannot leave endpoint with no attached sandbox")
	}
	if sid != sbox.ID() {
		return types.ForbiddenErrorf("unexpected sandbox ID in leave request. Expected %s. Got %s", ep.sandboxID, sbox.ID())
	}

	ep.processOptions(options...)

	ep.Lock()
	ep.sandboxID = ""
	n := ep.network
	ep.Unlock()

	n.Lock()
	c := n.ctrlr
	d := n.driver
	n.Unlock()

	if err := c.updateEndpointToStore(ep); err != nil {
		ep.Lock()
		ep.sandboxID = sid
		ep.Unlock()
		return err
	}

	if err := d.Leave(n.id, ep.id); err != nil {
		return err
	}

	return sb.clearNetworkResources(ep)
}

func (ep *endpoint) Delete() error {
	var err error
	ep.Lock()
	epid := ep.id
	name := ep.name
	n := ep.network
	if ep.sandboxID != "" {
		ep.Unlock()
		return &ActiveContainerError{name: name, id: epid}
	}
	n.Lock()
	ctrlr := n.ctrlr
	n.Unlock()
	ep.Unlock()

	if err = ctrlr.deleteEndpointFromStore(ep); err != nil {
		return err
	}
	defer func() {
		if err != nil {
			ep.SetIndex(0)
			if e := ctrlr.updateEndpointToStore(ep); e != nil {
				log.Warnf("failed to recreate endpoint in store %s : %v", name, err)
			}
		}
	}()

	// Update the endpoint count in network and update it in the datastore
	n.DecEndpointCnt()
	if err = ctrlr.updateNetworkToStore(n); err != nil {
		return err
	}
	defer func() {
		if err != nil {
			n.IncEndpointCnt()
			if e := ctrlr.updateNetworkToStore(n); e != nil {
				log.Warnf("failed to update network %s : %v", n.name, e)
			}
		}
	}()

	if err = ep.deleteEndpoint(); err != nil {
		return err
	}

	return nil
}

func (ep *endpoint) deleteEndpoint() error {
	ep.Lock()
	n := ep.network
	name := ep.name
	epid := ep.id
	ep.Unlock()

	n.Lock()
	_, ok := n.endpoints[epid]
	if !ok {
		n.Unlock()
		return nil
	}

	nid := n.id
	driver := n.driver
	delete(n.endpoints, epid)
	n.Unlock()

	if err := driver.DeleteEndpoint(nid, epid); err != nil {
		if _, ok := err.(types.ForbiddenError); ok {
			n.Lock()
			n.endpoints[epid] = ep
			n.Unlock()
			return err
		}
		log.Warnf("driver error deleting endpoint %s : %v", name, err)
	}

	n.updateSvcRecord(ep, false)
	return nil
}

func (ep *endpoint) getNetwork() *network {
	ep.Lock()
	defer ep.Unlock()
	return ep.network
}

func (ep *endpoint) getSandbox() (*sandbox, bool) {
	ep.Lock()
	c := ep.network.getController()
	sid := ep.sandboxID
	ep.Unlock()

	c.Lock()
	ps, ok := c.sandboxes[sid]
	c.Unlock()

	return ps, ok
}

func (ep *endpoint) getFirstInterfaceAddress() net.IP {
	ep.Lock()
	defer ep.Unlock()

	if ep.iface != nil {
		return ep.iface.addr.IP
	}

	return nil
}

// EndpointOptionGeneric function returns an option setter for a Generic option defined
// in a Dictionary of Key-Value pair
func EndpointOptionGeneric(generic map[string]interface{}) EndpointOption {
	return func(ep *endpoint) {
		for k, v := range generic {
			ep.generic[k] = v
		}
	}
}

// CreateOptionExposedPorts function returns an option setter for the container exposed
// ports option to be passed to network.CreateEndpoint() method.
func CreateOptionExposedPorts(exposedPorts []types.TransportPort) EndpointOption {
	return func(ep *endpoint) {
		// Defensive copy
		eps := make([]types.TransportPort, len(exposedPorts))
		copy(eps, exposedPorts)
		// Store endpoint label and in generic because driver needs it
		ep.exposedPorts = eps
		ep.generic[netlabel.ExposedPorts] = eps
	}
}

// CreateOptionPortMapping function returns an option setter for the mapping
// ports option to be passed to network.CreateEndpoint() method.
func CreateOptionPortMapping(portBindings []types.PortBinding) EndpointOption {
	return func(ep *endpoint) {
		// Store a copy of the bindings as generic data to pass to the driver
		pbs := make([]types.PortBinding, len(portBindings))
		copy(pbs, portBindings)
		ep.generic[netlabel.PortMap] = pbs
	}
}

// JoinOptionPriority function returns an option setter for priority option to
// be passed to the endpoint.Join() method.
func JoinOptionPriority(ep Endpoint, prio int) EndpointOption {
	return func(ep *endpoint) {
		// ep lock already acquired
		c := ep.network.getController()
		c.Lock()
		sb, ok := c.sandboxes[ep.sandboxID]
		c.Unlock()
		if !ok {
			log.Errorf("Could not set endpoint priority value during Join to endpoint %s: No sandbox id present in endpoint", ep.id)
			return
		}
		sb.epPriority[ep.id] = prio
	}
}
<commit_msg>Fix unmarshal error in endpoint interface<commit_after>package libnetwork

import (
	"container/heap"
	"encoding/json"
	"fmt"
	"net"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/datastore"
	"github.com/docker/libnetwork/netlabel"
	"github.com/docker/libnetwork/types"
)

// Endpoint represents a logical connection between a network and a sandbox.
type Endpoint interface {
	// A system generated id for this endpoint.
	ID() string

	// Name returns the name of this endpoint.
	Name() string

	// Network returns the name of the network to which this endpoint is attached.
	Network() string

	// Join joins the sandbox to the endpoint and populates into the sandbox
	// the network resources allocated for the endpoint.
	Join(sandbox Sandbox, options ...EndpointOption) error

	// Leave detaches the network resources populated in the sandbox.
	Leave(sandbox Sandbox, options ...EndpointOption) error

	// Return certain operational data belonging to this endpoint
	Info() EndpointInfo

	// DriverInfo returns a collection of driver operational data related to this endpoint retrieved from the driver
	DriverInfo() (map[string]interface{}, error)

	// Delete and detaches this endpoint from the network.
	Delete() error
}

// EndpointOption is a option setter function type used to pass varios options to Network
// and Endpoint interfaces methods. The various setter functions of type EndpointOption are
// provided by libnetwork, they look like <Create|Join|Leave>Option[...](...)
type EndpointOption func(ep *endpoint)

type endpoint struct {
	name          string
	id            string
	network       *network
	iface         *endpointInterface
	joinInfo      *endpointJoinInfo
	sandboxID     string
	exposedPorts  []types.TransportPort
	generic       map[string]interface{}
	joinLeaveDone chan struct{}
	dbIndex       uint64
	dbExists      bool
	sync.Mutex
}

func (ep *endpoint) MarshalJSON() ([]byte, error) {
	ep.Lock()
	defer ep.Unlock()

	epMap := make(map[string]interface{})
	epMap["name"] = ep.name
	epMap["id"] = ep.id
	epMap["ep_iface"] = ep.iface
	epMap["exposed_ports"] = ep.exposedPorts
	epMap["generic"] = ep.generic
	epMap["sandbox"] = ep.sandboxID
	return json.Marshal(epMap)
}

func (ep *endpoint) UnmarshalJSON(b []byte) (err error) {
	ep.Lock()
	defer ep.Unlock()

	var epMap map[string]interface{}
	if err := json.Unmarshal(b, &epMap); err != nil {
		return err
	}
	ep.name = epMap["name"].(string)
	ep.id = epMap["id"].(string)

	ib, _ := json.Marshal(epMap["ep_iface"])
	json.Unmarshal(ib, &ep.iface)

	tb, _ := json.Marshal(epMap["exposed_ports"])
	var tPorts []types.TransportPort
	json.Unmarshal(tb, &tPorts)
	ep.exposedPorts = tPorts

	cb, _ := json.Marshal(epMap["sandbox"])
	json.Unmarshal(cb, &ep.sandboxID)

	if epMap["generic"] != nil {
		ep.generic = epMap["generic"].(map[string]interface{})
	}
	return nil
}

func (ep *endpoint) ID() string {
	ep.Lock()
	defer ep.Unlock()

	return ep.id
}

func (ep *endpoint) Name() string {
	ep.Lock()
	defer ep.Unlock()

	return ep.name
}

func (ep *endpoint) Network() string {
	return ep.getNetwork().name
}

// endpoint Key structure : endpoint/network-id/endpoint-id
func (ep *endpoint) Key() []string {
	return []string{datastore.EndpointKeyPrefix, ep.getNetwork().id, ep.id}
}

func (ep *endpoint) KeyPrefix() []string {
	return []string{datastore.EndpointKeyPrefix, ep.getNetwork().id}
}

func (ep *endpoint) networkIDFromKey(key []string) (string, error) {
	// endpoint Key structure : endpoint/network-id/endpoint-id
	// it's an invalid key if the key doesn't have all the 3 key elements above
	if key == nil || len(key) < 3 || key[0] != datastore.EndpointKeyPrefix {
		return "", fmt.Errorf("invalid endpoint key : %v", key)
	}

	// network-id is placed at index=1. pls refer to endpoint.Key() method
	return key[1], nil
}

func (ep *endpoint) Value() []byte {
	b, err := json.Marshal(ep)
	if err != nil {
		return nil
	}
	return b
}

func (ep *endpoint) SetValue(value []byte) error {
	return json.Unmarshal(value, ep)
}

func (ep *endpoint) Index() uint64 {
	ep.Lock()
	defer ep.Unlock()
	return ep.dbIndex
}

func (ep *endpoint) SetIndex(index uint64) {
	ep.Lock()
	defer ep.Unlock()
	ep.dbIndex = index
	ep.dbExists = true
}

func (ep *endpoint) Exists() bool {
	ep.Lock()
	defer ep.Unlock()
	return ep.dbExists
}

func (ep *endpoint) processOptions(options ...EndpointOption) {
	ep.Lock()
	defer ep.Unlock()

	for _, opt := range options {
		if opt != nil {
			opt(ep)
		}
	}
}

// joinLeaveStart waits to ensure there are no joins or leaves in progress and
// marks this join/leave in progress without race
func (ep *endpoint) joinLeaveStart() {
	ep.Lock()
	defer ep.Unlock()

	for ep.joinLeaveDone != nil {
		joinLeaveDone := ep.joinLeaveDone
		ep.Unlock()

		select {
		case <-joinLeaveDone:
		}

		ep.Lock()
	}

	ep.joinLeaveDone = make(chan struct{})
}

// joinLeaveEnd marks the end of this join/leave operation and
// signals the same without race to other join and leave waiters
func (ep *endpoint) joinLeaveEnd() {
	ep.Lock()
	defer ep.Unlock()

	if ep.joinLeaveDone != nil {
		close(ep.joinLeaveDone)
		ep.joinLeaveDone = nil
	}
}

func (ep *endpoint) Join(sbox Sandbox, options ...EndpointOption) error {
	var err error

	if sbox == nil {
		return types.BadRequestErrorf("endpoint cannot be joined by nil container")
	}

	sb, ok := sbox.(*sandbox)
	if !ok {
		return types.BadRequestErrorf("not a valid Sandbox interface")
	}

	ep.joinLeaveStart()
	defer ep.joinLeaveEnd()

	ep.Lock()
	if ep.sandboxID != "" {
		ep.Unlock()
		return types.ForbiddenErrorf("a sandbox has already joined the endpoint")
	}

	ep.sandboxID = sbox.ID()
	ep.joinInfo = &endpointJoinInfo{}
	network := ep.network
	epid := ep.id
	ep.Unlock()
	defer func() {
		if err != nil {
			ep.Lock()
			ep.sandboxID = ""
			ep.Unlock()
		}
	}()

	network.Lock()
	driver := network.driver
	nid := network.id
	network.Unlock()

	ep.processOptions(options...)

	err = driver.Join(nid, epid, sbox.Key(), ep, sbox.Labels())
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			// Do not alter global err variable, it's needed by the previous defer
			if err := driver.Leave(nid, epid); err != nil {
				log.Warnf("driver leave failed while rolling back join: %v", err)
			}
		}
	}()

	address := ""
	if ip := ep.getFirstInterfaceAddress(); ip != nil {
		address = ip.String()
	}
	if err = sb.updateHostsFile(address, network.getSvcRecords()); err != nil {
		return err
	}

	if err = sb.updateDNS(ep.getNetwork().enableIPv6); err != nil {
		return err
	}

	if err = network.ctrlr.updateEndpointToStore(ep); err != nil {
		return err
	}

	sb.Lock()
	heap.Push(&sb.endpoints, ep)
	sb.Unlock()
	defer func() {
		if err != nil {
			for i, e := range sb.getConnectedEndpoints() {
				if e == ep {
					sb.Lock()
					heap.Remove(&sb.endpoints, i)
					sb.Unlock()
					return
				}
			}
		}
	}()

	if err = sb.populateNetworkResources(ep); err != nil {
		return err
	}
	return nil
}

func (ep *endpoint) hasInterface(iName string) bool {
	ep.Lock()
	defer ep.Unlock()

	return ep.iface != nil && ep.iface.srcName == iName
}

func (ep *endpoint) Leave(sbox Sandbox, options ...EndpointOption) error {
	ep.joinLeaveStart()
	defer ep.joinLeaveEnd()

	if sbox == nil || sbox.ID() == "" || sbox.Key() == "" {
		return types.BadRequestErrorf("invalid Sandbox passed to enpoint leave: %v", sbox)
	}

	sb, ok := sbox.(*sandbox)
	if !ok {
		return types.BadRequestErrorf("not a valid Sandbox interface")
	}

	ep.Lock()
	sid := ep.sandboxID
	ep.Unlock()

	if sid == "" {
		return types.ForbiddenErrorf("cannot leave endpoint with no attached sandbox")
	}
	if sid != sbox.ID() {
		return types.ForbiddenErrorf("unexpected sandbox ID in leave request. Expected %s. Got %s", ep.sandboxID, sbox.ID())
	}

	ep.processOptions(options...)

	ep.Lock()
	ep.sandboxID = ""
	n := ep.network
	ep.Unlock()

	n.Lock()
	c := n.ctrlr
	d := n.driver
	n.Unlock()

	if err := c.updateEndpointToStore(ep); err != nil {
		ep.Lock()
		ep.sandboxID = sid
		ep.Unlock()
		return err
	}

	if err := d.Leave(n.id, ep.id); err != nil {
		return err
	}

	return sb.clearNetworkResources(ep)
}

func (ep *endpoint) Delete() error {
	var err error
	ep.Lock()
	epid := ep.id
	name := ep.name
	n := ep.network
	if ep.sandboxID != "" {
		ep.Unlock()
		return &ActiveContainerError{name: name, id: epid}
	}
	n.Lock()
	ctrlr := n.ctrlr
	n.Unlock()
	ep.Unlock()

	if err = ctrlr.deleteEndpointFromStore(ep); err != nil {
		return err
	}
	defer func() {
		if err != nil {
			ep.SetIndex(0)
			if e := ctrlr.updateEndpointToStore(ep); e != nil {
				log.Warnf("failed to recreate endpoint in store %s : %v", name, err)
			}
		}
	}()

	// Update the endpoint count in network and update it in the datastore
	n.DecEndpointCnt()
	if err = ctrlr.updateNetworkToStore(n); err != nil {
		return err
	}
	defer func() {
		if err != nil {
			n.IncEndpointCnt()
			if e := ctrlr.updateNetworkToStore(n); e != nil {
				log.Warnf("failed to update network %s : %v", n.name, e)
			}
		}
	}()

	if err = ep.deleteEndpoint(); err != nil {
		return err
	}

	return nil
}

func (ep *endpoint) deleteEndpoint() error {
	ep.Lock()
	n := ep.network
	name := ep.name
	epid := ep.id
	ep.Unlock()

	n.Lock()
	_, ok := n.endpoints[epid]
	if !ok {
		n.Unlock()
		return nil
	}

	nid := n.id
	driver := n.driver
	delete(n.endpoints, epid)
	n.Unlock()

	if err := driver.DeleteEndpoint(nid, epid); err != nil {
		if _, ok := err.(types.ForbiddenError); ok {
			n.Lock()
			n.endpoints[epid] = ep
			n.Unlock()
			return err
		}
		log.Warnf("driver error deleting endpoint %s : %v", name, err)
	}

	n.updateSvcRecord(ep, false)
	return nil
}

func (ep *endpoint) getNetwork() *network {
	ep.Lock()
	defer ep.Unlock()
	return ep.network
}

func (ep *endpoint) getSandbox() (*sandbox, bool) {
	ep.Lock()
	c := ep.network.getController()
	sid := ep.sandboxID
	ep.Unlock()

	c.Lock()
	ps, ok := c.sandboxes[sid]
	c.Unlock()

	return ps, ok
}

func (ep *endpoint) getFirstInterfaceAddress() net.IP {
	ep.Lock()
	defer ep.Unlock()

	if ep.iface != nil {
		return ep.iface.addr.IP
	}

	return nil
}

// EndpointOptionGeneric function returns an option setter for a Generic option defined
// in a Dictionary of Key-Value pair
func EndpointOptionGeneric(generic map[string]interface{}) EndpointOption {
	return func(ep *endpoint) {
		for k, v := range generic {
			ep.generic[k] = v
		}
	}
}

// CreateOptionExposedPorts function returns an option setter for the container exposed
// ports option to be passed to network.CreateEndpoint() method.
func CreateOptionExposedPorts(exposedPorts []types.TransportPort) EndpointOption {
	return func(ep *endpoint) {
		// Defensive copy
		eps := make([]types.TransportPort, len(exposedPorts))
		copy(eps, exposedPorts)
		// Store endpoint label and in generic because driver needs it
		ep.exposedPorts = eps
		ep.generic[netlabel.ExposedPorts] = eps
	}
}

// CreateOptionPortMapping function returns an option setter for the mapping
// ports option to be passed to network.CreateEndpoint() method.
func CreateOptionPortMapping(portBindings []types.PortBinding) EndpointOption {
	return func(ep *endpoint) {
		// Store a copy of the bindings as generic data to pass to the driver
		pbs := make([]types.PortBinding, len(portBindings))
		copy(pbs, portBindings)
		ep.generic[netlabel.PortMap] = pbs
	}
}

// JoinOptionPriority function returns an option setter for priority option to
// be passed to the endpoint.Join() method.
func JoinOptionPriority(ep Endpoint, prio int) EndpointOption {
	return func(ep *endpoint) {
		// ep lock already acquired
		c := ep.network.getController()
		c.Lock()
		sb, ok := c.sandboxes[ep.sandboxID]
		c.Unlock()
		if !ok {
			log.Errorf("Could not set endpoint priority value during Join to endpoint %s: No sandbox id present in endpoint", ep.id)
			return
		}
		sb.epPriority[ep.id] = prio
	}
}
<|endoftext|>


<commit_before>package tagcheck

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/AlternativaPlatform/docker-registry-client/registry"
	"github.com/dollarshaveclub/furan/lib/config"
)

// ImageTagChecker describes an object that can see if a tag exists for an image in a registry
type ImageTagChecker interface {
	AllTagsExist(tags []string, repo string) (bool, []string, error)
}

// RegistryTagChecker is an object that can check a remote registry for a set of tags
type RegistryTagChecker struct {
	dockercfg  *config.Dockerconfig
	loggerFunc func(string, ...interface{})
}

// NewRegistryTagChecker returns a RegistryTagChecker using the specified dockercfg for authentication
func NewRegistryTagChecker(dockercfg *config.Dockerconfig, loggerFunc func(string, ...interface{})) *RegistryTagChecker {
	return &RegistryTagChecker{
		dockercfg:  dockercfg,
		loggerFunc: loggerFunc,
	}
}

// AllTagsExist checks a remote registry to see if all tags exist for the given repository.
// It returns the missing tags if any
func (rtc *RegistryTagChecker) AllTagsExist(tags []string, repo string) (bool, []string, error) {
	rs := strings.Split(repo, "/")
	if len(rs) != 3 {
		if len(rs) != 2 {
			return false, nil, fmt.Errorf("bad format for repo: expected [host]/[namespace]/[repository] or [namespace]/[repository]: %v", repo)
		}
		rs = []string{"registry-1.docker.io", rs[0], rs[1]}
	}
	if len(tags) == 0 {
		return false, nil, fmt.Errorf("at least one tag is required")
	}
	hc := &http.Client{}
	url := "https://" + rs[0]
	ac, ok := rtc.dockercfg.DockercfgContents[rs[0]]
	if ok { // if missing, anonymous auth
		hc.Transport = registry.WrapTransport(http.DefaultTransport, url, ac.Username, ac.Password)
	}
	// reg.Ping() fails for quay.io, so we manually construct a registry client here
	reg := registry.Registry{
		URL:    url,
		Client: hc,
		Logf:   rtc.loggerFunc,
	}
	missing := []string{}
	for _, t := range tags {
		m, err := reg.ManifestV2(fmt.Sprintf("%v/%v", rs[1], rs[2]), t)
		if err != nil {
			return false, nil, fmt.Errorf("error getting manifest: %v", err)
		}
		b, err := m.MarshalJSON()
		if err != nil {
			return false, nil, fmt.Errorf("error marshing json manifest: %v", err)
		}
		if strings.Contains(string(b), "\"MANIFEST_UNKNOWN\"") { // HACK
			missing = append(missing, t)
		}
	}
	return len(missing) == 0, missing, nil
}
<commit_msg>tagcheck: only support quay.io for now, execute tag manifest checks in parallel<commit_after>package tagcheck

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/AlternativaPlatform/docker-registry-client/registry"
	"github.com/dollarshaveclub/furan/lib/config"
	"golang.org/x/sync/errgroup"
)

// ImageTagChecker describes an object that can see if a tag exists for an image in a registry
type ImageTagChecker interface {
	AllTagsExist(tags []string, repo string) (bool, []string, error)
}

// RegistryTagChecker is an object that can check a remote registry for a set of tags
type RegistryTagChecker struct {
	dockercfg  *config.Dockerconfig
	loggerFunc func(string, ...interface{})
}

// NewRegistryTagChecker returns a RegistryTagChecker using the specified dockercfg for authentication
func NewRegistryTagChecker(dockercfg *config.Dockerconfig, loggerFunc func(string, ...interface{})) *RegistryTagChecker {
	return &RegistryTagChecker{
		dockercfg:  dockercfg,
		loggerFunc: loggerFunc,
	}
}

type manifestError struct {
	Errors []struct {
		Code string `json:"code"`
	} `json:"errors"`
}

const tagMissingManifestErrorCode = "MANIFEST_UNKNOWN"

// AllTagsExist checks a remote registry to see if all tags exist for the given repository.
// It returns the missing tags, if any.
// Only quay.io is supported for now as each registry demonstrates different behavior when using the Docker Registry V2 API
func (rtc *RegistryTagChecker) AllTagsExist(tags []string, repo string) (bool, []string, error) {
	if len(tags) == 0 {
		return false, nil, fmt.Errorf("at least one tag is required")
	}
	rs := strings.Split(repo, "/")
	if len(rs) == 3 {
		if rs[0] != "quay.io" {
			rtc.loggerFunc("quay.io is the only supported repository for tag checking: %v", rs[0])
			return false, tags, nil
		}
	} else {
		if len(rs) == 2 {
			rtc.loggerFunc("quay.io is the only supported repository for tag checking: Docker Hub")
			return false, tags, nil
		}
		return false, nil, fmt.Errorf("bad format for repo: expected [host]/[namespace]/[repository] or [namespace]/[repository]: %v", repo)
	}
	hc := &http.Client{}
	domain := rs[0]
	url := "https://" + domain
	ac, ok := rtc.dockercfg.DockercfgContents[domain]
	if ok { // if missing, anonymous auth
		hc.Transport = registry.WrapTransport(http.DefaultTransport, url, ac.Username, ac.Password)
	}
	// reg.Ping() fails for quay.io, so we manually construct a registry client here
	reg := registry.Registry{
		URL:    url,
		Client: hc,
		Logf:   rtc.loggerFunc,
	}
	ctx, cf := context.WithTimeout(context.Background(), 1*time.Minute)
	defer cf()
	g, _ := errgroup.WithContext(ctx)
	missing := make([]string, len(tags))
	for i := range tags {
		g.Go(func() error {
			t := tags[i]
			m, err := reg.ManifestV2(fmt.Sprintf("%v/%v", rs[1], rs[2]), t)
			if err != nil {
				return fmt.Errorf("error getting manifest: tag: %v: %v", t, err)
			}
			b, _ := m.MarshalJSON()
			me := manifestError{}
			if err := json.Unmarshal(b, &me); err != nil {
				return fmt.Errorf("error unmarshaling response: tag: %v: %v", t, err)
			}
			if len(me.Errors) > 0 {
				if me.Errors[0].Code == tagMissingManifestErrorCode {
					missing[i] = t
				} else {
					return fmt.Errorf("unknown error: tag: %v: %v", t, string(b))
				}
			}
			return nil
		})
	}
	if err := g.Wait(); err != nil {
		return false, nil, err
	}
	out := []string{}
	for _, m := range missing {
		if m != "" {
			out = append(out, m)
		}
	}
	return len(out) == 0, out, nil
}
<|endoftext|>


<commit_before>package data

import (
	"net/http"

	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/cozy/cozy-stack/web/permissions"
	"github.com/labstack/echo"
)

func proxy(c echo.Context, path string) error {
	doctype := c.Get("doctype").(string)
	instance := middlewares.GetInstance(c)
	p := couchdb.Proxy(instance, doctype, path)
	p.ServeHTTP(c.Response(), c.Request())
	return nil
}

func getDesignDoc(c echo.Context) error {
	docid := c.Param("designdocid")
	doctype := c.Get("doctype").(string)

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	if paramIsTrue(c, "revs") {
		return proxy(c, "_design/"+docid)
	}

	return c.JSON(http.StatusBadRequest, echo.Map{
		"error": "_design docs are only readable for replication",
	})
}

func getLocalDoc(c echo.Context) error {
	doctype := c.Get("doctype").(string)
	docid := c.Param("docid")

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	if err := CheckReadable(doctype); err != nil {
		return err
	}

	return proxy(c, "_local/"+docid)

}

func setLocalDoc(c echo.Context) error {
	doctype := c.Get("doctype").(string)
	docid := c.Param("docid")

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	if err := CheckReadable(doctype); err != nil {
		return err
	}

	return proxy(c, "_local/"+docid)
}

func bulkGet(c echo.Context) error {
	doctype := c.Get("doctype").(string)

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	if err := CheckReadable(doctype); err != nil {
		return err
	}

	return proxy(c, "_bulk_get")
}

func fullCommit(c echo.Context) error {
	doctype := c.Get("doctype").(string)

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	if err := CheckReadable(doctype); err != nil {
		return err
	}

	return proxy(c, "_ensure_full_commit")
}

func revsDiff(c echo.Context) error {
	doctype := c.Get("doctype").(string)

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	if err := CheckReadable(doctype); err != nil {
		return err
	}

	return proxy(c, "_revs_diff")
}

func dbStatus(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	doctype := c.Get("doctype").(string)

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	if err := CheckReadable(doctype); err != nil {
		return err
	}

	status, err := couchdb.DBStatus(instance, doctype)
	if err != nil {
		return err
	}

	return c.JSON(http.StatusOK, status)
}

func replicationRoutes(group *echo.Group) {
	// Routes used only for replication
	group.GET("/", dbStatus)
	group.GET("/_design/:designdocid", getDesignDoc)
	group.GET("/_changes", changesFeed)
	// POST=GET see http://docs.couchdb.org/en/2.1.0/api/database/changes.html#post--db-_changes)
	group.POST("/_changes", changesFeed)

	group.POST("/_ensure_full_commit", fullCommit)

	// useful for Pouchdb replication
	group.GET("/_bulk_get", bulkGet)

	group.POST("/_revs_diff", revsDiff)

	// for storing checkpoints
	group.GET("/_local/:docid", getLocalDoc)
	group.PUT("/_local/:docid", setLocalDoc)
}
<commit_msg>Add _bulk_docs route for replications<commit_after>package data

import (
	"net/http"

	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/cozy/cozy-stack/web/permissions"
	"github.com/labstack/echo"
)

func proxy(c echo.Context, path string) error {
	doctype := c.Get("doctype").(string)
	instance := middlewares.GetInstance(c)
	p := couchdb.Proxy(instance, doctype, path)
	p.ServeHTTP(c.Response(), c.Request())
	return nil
}

func getDesignDoc(c echo.Context) error {
	docid := c.Param("designdocid")
	doctype := c.Get("doctype").(string)

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	if paramIsTrue(c, "revs") {
		return proxy(c, "_design/"+docid)
	}

	return c.JSON(http.StatusBadRequest, echo.Map{
		"error": "_design docs are only readable for replication",
	})
}

func getLocalDoc(c echo.Context) error {
	doctype := c.Get("doctype").(string)
	docid := c.Param("docid")

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	if err := CheckReadable(doctype); err != nil {
		return err
	}

	return proxy(c, "_local/"+docid)

}

func setLocalDoc(c echo.Context) error {
	doctype := c.Get("doctype").(string)
	docid := c.Param("docid")

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	if err := CheckReadable(doctype); err != nil {
		return err
	}

	return proxy(c, "_local/"+docid)
}

func bulkGet(c echo.Context) error {
	doctype := c.Get("doctype").(string)

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	if err := CheckReadable(doctype); err != nil {
		return err
	}

	return proxy(c, "_bulk_get")
}

func bulkDocs(c echo.Context) error {
	doctype := c.Get("doctype").(string)

	if err := permissions.AllowWholeType(c, permissions.POST, doctype); err != nil {
		return err
	}

	if err := CheckReadable(doctype); err != nil {
		return err
	}

	return proxy(c, "_bulk_docs")
}

func fullCommit(c echo.Context) error {
	doctype := c.Get("doctype").(string)

	if err := permissions.AllowWholeType(c, permissions.POST, doctype); err != nil {
		return err
	}

	if err := CheckReadable(doctype); err != nil {
		return err
	}

	return proxy(c, "_ensure_full_commit")
}

func revsDiff(c echo.Context) error {
	doctype := c.Get("doctype").(string)

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	if err := CheckReadable(doctype); err != nil {
		return err
	}

	return proxy(c, "_revs_diff")
}

func dbStatus(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	doctype := c.Get("doctype").(string)

	if err := permissions.AllowWholeType(c, permissions.GET, doctype); err != nil {
		return err
	}

	if err := CheckReadable(doctype); err != nil {
		return err
	}

	status, err := couchdb.DBStatus(instance, doctype)
	if err != nil {
		return err
	}

	return c.JSON(http.StatusOK, status)
}

func replicationRoutes(group *echo.Group) {
	// Routes used only for replication
	group.GET("/", dbStatus)
	group.GET("/_design/:designdocid", getDesignDoc)
	group.GET("/_changes", changesFeed)
	// POST=GET see http://docs.couchdb.org/en/2.1.0/api/database/changes.html#post--db-_changes)
	group.POST("/_changes", changesFeed)

	group.POST("/_ensure_full_commit", fullCommit)

	// useful for Pouchdb replication
	group.GET("/_bulk_get", bulkGet)
	group.POST("/_bulk_docs", bulkDocs)

	group.POST("/_revs_diff", revsDiff)

	// for storing checkpoints
	group.GET("/_local/:docid", getLocalDoc)
	group.PUT("/_local/:docid", setLocalDoc)
}
<|endoftext|>


<commit_before>package database

import (
	"database/sql"

	"github.com/karousel/core/album"
	"github.com/karousel/core/collection"
	"github.com/karousel/core/file"
	"github.com/karousel/core/metadata"
	"github.com/karousel/core/photo"
	"github.com/karousel/core/token"
	"github.com/karousel/core/user"

	_ "github.com/lib/pq"
	gorp "gopkg.in/gorp.v1"
)

func NewDatabase(datasource string) (*gorp.DbMap, error) {
	var databaseMap *gorp.DbMap

	database, err := sql.Open("postgres", datasource)

	if err != nil {
		return databaseMap, err
	}

	databaseMap = &gorp.DbMap{Db: database, Dialect: gorp.PostgresDialect{}}

	collectionsTable := databaseMap.AddTableWithName(collection.Collection{}, "collections")
	collectionsTable.SetKeys(true, "id")
	collectionsTable.ColMap("name").SetNotNull(true)
	collectionsTable.ColMap("name").SetUnique(true)

	albumsTable := databaseMap.AddTableWithName(album.Album{}, "albums")
	albumsTable.SetKeys(true, "id")
	albumsTable.ColMap("collection_id").SetNotNull(true)
	albumsTable.ColMap("name").SetNotNull(true)
	albumsTable.ColMap("name").SetUnique(true)

	photosTable := databaseMap.AddTableWithName(photo.Photo{}, "photos")
	photosTable.SetKeys(true, "id")
	photosTable.ColMap("album_id").SetNotNull(true)
	photosTable.ColMap("name").SetNotNull(true)
	photosTable.ColMap("uploaded").SetNotNull(true)
	photosTable.ColMap("status").SetNotNull(true)

	filesTable := databaseMap.AddTableWithName(file.File{}, "files")
	filesTable.SetKeys(true, "id")
	filesTable.ColMap("parent_id").SetNotNull(true)
	filesTable.ColMap("checksum").SetNotNull(true)

	metadataTable := databaseMap.AddTableWithName(metadata.Metadata{}, "metadata")
	metadataTable.SetKeys(true, "id")
	metadataTable.ColMap("parent_id").SetNotNull(true)
	metadataTable.ColMap("raw").SetNotNull(true)

	usersTable := databaseMap.AddTableWithName(user.User{}, "users")
	usersTable.SetKeys(true, "id")
	usersTable.ColMap("email_address").SetNotNull(true)
	usersTable.ColMap("email_address").SetUnique(true)
	usersTable.ColMap("password").SetNotNull(true)
	usersTable.ColMap("name").SetNotNull(true)
	usersTable.ColMap("is_administrator").SetNotNull(true)

	tokensTable := databaseMap.AddTableWithName(token.Token{}, "tokens")
	tokensTable.SetKeys(true, "id")
	tokensTable.ColMap("user_id").SetNotNull(true)
	tokensTable.ColMap("expires").SetNotNull(true)
	tokensTable.ColMap("token").SetNotNull(true)
	tokensTable.ColMap("token").SetUnique(true)

	err = databaseMap.CreateTablesIfNotExists()

	if err != nil {
		return databaseMap, err
	}

	return databaseMap, nil
}
<commit_msg>Created a Database type which has a Connection field for the DbMap<commit_after>package database

import (
	"database/sql"

	"github.com/karousel/core/album"
	"github.com/karousel/core/collection"
	"github.com/karousel/core/file"
	"github.com/karousel/core/metadata"
	"github.com/karousel/core/photo"
	"github.com/karousel/core/token"
	"github.com/karousel/core/user"

	_ "github.com/lib/pq"
	gorp "gopkg.in/gorp.v1"
)

type Database struct {
	Connection *gorp.DbMap
}

func NewDatabase(datasource string) (Database, error) {
	var database Database
	var databaseMap *gorp.DbMap

	sqlConnection, err := sql.Open("postgres", datasource)

	if err != nil {
		return database, err
	}

	databaseMap = &gorp.DbMap{Db: sqlConnection, Dialect: gorp.PostgresDialect{}}

	collectionsTable := databaseMap.AddTableWithName(collection.Collection{}, "collections")
	collectionsTable.SetKeys(true, "id")
	collectionsTable.ColMap("name").SetNotNull(true)
	collectionsTable.ColMap("name").SetUnique(true)

	albumsTable := databaseMap.AddTableWithName(album.Album{}, "albums")
	albumsTable.SetKeys(true, "id")
	albumsTable.ColMap("collection_id").SetNotNull(true)
	albumsTable.ColMap("name").SetNotNull(true)
	albumsTable.ColMap("name").SetUnique(true)

	photosTable := databaseMap.AddTableWithName(photo.Photo{}, "photos")
	photosTable.SetKeys(true, "id")
	photosTable.ColMap("album_id").SetNotNull(true)
	photosTable.ColMap("name").SetNotNull(true)
	photosTable.ColMap("uploaded").SetNotNull(true)
	photosTable.ColMap("status").SetNotNull(true)

	filesTable := databaseMap.AddTableWithName(file.File{}, "files")
	filesTable.SetKeys(true, "id")
	filesTable.ColMap("parent_id").SetNotNull(true)
	filesTable.ColMap("checksum").SetNotNull(true)

	metadataTable := databaseMap.AddTableWithName(metadata.Metadata{}, "metadata")
	metadataTable.SetKeys(true, "id")
	metadataTable.ColMap("parent_id").SetNotNull(true)
	metadataTable.ColMap("raw").SetNotNull(true)

	usersTable := databaseMap.AddTableWithName(user.User{}, "users")
	usersTable.SetKeys(true, "id")
	usersTable.ColMap("email_address").SetNotNull(true)
	usersTable.ColMap("email_address").SetUnique(true)
	usersTable.ColMap("password").SetNotNull(true)
	usersTable.ColMap("name").SetNotNull(true)
	usersTable.ColMap("is_administrator").SetNotNull(true)

	tokensTable := databaseMap.AddTableWithName(token.Token{}, "tokens")
	tokensTable.SetKeys(true, "id")
	tokensTable.ColMap("user_id").SetNotNull(true)
	tokensTable.ColMap("expires").SetNotNull(true)
	tokensTable.ColMap("token").SetNotNull(true)
	tokensTable.ColMap("token").SetUnique(true)

	err = databaseMap.CreateTablesIfNotExists()

	if err != nil {
		return database, err
	}

	database.Connection = databaseMap

	return database, nil
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Aaron Meihm ameihm@mozilla.com [:alm]

package database /* import "mig.ninja/mig/database" */

import (
	"fmt"
	_ "github.com/lib/pq"
	"mig.ninja/mig"
)

// The number of signatures required for a manifest to be marked as active.
// XXX This should probably be somewhere else like in the configuration file.
const REQUIRED_SIGNATURES int = 1

// Add a new manifest record to the database
func (db *DB) ManifestAdd(mr mig.ManifestRecord) (err error) {
	_, err = db.c.Exec(`INSERT INTO manifests VALUES
		(DEFAULT, $1, $2, now(), 'staged', $3)`, mr.Name,
		mr.Content, mr.Target)
	return
}

// Add a signature to the database for an existing manifest
func (db *DB) ManifestAddSignature(mid float64, sig string, invid float64) (err error) {
	_, err = db.c.Exec(`INSERT INTO manifestsig
		(manifestid, pgpsignature, investigatorid)
		VALUES ($1, $2, $3)`, mid, sig, invid)
	if err != nil {
		return
	}

	err = db.ManifestUpdateStatus(mid)
	return
}

// Disable a manifest record
func (db *DB) ManifestDisable(mid float64) (err error) {
	_, err = db.c.Exec(`UPDATE manifests SET status='disabled' WHERE
		id=$1`, mid)
	if err != nil {
		return
	}
	return
}

// Update the status of a manifest based on the number of signatures it has
func (db *DB) ManifestUpdateStatus(mid float64) (err error) {
	var cnt int
	row := db.c.QueryRow(`SELECT COUNT(*) FROM manifestsig
		WHERE manifestid=$1`, mid)
	err = row.Scan(&cnt)
	if err != nil {
		return err
	}
	status := "staged"
	if cnt >= REQUIRED_SIGNATURES {
		status = "active"
	}
	_, err = db.c.Exec(`UPDATE manifests SET status=$1 WHERE
		id=$2`, status, mid)
	if err != nil {
		return err
	}
	return
}

// Clear existing signatures for a manifest record
func (db *DB) ManifestClearSignatures(mid float64) (err error) {
	_, err = db.c.Exec(`DELETE FROM manifestsig WHERE manifestid=$1`, mid)
	return err
}

// Return the entire contents of manifest ID mid from the database
func (db *DB) GetManifestFromID(mid float64) (ret mig.ManifestRecord, err error) {
	row := db.c.QueryRow(`SELECT id, name, content, timestamp, status, target
		FROM manifests WHERE id=$1`, mid)
	err = row.Scan(&ret.ID, &ret.Name, &ret.Content, &ret.Timestamp, &ret.Status, &ret.Target)
	if err != nil {
		err = fmt.Errorf("Error while retrieving manifest: '%v'", err)
		return
	}
	// Also add any signatures that exist for this manifest record
	rows, err := db.c.Query(`SELECT pgpsignature FROM manifestsig
		WHERE manifestid=$1`, mid)
	if err != nil {
		return
	}
	if rows != nil {
		defer rows.Close()
	}
	for rows.Next() {
		var s string
		err = rows.Scan(&s)
		if err != nil {
			return ret, err
		}
		ret.Signatures = append(ret.Signatures, s)
	}
	return
}
<commit_msg>[minor] honor the disabled flag in manifest maintenance operations<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Aaron Meihm ameihm@mozilla.com [:alm]

package database /* import "mig.ninja/mig/database" */

import (
	"fmt"
	_ "github.com/lib/pq"
	"mig.ninja/mig"
)

// The number of signatures required for a manifest to be marked as active.
// XXX This should probably be somewhere else like in the configuration file.
const REQUIRED_SIGNATURES int = 1

// Add a new manifest record to the database
func (db *DB) ManifestAdd(mr mig.ManifestRecord) (err error) {
	_, err = db.c.Exec(`INSERT INTO manifests VALUES
		(DEFAULT, $1, $2, now(), 'staged', $3)`, mr.Name,
		mr.Content, mr.Target)
	return
}

// Add a signature to the database for an existing manifest
func (db *DB) ManifestAddSignature(mid float64, sig string, invid float64) (err error) {
	res, err := db.c.Exec(`INSERT INTO manifestsig
		(manifestid, pgpsignature, investigatorid)
		SELECT $1, $2, $3
		WHERE EXISTS (SELECT id FROM manifests
		WHERE id=$4 AND status!='disabled')`, mid, sig, invid, mid)
	if err != nil {
		return
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return
	}
	if ra != 1 {
		return fmt.Errorf("Manifest signing operation failed")
	}

	err = db.ManifestUpdateStatus(mid)
	return
}

// Disable a manifest record
func (db *DB) ManifestDisable(mid float64) (err error) {
	_, err = db.c.Exec(`UPDATE manifests SET status='disabled' WHERE
		id=$1`, mid)
	if err != nil {
		return
	}
	return
}

// Update the status of a manifest based on the number of signatures it has
func (db *DB) ManifestUpdateStatus(mid float64) (err error) {
	var cnt int
	row := db.c.QueryRow(`SELECT COUNT(*) FROM manifestsig
		WHERE manifestid=$1`, mid)
	err = row.Scan(&cnt)
	if err != nil {
		return err
	}
	status := "staged"
	if cnt >= REQUIRED_SIGNATURES {
		status = "active"
	}
	_, err = db.c.Exec(`UPDATE manifests SET status=$1 WHERE
		id=$2 AND status!='disabled'`, status, mid)
	if err != nil {
		return err
	}
	return
}

// Clear existing signatures for a manifest record
func (db *DB) ManifestClearSignatures(mid float64) (err error) {
	_, err = db.c.Exec(`DELETE FROM manifestsig WHERE manifestid=$1`, mid)
	return err
}

// Return the entire contents of manifest ID mid from the database
func (db *DB) GetManifestFromID(mid float64) (ret mig.ManifestRecord, err error) {
	row := db.c.QueryRow(`SELECT id, name, content, timestamp, status, target
		FROM manifests WHERE id=$1`, mid)
	err = row.Scan(&ret.ID, &ret.Name, &ret.Content, &ret.Timestamp, &ret.Status, &ret.Target)
	if err != nil {
		err = fmt.Errorf("Error while retrieving manifest: '%v'", err)
		return
	}
	// Also add any signatures that exist for this manifest record
	rows, err := db.c.Query(`SELECT pgpsignature FROM manifestsig
		WHERE manifestid=$1`, mid)
	if err != nil {
		return
	}
	if rows != nil {
		defer rows.Close()
	}
	for rows.Next() {
		var s string
		err = rows.Scan(&s)
		if err != nil {
			return ret, err
		}
		ret.Signatures = append(ret.Signatures, s)
	}
	return
}
<|endoftext|>


<commit_before>/*
  Package certgen id set of utilities used to generate ssh certificates
*/
package certgen

import (
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"golang.org/x/crypto/ssh"
	"math/big"
	//"os"
	"os/exec"
	"time"

	"fmt"
)

// GetUserPubKeyFromSSSD user authorized keys content based on the running sssd configuration
func GetUserPubKeyFromSSSD(username string) (string, error) {
	cmd := exec.Command("/usr/bin/sss_ssh_authorizedkeys", username)
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return "", err
	}
	return out.String(), nil
}

func goCertToFileString(c ssh.Certificate, username string) (string, error) {
	certBytes := c.Marshal()
	encoded := base64.StdEncoding.EncodeToString(certBytes)
	fileComment := "/tmp/" + username + "-cert.pub"
	return "ssh-rsa-cert-v01@openssh.com " + encoded + " " + fileComment, nil
}

// gen_user_cert a username and key, returns a short lived cert for that user
func GenSSHCertFileString(username string, userPubKey string, signer ssh.Signer, host_identity string, duration time.Duration) (string, error) {
	userKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(userPubKey))
	if err != nil {
		return "", err
	}
	keyIdentity := host_identity + "_" + username

	currentEpoch := uint64(time.Now().Unix())
	expireEpoch := currentEpoch + uint64(duration.Seconds())

	// The values of the permissions are taken from the default values used
	// by ssh-keygen
	cert := ssh.Certificate{
		Key:             userKey,
		CertType:        ssh.UserCert,
		SignatureKey:    signer.PublicKey(),
		ValidPrincipals: []string{username},
		KeyId:           keyIdentity,
		ValidAfter:      currentEpoch,
		ValidBefore:     expireEpoch,
		Permissions: ssh.Permissions{Extensions: map[string]string{
			"permit-X11-forwarding":   "",
			"permit-agent-forwarding": "",
			"permit-port-forwarding":  "",
			"permit-pty":              "",
			"permit-user-rc":          ""}}}

	err = cert.SignCert(bytes.NewReader(cert.Marshal()), signer)
	if err != nil {
		return "", err
	}
	certString, err := goCertToFileString(cert, username)
	if err != nil {
		return "", err
	}
	return certString, nil
}

func GenSSHCertFileStringFromSSSDPublicKey(userName string, signer ssh.Signer, hostIdentity string, duration time.Duration) (string, error) {

	userPubKey, err := GetUserPubKeyFromSSSD(userName)
	if err != nil {
		return "", err
	}
	cert, err := GenSSHCertFileString(userName, userPubKey, signer, hostIdentity, duration)
	if err != nil {
		return "", err
	}
	return cert, err
}

/// X509 section
func getPubKeyFromPem(pubkey string) (pub interface{}, err error) {
	block, rest := pem.Decode([]byte(pubkey))
	if block == nil || block.Type != "PUBLIC KEY" {
		err := errors.New(fmt.Sprintf("Cannot decode user public Key '%s' rest='%s'", pubkey, string(rest)))
		if block != nil {
			err = errors.New(fmt.Sprintf("public key bad type %s", block.Type))
		}
		return nil, err
	}
	return x509.ParsePKIXPublicKey(block.Bytes)
}

func GetSignerFromPEMBytes(privateKey []byte) (crypto.Signer, error) {
	block, _ := pem.Decode(privateKey)
	if block == nil {
		err := errors.New("Cannot decode Private Key")
		return nil, err
	}
	switch block.Type {
	case "RSA PRIVATE KEY":
		return x509.ParsePKCS1PrivateKey(block.Bytes)
	case "EC PRIVATE KEY":
		return x509.ParseECPrivateKey(block.Bytes)
	default:
		err := errors.New("Cannot process that key")
		return nil, err
	}
}

//copied from https://golang.org/src/crypto/tls/generate_cert.go
func publicKey(priv interface{}) interface{} {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &k.PublicKey
	//case *ecdsa.PrivateKey:
	//	return &k.PublicKey
	default:
		return nil
	}
}

/*
func derBytesCertToCertAndPem(derBytes []byte) (*x509.Certificate, string, error) {
	cert, err := x509.ParseCertificate(derBytes)
	if err != nil {
		return nil, "", err
	}
	pemCert := string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}))
	return cert, pemCert, nil
}
*/

// return both an internal representation an the pem representation of the string
// As long as the issuer value matches THEN the serial number can be different every time
func GenSelfSignedCACert(commonName string, organization string, caPriv crypto.Signer) ([]byte, error) {
	//// Now do the actual work...
	notBefore := time.Now()
	notAfter := notBefore.Add(24 * 365 * 8 * time.Hour)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, err
	}
	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			CommonName:   commonName,
			Organization: []string{organization},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,
		KeyUsage:  x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign,
		//ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IsCA: true,
	}

	return x509.CreateCertificate(rand.Reader, &template, &template, publicKey(caPriv), caPriv)
}

// From RFC 4120 section 5.2.2 (https://tools.ietf.org/html/rfc4120)
type KerberosPrincipal struct {
	Len       int      `asn1:"explicit,tag:0"`
	Principal []string `asn1:"explicit,tag:1"`
}

// From RFC 4556 section 3.2.2 (https://tools.ietf.org/html/rfc4556.html)
type KRB5PrincipalName struct {
	Realm     string            `asn1:"explicit,tag:0"`
	Principal KerberosPrincipal `asn1:"explicit,tag:1"`
}

type PKInitSANAnotherName struct {
	Id    asn1.ObjectIdentifier
	Value KRB5PrincipalName `asn1:"explicit,tag:0"`
}

// Since currently asn1 cannot mashal into GeneralString (https://github.com/golang/go/issues/18832)
// We make this hack since we know the positions of the items we want to change
func changePrintableStringToGeneralString(kerberosRealm string, inString []byte) []byte {
	position := 16
	inString[position] = 27

	position = position + 1 + len(kerberosRealm) + 14
	inString[position] = 27

	return inString
}

func genSANExtension(userName string, kerberosRealm *string) (*pkix.Extension, error) {
	if kerberosRealm == nil {
		return nil, nil
	}
	krbRealm := *kerberosRealm

	//1.3.6.1.5.2.2
	krbSanAnotherName := PKInitSANAnotherName{
		Id: []int{1, 3, 6, 1, 5, 2, 2},
		Value: KRB5PrincipalName{
			Realm:     krbRealm,
			Principal: KerberosPrincipal{Len: 1, Principal: []string{userName}},
		},
	}
	krbSanAnotherNameDer, err := asn1.Marshal(krbSanAnotherName)
	if err != nil {
		return nil, err
	}
	//fmt.Printf("ext: %+x\n", krbSanAnotherNameDer)
	krbSanAnotherNameDer = changePrintableStringToGeneralString(krbRealm, krbSanAnotherNameDer)
	krbSanAnotherNameDer[0] = 0xA0
	//fmt.Printf("ext: %+x\n", krbSanAnotherNameDer)

	// inspired by marshalSANs in x509.go
	var rawValues []asn1.RawValue
	rawValues = append(rawValues, asn1.RawValue{FullBytes: krbSanAnotherNameDer})

	rawSan, err := asn1.Marshal(rawValues)
	if err != nil {
		return nil, err
	}

	sanExtension := pkix.Extension{
		Id:    []int{2, 5, 29, 17},
		Value: rawSan,
	}

	return &sanExtension, nil
}

// returns an x509 cert that has the username in the common name,
// optionally if a kerberos Realm is present it will also add a kerberos
// SAN exention for pkinit
func GenUserX509Cert(userName string, userPub interface{},
	caCert *x509.Certificate, caPriv crypto.Signer,
	kerberosRealm *string, duration time.Duration) ([]byte, error) {
	//// Now do the actual work...
	notBefore := time.Now()
	notAfter := notBefore.Add(duration)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, err
	}

	sanExtension, err := genSANExtension(userName, kerberosRealm)
	if err != nil {
		return nil, err
	}

	// need to add the extended key usage... that is special for kerberos
	//and also the client key usage
	kerberosClientExtKeyUsage := []int{1, 3, 6, 1, 5, 2, 3, 4}
	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			CommonName:   userName,
			Organization: []string{"Keymaster"},
		},
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
		UnknownExtKeyUsage:    []asn1.ObjectIdentifier{kerberosClientExtKeyUsage},
		BasicConstraintsValid: true,
		IsCA: false,
	}
	if sanExtension != nil {
		template.ExtraExtensions = []pkix.Extension{*sanExtension}
	}

	return x509.CreateCertificate(rand.Reader, &template, caCert, userPub, caPriv)
}
<commit_msg>making the PKIX name dependent on the signer to allow backends to distinguish them<commit_after>/*
  Package certgen id set of utilities used to generate ssh certificates
*/
package certgen

import (
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"golang.org/x/crypto/ssh"
	"math/big"
	//"os"
	"os/exec"
	"time"

	"fmt"
)

// GetUserPubKeyFromSSSD user authorized keys content based on the running sssd configuration
func GetUserPubKeyFromSSSD(username string) (string, error) {
	cmd := exec.Command("/usr/bin/sss_ssh_authorizedkeys", username)
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		return "", err
	}
	return out.String(), nil
}

func goCertToFileString(c ssh.Certificate, username string) (string, error) {
	certBytes := c.Marshal()
	encoded := base64.StdEncoding.EncodeToString(certBytes)
	fileComment := "/tmp/" + username + "-cert.pub"
	return "ssh-rsa-cert-v01@openssh.com " + encoded + " " + fileComment, nil
}

// gen_user_cert a username and key, returns a short lived cert for that user
func GenSSHCertFileString(username string, userPubKey string, signer ssh.Signer, host_identity string, duration time.Duration) (string, error) {
	userKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(userPubKey))
	if err != nil {
		return "", err
	}
	keyIdentity := host_identity + "_" + username

	currentEpoch := uint64(time.Now().Unix())
	expireEpoch := currentEpoch + uint64(duration.Seconds())

	// The values of the permissions are taken from the default values used
	// by ssh-keygen
	cert := ssh.Certificate{
		Key:             userKey,
		CertType:        ssh.UserCert,
		SignatureKey:    signer.PublicKey(),
		ValidPrincipals: []string{username},
		KeyId:           keyIdentity,
		ValidAfter:      currentEpoch,
		ValidBefore:     expireEpoch,
		Permissions: ssh.Permissions{Extensions: map[string]string{
			"permit-X11-forwarding":   "",
			"permit-agent-forwarding": "",
			"permit-port-forwarding":  "",
			"permit-pty":              "",
			"permit-user-rc":          ""}}}

	err = cert.SignCert(bytes.NewReader(cert.Marshal()), signer)
	if err != nil {
		return "", err
	}
	certString, err := goCertToFileString(cert, username)
	if err != nil {
		return "", err
	}
	return certString, nil
}

func GenSSHCertFileStringFromSSSDPublicKey(userName string, signer ssh.Signer, hostIdentity string, duration time.Duration) (string, error) {

	userPubKey, err := GetUserPubKeyFromSSSD(userName)
	if err != nil {
		return "", err
	}
	cert, err := GenSSHCertFileString(userName, userPubKey, signer, hostIdentity, duration)
	if err != nil {
		return "", err
	}
	return cert, err
}

/// X509 section
func getPubKeyFromPem(pubkey string) (pub interface{}, err error) {
	block, rest := pem.Decode([]byte(pubkey))
	if block == nil || block.Type != "PUBLIC KEY" {
		err := errors.New(fmt.Sprintf("Cannot decode user public Key '%s' rest='%s'", pubkey, string(rest)))
		if block != nil {
			err = errors.New(fmt.Sprintf("public key bad type %s", block.Type))
		}
		return nil, err
	}
	return x509.ParsePKIXPublicKey(block.Bytes)
}

func GetSignerFromPEMBytes(privateKey []byte) (crypto.Signer, error) {
	block, _ := pem.Decode(privateKey)
	if block == nil {
		err := errors.New("Cannot decode Private Key")
		return nil, err
	}
	switch block.Type {
	case "RSA PRIVATE KEY":
		return x509.ParsePKCS1PrivateKey(block.Bytes)
	case "EC PRIVATE KEY":
		return x509.ParseECPrivateKey(block.Bytes)
	default:
		err := errors.New("Cannot process that key")
		return nil, err
	}
}

//copied from https://golang.org/src/crypto/tls/generate_cert.go
func publicKey(priv interface{}) interface{} {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &k.PublicKey
	//case *ecdsa.PrivateKey:
	//	return &k.PublicKey
	default:
		return nil
	}
}

/*
func derBytesCertToCertAndPem(derBytes []byte) (*x509.Certificate, string, error) {
	cert, err := x509.ParseCertificate(derBytes)
	if err != nil {
		return nil, "", err
	}
	pemCert := string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}))
	return cert, pemCert, nil
}
*/

// return both an internal representation an the pem representation of the string
// As long as the issuer value matches THEN the serial number can be different every time
func GenSelfSignedCACert(commonName string, organization string, caPriv crypto.Signer) ([]byte, error) {
	//// Now do the actual work...
	notBefore := time.Now()
	notAfter := notBefore.Add(24 * 365 * 8 * time.Hour)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, err
	}
	sum := sha256.Sum256([]byte(commonName))
	signedCN, err := caPriv.Sign(rand.Reader, sum[:], crypto.SHA256)
	if err != nil {
		return nil, err
	}
	sigSum := sha256.Sum256(signedCN)
	sig := base64.StdEncoding.EncodeToString(sigSum[:])
	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			CommonName:   commonName,
			Organization: []string{organization},
			SerialNumber: sig,
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,
		KeyUsage:  x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign,
		//ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IsCA: true,
	}

	return x509.CreateCertificate(rand.Reader, &template, &template, publicKey(caPriv), caPriv)
}

// From RFC 4120 section 5.2.2 (https://tools.ietf.org/html/rfc4120)
type KerberosPrincipal struct {
	Len       int      `asn1:"explicit,tag:0"`
	Principal []string `asn1:"explicit,tag:1"`
}

// From RFC 4556 section 3.2.2 (https://tools.ietf.org/html/rfc4556.html)
type KRB5PrincipalName struct {
	Realm     string            `asn1:"explicit,tag:0"`
	Principal KerberosPrincipal `asn1:"explicit,tag:1"`
}

type PKInitSANAnotherName struct {
	Id    asn1.ObjectIdentifier
	Value KRB5PrincipalName `asn1:"explicit,tag:0"`
}

// Since currently asn1 cannot mashal into GeneralString (https://github.com/golang/go/issues/18832)
// We make this hack since we know the positions of the items we want to change
func changePrintableStringToGeneralString(kerberosRealm string, inString []byte) []byte {
	position := 16
	inString[position] = 27

	position = position + 1 + len(kerberosRealm) + 14
	inString[position] = 27

	return inString
}

func genSANExtension(userName string, kerberosRealm *string) (*pkix.Extension, error) {
	if kerberosRealm == nil {
		return nil, nil
	}
	krbRealm := *kerberosRealm

	//1.3.6.1.5.2.2
	krbSanAnotherName := PKInitSANAnotherName{
		Id: []int{1, 3, 6, 1, 5, 2, 2},
		Value: KRB5PrincipalName{
			Realm:     krbRealm,
			Principal: KerberosPrincipal{Len: 1, Principal: []string{userName}},
		},
	}
	krbSanAnotherNameDer, err := asn1.Marshal(krbSanAnotherName)
	if err != nil {
		return nil, err
	}
	//fmt.Printf("ext: %+x\n", krbSanAnotherNameDer)
	krbSanAnotherNameDer = changePrintableStringToGeneralString(krbRealm, krbSanAnotherNameDer)
	krbSanAnotherNameDer[0] = 0xA0
	//fmt.Printf("ext: %+x\n", krbSanAnotherNameDer)

	// inspired by marshalSANs in x509.go
	var rawValues []asn1.RawValue
	rawValues = append(rawValues, asn1.RawValue{FullBytes: krbSanAnotherNameDer})

	rawSan, err := asn1.Marshal(rawValues)
	if err != nil {
		return nil, err
	}

	sanExtension := pkix.Extension{
		Id:    []int{2, 5, 29, 17},
		Value: rawSan,
	}

	return &sanExtension, nil
}

// returns an x509 cert that has the username in the common name,
// optionally if a kerberos Realm is present it will also add a kerberos
// SAN exention for pkinit
func GenUserX509Cert(userName string, userPub interface{},
	caCert *x509.Certificate, caPriv crypto.Signer,
	kerberosRealm *string, duration time.Duration) ([]byte, error) {
	//// Now do the actual work...
	notBefore := time.Now()
	notAfter := notBefore.Add(duration)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, err
	}

	sanExtension, err := genSANExtension(userName, kerberosRealm)
	if err != nil {
		return nil, err
	}

	// need to add the extended key usage... that is special for kerberos
	//and also the client key usage
	kerberosClientExtKeyUsage := []int{1, 3, 6, 1, 5, 2, 3, 4}
	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			CommonName:   userName,
			Organization: []string{"Keymaster"},
		},
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
		UnknownExtKeyUsage:    []asn1.ObjectIdentifier{kerberosClientExtKeyUsage},
		BasicConstraintsValid: true,
		IsCA: false,
	}
	if sanExtension != nil {
		template.ExtraExtensions = []pkix.Extension{*sanExtension}
	}

	return x509.CreateCertificate(rand.Reader, &template, caCert, userPub, caPriv)
}
<|endoftext|>


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

// Waiting for FDs via select(2).

package net

import (
	"os"
	"syscall"
	//"fmt"
)

type pollster struct {
	readFds, writeFds, repeatFds *syscall.FdSet_t
	maxFd int
	readyReadFds, readyWriteFds *syscall.FdSet_t
	nReady int
	lastFd int
}

func newpollster() (p *pollster, err os.Error) {
	p = new(pollster)
	p.readFds = new(syscall.FdSet_t)
	p.writeFds = new(syscall.FdSet_t)
	p.repeatFds = new(syscall.FdSet_t)
	p.readyReadFds = new(syscall.FdSet_t)
	p.readyWriteFds = new(syscall.FdSet_t)
	p.maxFd = -1
	p.nReady = 0
	p.lastFd = 0
	return p, nil
}

func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error {
	if mode == 'r' {
		syscall.FDSet(fd, p.readFds)
	} else {
		syscall.FDSet(fd, p.writeFds)
	}

	if repeat {
		syscall.FDSet(fd, p.repeatFds)
	}

	if fd > p.maxFd {
		p.maxFd = fd
	}

	return nil
}

func (p *pollster) DelFD(fd int, mode int) {
	if mode == 'r' {
		if !syscall.FDIsSet(fd, p.readFds) {
			print("Select unexpected fd=", fd, " for read\n")
			return
		}
		syscall.FDClr(fd, p.readFds)
	} else {
		if !syscall.FDIsSet(fd, p.writeFds) {
			print("Select unexpected fd=", fd, " for write\n")
			return
		}
		syscall.FDClr(fd, p.writeFds)
	}

	// Doesn't matter if not already present.
	syscall.FDClr(fd, p.repeatFds)

	// We don't worry about maxFd here.
}

func (p *pollster) WaitFD(nsec int64) (fd int, mode int, err os.Error) {
	if p.nReady == 0 {
		var timeout *syscall.Timeval
		var tv syscall.Timeval
		timeout = nil
		if nsec > 0 {
			tv = syscall.NsecToTimeval(nsec)
			timeout = &tv
		}

		var n, e int
		var tmpReadFds, tmpWriteFds syscall.FdSet_t
		for {
			// Temporary syscall.FdSet_ts into which the values are copied
			// because select mutates the values.
			tmpReadFds = *p.readFds
			tmpWriteFds = *p.writeFds

			n, e = syscall.Select(p.maxFd + 1, &tmpReadFds, &tmpWriteFds, nil, timeout)
			if e != syscall.EINTR {
				break
			}
		}
		if e != 0 {
			return -1, 0, os.NewSyscallError("select", e)
		}
		if n == 0 {
			return -1, 0, nil
		}

		p.nReady = n
		*p.readyReadFds = tmpReadFds
		*p.readyWriteFds = tmpWriteFds
		p.lastFd = 0
	}

	flag := false
	for i := p.lastFd; i < p.maxFd + 1; i++ {
		if syscall.FDIsSet(i, p.readyReadFds) {
			flag = true
			mode = 'r'
			syscall.FDClr(i, p.readyReadFds)
		} else if syscall.FDIsSet(i, p.readyWriteFds) {
			flag = true
			mode = 'w'
			syscall.FDClr(i, p.readyWriteFds)
		}
		if flag {
			if !syscall.FDIsSet(i, p.repeatFds) {
				p.DelFD(i, mode)
			}
			p.nReady--
			p.lastFd = i
			return i, mode, nil
		}
	}

	// Will not reach here.  Just to shut up the compiler.
	return -1, 0, nil
}

func (p *pollster) Close() os.Error {
	return nil
}
<commit_msg>go/net/fd_rtems.go: Clean unwanted line.<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Waiting for FDs via select(2).

package net

import (
	"os"
	"syscall"
)

type pollster struct {
	readFds, writeFds, repeatFds *syscall.FdSet_t
	maxFd int
	readyReadFds, readyWriteFds *syscall.FdSet_t
	nReady int
	lastFd int
}

func newpollster() (p *pollster, err os.Error) {
	p = new(pollster)
	p.readFds = new(syscall.FdSet_t)
	p.writeFds = new(syscall.FdSet_t)
	p.repeatFds = new(syscall.FdSet_t)
	p.readyReadFds = new(syscall.FdSet_t)
	p.readyWriteFds = new(syscall.FdSet_t)
	p.maxFd = -1
	p.nReady = 0
	p.lastFd = 0
	return p, nil
}

func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error {
	if mode == 'r' {
		syscall.FDSet(fd, p.readFds)
	} else {
		syscall.FDSet(fd, p.writeFds)
	}

	if repeat {
		syscall.FDSet(fd, p.repeatFds)
	}

	if fd > p.maxFd {
		p.maxFd = fd
	}

	return nil
}

func (p *pollster) DelFD(fd int, mode int) {
	if mode == 'r' {
		if !syscall.FDIsSet(fd, p.readFds) {
			print("Select unexpected fd=", fd, " for read\n")
			return
		}
		syscall.FDClr(fd, p.readFds)
	} else {
		if !syscall.FDIsSet(fd, p.writeFds) {
			print("Select unexpected fd=", fd, " for write\n")
			return
		}
		syscall.FDClr(fd, p.writeFds)
	}

	// Doesn't matter if not already present.
	syscall.FDClr(fd, p.repeatFds)

	// We don't worry about maxFd here.
}

func (p *pollster) WaitFD(nsec int64) (fd int, mode int, err os.Error) {
	if p.nReady == 0 {
		var timeout *syscall.Timeval
		var tv syscall.Timeval
		timeout = nil
		if nsec > 0 {
			tv = syscall.NsecToTimeval(nsec)
			timeout = &tv
		}

		var n, e int
		var tmpReadFds, tmpWriteFds syscall.FdSet_t
		for {
			// Temporary syscall.FdSet_ts into which the values are copied
			// because select mutates the values.
			tmpReadFds = *p.readFds
			tmpWriteFds = *p.writeFds

			n, e = syscall.Select(p.maxFd + 1, &tmpReadFds, &tmpWriteFds, nil, timeout)
			if e != syscall.EINTR {
				break
			}
		}
		if e != 0 {
			return -1, 0, os.NewSyscallError("select", e)
		}
		if n == 0 {
			return -1, 0, nil
		}

		p.nReady = n
		*p.readyReadFds = tmpReadFds
		*p.readyWriteFds = tmpWriteFds
		p.lastFd = 0
	}

	flag := false
	for i := p.lastFd; i < p.maxFd + 1; i++ {
		if syscall.FDIsSet(i, p.readyReadFds) {
			flag = true
			mode = 'r'
			syscall.FDClr(i, p.readyReadFds)
		} else if syscall.FDIsSet(i, p.readyWriteFds) {
			flag = true
			mode = 'w'
			syscall.FDClr(i, p.readyWriteFds)
		}
		if flag {
			if !syscall.FDIsSet(i, p.repeatFds) {
				p.DelFD(i, mode)
			}
			p.nReady--
			p.lastFd = i
			return i, mode, nil
		}
	}

	// Will not reach here.  Just to shut up the compiler.
	return -1, 0, nil
}

func (p *pollster) Close() os.Error {
	return nil
}
<|endoftext|>