<commit_before>package vsphere

import (
	"context"
	"fmt"
	"net/url"
	"sort"

	"github.com/vmware/govmomi"
	"github.com/vmware/govmomi/find"
	"github.com/vmware/govmomi/object"
	"github.com/vmware/govmomi/vapi/library"
	"github.com/vmware/govmomi/vapi/rest"
	"github.com/vmware/govmomi/vapi/tags"
	corev1 "k8s.io/api/core/v1"
	v1 "k8s.io/api/core/v1"
)

func processSoapFinder(ctx context.Context, fieldName string, cc *v1.Secret, dc string) ([]string, error) {
	finder, err := getSoapFinder(ctx, cc, dc)
	if err != nil {
		return nil, err
	}

	var data []string
	switch fieldName {
	case "clusters":
		data, err = listClusters(ctx, finder)
	case "virtual-machines":
		data, err = listVirtualMachines(ctx, finder, "virtual-machines")
	case "templates":
		data, err = listVirtualMachines(ctx, finder, "templates")
	case "data-stores":
		data, err = listDataStores(ctx, finder)
	case "data-store-clusters":
		data, err = listDataStoreClusters(ctx, finder)
	case "folders":
		data, err = listFolders(ctx, finder)
	case "hosts":
		data, err = listHosts(ctx, finder)
	case "networks":
		data, err = listNetworks(ctx, finder)
	case "data-centers":
		data, err = listDataCenters(ctx, finder)
	case "resource-pools":
		data, err = listResourcePools(ctx, finder)
	}

	return data, err
}

func processTagsManager(ctx context.Context, fieldName string, cc *v1.Secret, cat string) ([]map[string]string, error) {
	tagsManager, err := getTagsManager(ctx, cc)
	if err != nil {
		return nil, err
	}

	var data []map[string]string
	switch fieldName {
	case "tags":
		data, err = listTags(ctx, tagsManager, cat)
	case "tag-categories":
		data, err = listTagCategories(ctx, tagsManager)
	}

	return data, err
}

func processContentLibraryManager(ctx context.Context, fieldName string, cc *v1.Secret, library string) ([]string, error) {
	libraryManager, err := getContentLibraryManager(ctx, cc)
	if err != nil {
		return nil, err
	}

	var data []string
	switch fieldName {
	case "library-templates":
		data, err = listContentLibraryTemplates(ctx, libraryManager, library)
	case "content-libraries":
		data, err = listContentLibraries(ctx, libraryManager)
	}

	return data, err
}

func listContentLibraries(ctx context.Context, mgr *library.Manager) ([]string, error) {
	libs, err := mgr.GetLibraries(ctx)
	if err != nil {
		return nil, err
	}

	data := []string{""}
	for _, lib := range libs {
		data = append(data, lib.Name)
	}

	return data, nil
}

func listContentLibraryTemplates(ctx context.Context, mgr *library.Manager, library string) ([]string, error) {
	lib, err := mgr.GetLibraryByName(ctx, library)
	if err != nil {
		return nil, err
	}

	items, err := mgr.GetLibraryItems(ctx, lib.ID)
	if err != nil {
		return nil, err
	}

	var data []string
	for _, item := range items {
		if item.Type == "ovf" {
			data = append(data, item.Name)
		}
	}

	sort.Strings(data)
	return data, nil
}

func processCustomFieldsFinder(ctx context.Context, cc *v1.Secret) ([]map[string]interface{}, error) {
	mgr, err := getCustomFieldsManager(ctx, cc)
	if err != nil {
		return nil, err
	}

	field, err := mgr.Field(ctx)
	if err != nil {
		return nil, err
	}

	var data []map[string]interface{}
	for _, def := range field {
		if def.ManagedObjectType != "VirtualMachine" {
			continue
		}
		f := map[string]interface{}{
			"key":  def.Key,
			"name": def.Name,
		}
		data = append(data, f)
	}
	return data, nil
}

func listTags(ctx context.Context, tagsManager *tags.Manager, cat string) ([]map[string]string, error) {
	var lsTags []tags.Tag
	var err error
	if cat == "" {
		lsTags, err = tagsManager.GetTags(ctx)
	} else {
		lsTags, err = tagsManager.GetTagsForCategory(ctx, cat)
	}

	if err != nil {
		return nil, err
	}

	categories, err := tagsManager.GetCategories(ctx)
	if err != nil {
		return nil, err
	}

	m := make(map[string]tags.Category)
	for _, category := range categories {
		m[category.ID] = category
	}

	var data []map[string]string
	for _, tag := range lsTags {
		t := map[string]string{
			"id":       tag.ID,
			"name":     tag.Name,
			"category": m[tag.CategoryID].Name,
		}
		data = append(data, t)
	}

	return data, nil
}

func listTagCategories(ctx context.Context, tagsManager *tags.Manager) ([]map[string]string, error) {
	categories, err := tagsManager.GetCategories(ctx)
	if err != nil {
		return nil, err
	}

	var data []map[string]string
	for _, cat := range categories {
		tc := map[string]string{
			"id":                  cat.ID,
			"name":                cat.Name,
			"multipleCardinality": cat.Cardinality,
		}
		data = append(data, tc)
	}

	return data, nil
}

func listClusters(ctx context.Context, finder *find.Finder) ([]string, error) {
	clusters, err := finder.ClusterComputeResourceList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, cluster := range clusters {
		data = append(data, cluster.InventoryPath)
	}

	return data, nil
}

func listDataCenters(ctx context.Context, finder *find.Finder) ([]string, error) {
	dcs, err := finder.DatacenterList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, v := range dcs {
		data = append(data, v.InventoryPath)
	}

	return data, nil
}

func listResourcePools(ctx context.Context, finder *find.Finder) ([]string, error) {
	dcs, err := finder.ResourcePoolList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, v := range dcs {
		data = append(data, v.InventoryPath)
	}

	return data, nil
}

func listDataStores(ctx context.Context, finder *find.Finder) ([]string, error) {
	dataStores, err := finder.DatastoreList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, ds := range dataStores {
		data = append(data, ds.InventoryPath)
	}

	return data, nil
}

func listDataStoreClusters(ctx context.Context, finder *find.Finder) ([]string, error) {
	dataStores, err := finder.DatastoreClusterList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, ds := range dataStores {
		data = append(data, ds.InventoryPath)
	}

	return data, nil
}

func listFolders(ctx context.Context, finder *find.Finder) ([]string, error) {
	folders, err := finder.FolderList(ctx, "vm/*")
	if err != nil {
		return nil, err
	}

	data := []string{""}
	for _, f := range folders {
		data = append(data, f.InventoryPath)
	}

	return data, nil
}

func listHosts(ctx context.Context, finder *find.Finder) ([]string, error) {
	hosts, err := finder.HostSystemList(ctx, "*")
	if err != nil {
		return nil, err
	}

	data := []string{""} //blank default

	for _, h := range hosts {
		data = append(data, h.InventoryPath)
	}

	return data, nil
}

func listNetworks(ctx context.Context, finder *find.Finder) ([]string, error) {
	networks, err := finder.NetworkList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, net := range networks {
		data = append(data, net.GetInventoryPath())
	}

	return data, nil
}

func listVirtualMachines(ctx context.Context, finder *find.Finder, t string) ([]string, error) {
	if t == "" {
		t = "both"
	}
	vms, err := finder.VirtualMachineList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, vm := range vms {
		switch t {
		case "both":
			data = append(data, vm.InventoryPath)
		case "templates":
			if isTemplate, err := vm.IsTemplate(ctx); err == nil && isTemplate {
				data = append(data, vm.InventoryPath)
			}
		case "virtual-machines":
			if isTemplate, err := vm.IsTemplate(ctx); err == nil && !isTemplate {
				data = append(data, vm.InventoryPath)
			}
		}
	}

	sort.Strings(data)
	return data, nil
}

func getSoapFinder(ctx context.Context, cc *corev1.Secret, dc string) (*find.Finder, error) {
	c, err := getSoapClient(ctx, cc)
	if err != nil {
		return nil, err
	}

	finder := find.NewFinder(c.Client, true)

	if dc != "" {
		dataCenter, err := finder.Datacenter(ctx, dc)
		if err != nil {
			return nil, err
		}

		finder.SetDatacenter(dataCenter)
	}

	return finder, nil
}

func getSoapClient(ctx context.Context, cc *corev1.Secret) (*govmomi.Client, error) {
	u, err := url.Parse(fmt.Sprintf("https://%s:%s/sdk", cc.Data[dataFields["host"]], cc.Data[dataFields["port"]]))
	if err != nil {
		return nil, err
	}

	u.User = url.UserPassword(string(cc.Data[dataFields["username"]]), string(cc.Data[dataFields["password"]]))
	return govmomi.NewClient(ctx, u, true)
}

func getContentLibraryManager(ctx context.Context, cc *corev1.Secret) (*library.Manager, error) {
	soap, err := getSoapClient(ctx, cc)
	if err != nil {
		return nil, err
	}

	mgr := library.NewManager(rest.NewClient(soap.Client))
	ui := url.UserPassword(string(cc.Data[dataFields["username"]]), string(cc.Data[dataFields["password"]]))

	return mgr, mgr.Login(ctx, ui)
}

func getTagsManager(ctx context.Context, cc *corev1.Secret) (*tags.Manager, error) {
	soap, err := getSoapClient(ctx, cc)
	if err != nil {
		return nil, err
	}

	mgr := tags.NewManager(rest.NewClient(soap.Client))
	ui := url.UserPassword(string(cc.Data[dataFields["username"]]), string(cc.Data[dataFields["password"]]))

	return mgr, mgr.Login(ctx, ui)
}

func getCustomFieldsManager(ctx context.Context, cc *corev1.Secret) (*object.CustomFieldsManager, error) {
	soap, err := getSoapClient(ctx, cc)
	if err != nil {
		return nil, err
	}

	return object.GetCustomFieldsManager(soap.Client)
}
<commit_msg>removed folder matching from govmomi to do string prefix match<commit_after>package vsphere

import (
	"context"
	"fmt"
	"net/url"
	"path"
	"sort"
	"strings"

	"github.com/vmware/govmomi"
	"github.com/vmware/govmomi/find"
	"github.com/vmware/govmomi/object"
	"github.com/vmware/govmomi/vapi/library"
	"github.com/vmware/govmomi/vapi/rest"
	"github.com/vmware/govmomi/vapi/tags"
	corev1 "k8s.io/api/core/v1"
	v1 "k8s.io/api/core/v1"
)

func processSoapFinder(ctx context.Context, fieldName string, cc *v1.Secret, dc string) ([]string, error) {
	finder, err := getSoapFinder(ctx, cc, dc)
	if err != nil {
		return nil, err
	}

	var data []string
	switch fieldName {
	case "clusters":
		data, err = listClusters(ctx, finder)
	case "virtual-machines":
		data, err = listVirtualMachines(ctx, finder, "virtual-machines")
	case "templates":
		data, err = listVirtualMachines(ctx, finder, "templates")
	case "data-stores":
		data, err = listDataStores(ctx, finder)
	case "data-store-clusters":
		data, err = listDataStoreClusters(ctx, finder)
	case "folders":
		data, err = listFolders(ctx, finder, dc)
	case "hosts":
		data, err = listHosts(ctx, finder)
	case "networks":
		data, err = listNetworks(ctx, finder)
	case "data-centers":
		data, err = listDataCenters(ctx, finder)
	case "resource-pools":
		data, err = listResourcePools(ctx, finder)
	}

	return data, err
}

func processTagsManager(ctx context.Context, fieldName string, cc *v1.Secret, cat string) ([]map[string]string, error) {
	tagsManager, err := getTagsManager(ctx, cc)
	if err != nil {
		return nil, err
	}

	var data []map[string]string
	switch fieldName {
	case "tags":
		data, err = listTags(ctx, tagsManager, cat)
	case "tag-categories":
		data, err = listTagCategories(ctx, tagsManager)
	}

	return data, err
}

func processContentLibraryManager(ctx context.Context, fieldName string, cc *v1.Secret, library string) ([]string, error) {
	libraryManager, err := getContentLibraryManager(ctx, cc)
	if err != nil {
		return nil, err
	}

	var data []string
	switch fieldName {
	case "library-templates":
		data, err = listContentLibraryTemplates(ctx, libraryManager, library)
	case "content-libraries":
		data, err = listContentLibraries(ctx, libraryManager)
	}

	return data, err
}

func listContentLibraries(ctx context.Context, mgr *library.Manager) ([]string, error) {
	libs, err := mgr.GetLibraries(ctx)
	if err != nil {
		return nil, err
	}

	data := []string{""}
	for _, lib := range libs {
		data = append(data, lib.Name)
	}

	return data, nil
}

func listContentLibraryTemplates(ctx context.Context, mgr *library.Manager, library string) ([]string, error) {
	lib, err := mgr.GetLibraryByName(ctx, library)
	if err != nil {
		return nil, err
	}

	items, err := mgr.GetLibraryItems(ctx, lib.ID)
	if err != nil {
		return nil, err
	}

	var data []string
	for _, item := range items {
		if item.Type == "ovf" {
			data = append(data, item.Name)
		}
	}

	sort.Strings(data)
	return data, nil
}

func processCustomFieldsFinder(ctx context.Context, cc *v1.Secret) ([]map[string]interface{}, error) {
	mgr, err := getCustomFieldsManager(ctx, cc)
	if err != nil {
		return nil, err
	}

	field, err := mgr.Field(ctx)
	if err != nil {
		return nil, err
	}

	var data []map[string]interface{}
	for _, def := range field {
		if def.ManagedObjectType != "VirtualMachine" {
			continue
		}
		f := map[string]interface{}{
			"key":  def.Key,
			"name": def.Name,
		}
		data = append(data, f)
	}
	return data, nil
}

func listTags(ctx context.Context, tagsManager *tags.Manager, cat string) ([]map[string]string, error) {
	var lsTags []tags.Tag
	var err error
	if cat == "" {
		lsTags, err = tagsManager.GetTags(ctx)
	} else {
		lsTags, err = tagsManager.GetTagsForCategory(ctx, cat)
	}

	if err != nil {
		return nil, err
	}

	categories, err := tagsManager.GetCategories(ctx)
	if err != nil {
		return nil, err
	}

	m := make(map[string]tags.Category)
	for _, category := range categories {
		m[category.ID] = category
	}

	var data []map[string]string
	for _, tag := range lsTags {
		t := map[string]string{
			"id":       tag.ID,
			"name":     tag.Name,
			"category": m[tag.CategoryID].Name,
		}
		data = append(data, t)
	}

	return data, nil
}

func listTagCategories(ctx context.Context, tagsManager *tags.Manager) ([]map[string]string, error) {
	categories, err := tagsManager.GetCategories(ctx)
	if err != nil {
		return nil, err
	}

	var data []map[string]string
	for _, cat := range categories {
		tc := map[string]string{
			"id":                  cat.ID,
			"name":                cat.Name,
			"multipleCardinality": cat.Cardinality,
		}
		data = append(data, tc)
	}

	return data, nil
}

func listClusters(ctx context.Context, finder *find.Finder) ([]string, error) {
	clusters, err := finder.ClusterComputeResourceList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, cluster := range clusters {
		data = append(data, cluster.InventoryPath)
	}

	return data, nil
}

func listDataCenters(ctx context.Context, finder *find.Finder) ([]string, error) {
	dcs, err := finder.DatacenterList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, v := range dcs {
		data = append(data, v.InventoryPath)
	}

	return data, nil
}

func listResourcePools(ctx context.Context, finder *find.Finder) ([]string, error) {
	dcs, err := finder.ResourcePoolList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, v := range dcs {
		data = append(data, v.InventoryPath)
	}

	return data, nil
}

func listDataStores(ctx context.Context, finder *find.Finder) ([]string, error) {
	dataStores, err := finder.DatastoreList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, ds := range dataStores {
		data = append(data, ds.InventoryPath)
	}

	return data, nil
}

func listDataStoreClusters(ctx context.Context, finder *find.Finder) ([]string, error) {
	dataStores, err := finder.DatastoreClusterList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, ds := range dataStores {
		data = append(data, ds.InventoryPath)
	}

	return data, nil
}

func listFolders(ctx context.Context, finder *find.Finder, dc string) ([]string, error) {
	folders, err := finder.FolderList(ctx, "*")
	if err != nil {
		return nil, err
	}

	prefix := path.Join(dc, "vm")
	// base case of /<datacenter>/vm is covered by ""
	data := []string{""}
	for _, f := range folders {
		if strings.HasPrefix(f.InventoryPath, prefix) {
			data = append(data, f.InventoryPath)
		}
	}

	return data, nil
}

func listHosts(ctx context.Context, finder *find.Finder) ([]string, error) {
	hosts, err := finder.HostSystemList(ctx, "*")
	if err != nil {
		return nil, err
	}

	data := []string{""} //blank default

	for _, h := range hosts {
		data = append(data, h.InventoryPath)
	}

	return data, nil
}

func listNetworks(ctx context.Context, finder *find.Finder) ([]string, error) {
	networks, err := finder.NetworkList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, net := range networks {
		data = append(data, net.GetInventoryPath())
	}

	return data, nil
}

func listVirtualMachines(ctx context.Context, finder *find.Finder, t string) ([]string, error) {
	if t == "" {
		t = "both"
	}
	vms, err := finder.VirtualMachineList(ctx, "*")
	if err != nil {
		return nil, err
	}

	var data []string
	for _, vm := range vms {
		switch t {
		case "both":
			data = append(data, vm.InventoryPath)
		case "templates":
			if isTemplate, err := vm.IsTemplate(ctx); err == nil && isTemplate {
				data = append(data, vm.InventoryPath)
			}
		case "virtual-machines":
			if isTemplate, err := vm.IsTemplate(ctx); err == nil && !isTemplate {
				data = append(data, vm.InventoryPath)
			}
		}
	}

	sort.Strings(data)
	return data, nil
}

func getSoapFinder(ctx context.Context, cc *corev1.Secret, dc string) (*find.Finder, error) {
	c, err := getSoapClient(ctx, cc)
	if err != nil {
		return nil, err
	}

	finder := find.NewFinder(c.Client, true)

	if dc != "" {
		dataCenter, err := finder.Datacenter(ctx, dc)
		if err != nil {
			return nil, err
		}

		finder.SetDatacenter(dataCenter)
	}

	return finder, nil
}

func getSoapClient(ctx context.Context, cc *corev1.Secret) (*govmomi.Client, error) {
	u, err := url.Parse(fmt.Sprintf("https://%s:%s/sdk", cc.Data[dataFields["host"]], cc.Data[dataFields["port"]]))
	if err != nil {
		return nil, err
	}

	u.User = url.UserPassword(string(cc.Data[dataFields["username"]]), string(cc.Data[dataFields["password"]]))
	return govmomi.NewClient(ctx, u, true)
}

func getContentLibraryManager(ctx context.Context, cc *corev1.Secret) (*library.Manager, error) {
	soap, err := getSoapClient(ctx, cc)
	if err != nil {
		return nil, err
	}

	mgr := library.NewManager(rest.NewClient(soap.Client))
	ui := url.UserPassword(string(cc.Data[dataFields["username"]]), string(cc.Data[dataFields["password"]]))

	return mgr, mgr.Login(ctx, ui)
}

func getTagsManager(ctx context.Context, cc *corev1.Secret) (*tags.Manager, error) {
	soap, err := getSoapClient(ctx, cc)
	if err != nil {
		return nil, err
	}

	mgr := tags.NewManager(rest.NewClient(soap.Client))
	ui := url.UserPassword(string(cc.Data[dataFields["username"]]), string(cc.Data[dataFields["password"]]))

	return mgr, mgr.Login(ctx, ui)
}

func getCustomFieldsManager(ctx context.Context, cc *corev1.Secret) (*object.CustomFieldsManager, error) {
	soap, err := getSoapClient(ctx, cc)
	if err != nil {
		return nil, err
	}

	return object.GetCustomFieldsManager(soap.Client)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package hostport

import (
	"fmt"
	"net"
	"strings"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/api/v1"
	utiliptables "k8s.io/kubernetes/pkg/util/iptables"
)

const (
	// the hostport chain
	kubeHostportsChain utiliptables.Chain = "KUBE-HOSTPORTS"
	// prefix for hostport chains
	kubeHostportChainPrefix string = "KUBE-HP-"
)

// PortMapping represents a network port in a container
type PortMapping struct {
	Name          string
	HostPort      int32
	ContainerPort int32
	Protocol      v1.Protocol
	HostIP        string
}

// PodPortMapping represents a pod's network state and associated container port mappings
type PodPortMapping struct {
	Namespace    string
	Name         string
	PortMappings []*PortMapping
	HostNetwork  bool
	IP           net.IP
}

// ConstructPodPortMapping creates a PodPortMapping from the ports specified in the pod's
// containers.
func ConstructPodPortMapping(pod *v1.Pod, podIP net.IP) *PodPortMapping {
	portMappings := make([]*PortMapping, 0)
	for _, c := range pod.Spec.Containers {
		for _, port := range c.Ports {
			portMappings = append(portMappings, &PortMapping{
				Name:          port.Name,
				HostPort:      port.HostPort,
				ContainerPort: port.ContainerPort,
				Protocol:      port.Protocol,
				HostIP:        port.HostIP,
			})
		}
	}

	return &PodPortMapping{
		Namespace:    pod.Namespace,
		Name:         pod.Name,
		PortMappings: portMappings,
		HostNetwork:  pod.Spec.HostNetwork,
		IP:           podIP,
	}
}

type hostport struct {
	port     int32
	protocol string
}

type hostportOpener func(*hostport) (closeable, error)

type closeable interface {
	Close() error
}

func openLocalPort(hp *hostport) (closeable, error) {
	// For ports on node IPs, open the actual port and hold it, even though we
	// use iptables to redirect traffic.
	// This ensures a) that it's safe to use that port and b) that (a) stays
	// true.  The risk is that some process on the node (e.g. sshd or kubelet)
	// is using a port and we give that same port out to a Service.  That would
	// be bad because iptables would silently claim the traffic but the process
	// would never know.
	// NOTE: We should not need to have a real listen()ing socket - bind()
	// should be enough, but I can't figure out a way to e2e test without
	// it.  Tools like 'ss' and 'netstat' do not show sockets that are
	// bind()ed but not listen()ed, and at least the default debian netcat
	// has no way to avoid about 10 seconds of retries.
	var socket closeable
	switch hp.protocol {
	case "tcp":
		listener, err := net.Listen("tcp", fmt.Sprintf(":%d", hp.port))
		if err != nil {
			return nil, err
		}
		socket = listener
	case "udp":
		addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", hp.port))
		if err != nil {
			return nil, err
		}
		conn, err := net.ListenUDP("udp", addr)
		if err != nil {
			return nil, err
		}
		socket = conn
	default:
		return nil, fmt.Errorf("unknown protocol %q", hp.protocol)
	}
	glog.V(3).Infof("Opened local port %s", hp.String())
	return socket, nil
}

// openHostports opens all given hostports using the given hostportOpener
// If encounter any error, clean up and return the error
// If all ports are opened successfully, return the hostport and socket mapping
// TODO: move openHostports and closeHostports into a common struct
func openHostports(portOpener hostportOpener, podPortMapping *PodPortMapping) (map[hostport]closeable, error) {
	var retErr error
	ports := make(map[hostport]closeable)
	for _, pm := range podPortMapping.PortMappings {
		if pm.HostPort <= 0 {
			continue
		}
		hp := portMappingToHostport(pm)
		socket, err := portOpener(&hp)
		if err != nil {
			retErr = fmt.Errorf("cannot open hostport %d for pod %s: %v", pm.HostPort, getPodFullName(podPortMapping), err)
			break
		}
		ports[hp] = socket
	}

	// If encounter any error, close all hostports that just got opened.
	if retErr != nil {
		for hp, socket := range ports {
			if err := socket.Close(); err != nil {
				glog.Errorf("Cannot clean up hostport %d for pod %s: %v", hp.port, getPodFullName(podPortMapping), err)
			}
		}
		return nil, retErr
	}
	return ports, nil
}

// portMappingToHostport creates hostport structure based on input portmapping
func portMappingToHostport(portMapping *PortMapping) hostport {
	return hostport{
		port:     portMapping.HostPort,
		protocol: strings.ToLower(string(portMapping.Protocol)),
	}
}

// ensureKubeHostportChains ensures the KUBE-HOSTPORTS chain is setup correctly
func ensureKubeHostportChains(iptables utiliptables.Interface, natInterfaceName string) error {
	glog.V(4).Info("Ensuring kubelet hostport chains")
	// Ensure kubeHostportChain
	if _, err := iptables.EnsureChain(utiliptables.TableNAT, kubeHostportsChain); err != nil {
		return fmt.Errorf("Failed to ensure that %s chain %s exists: %v", utiliptables.TableNAT, kubeHostportsChain, err)
	}
	tableChainsNeedJumpServices := []struct {
		table utiliptables.Table
		chain utiliptables.Chain
	}{
		{utiliptables.TableNAT, utiliptables.ChainOutput},
		{utiliptables.TableNAT, utiliptables.ChainPrerouting},
	}
	args := []string{"-m", "comment", "--comment", "kube hostport portals",
		"-m", "addrtype", "--dst-type", "LOCAL",
		"-j", string(kubeHostportsChain)}
	for _, tc := range tableChainsNeedJumpServices {
		if _, err := iptables.EnsureRule(utiliptables.Prepend, tc.table, tc.chain, args...); err != nil {
			return fmt.Errorf("Failed to ensure that %s chain %s jumps to %s: %v", tc.table, tc.chain, kubeHostportsChain, err)
		}
	}
	// Need to SNAT traffic from localhost
	args = []string{"-m", "comment", "--comment", "SNAT for localhost access to hostports", "-o", natInterfaceName, "-s", "127.0.0.0/8", "-j", "MASQUERADE"}
	if _, err := iptables.EnsureRule(utiliptables.Append, utiliptables.TableNAT, utiliptables.ChainPostrouting, args...); err != nil {
		return fmt.Errorf("Failed to ensure that %s chain %s jumps to MASQUERADE: %v", utiliptables.TableNAT, utiliptables.ChainPostrouting, err)
	}
	return nil
}
<commit_msg>append KUBE-HOSTPORTS to system chains instead of prepend<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package hostport

import (
	"fmt"
	"net"
	"strings"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/api/v1"
	utiliptables "k8s.io/kubernetes/pkg/util/iptables"
)

const (
	// the hostport chain
	kubeHostportsChain utiliptables.Chain = "KUBE-HOSTPORTS"
	// prefix for hostport chains
	kubeHostportChainPrefix string = "KUBE-HP-"
)

// PortMapping represents a network port in a container
type PortMapping struct {
	Name          string
	HostPort      int32
	ContainerPort int32
	Protocol      v1.Protocol
	HostIP        string
}

// PodPortMapping represents a pod's network state and associated container port mappings
type PodPortMapping struct {
	Namespace    string
	Name         string
	PortMappings []*PortMapping
	HostNetwork  bool
	IP           net.IP
}

// ConstructPodPortMapping creates a PodPortMapping from the ports specified in the pod's
// containers.
func ConstructPodPortMapping(pod *v1.Pod, podIP net.IP) *PodPortMapping {
	portMappings := make([]*PortMapping, 0)
	for _, c := range pod.Spec.Containers {
		for _, port := range c.Ports {
			portMappings = append(portMappings, &PortMapping{
				Name:          port.Name,
				HostPort:      port.HostPort,
				ContainerPort: port.ContainerPort,
				Protocol:      port.Protocol,
				HostIP:        port.HostIP,
			})
		}
	}

	return &PodPortMapping{
		Namespace:    pod.Namespace,
		Name:         pod.Name,
		PortMappings: portMappings,
		HostNetwork:  pod.Spec.HostNetwork,
		IP:           podIP,
	}
}

type hostport struct {
	port     int32
	protocol string
}

type hostportOpener func(*hostport) (closeable, error)

type closeable interface {
	Close() error
}

func openLocalPort(hp *hostport) (closeable, error) {
	// For ports on node IPs, open the actual port and hold it, even though we
	// use iptables to redirect traffic.
	// This ensures a) that it's safe to use that port and b) that (a) stays
	// true.  The risk is that some process on the node (e.g. sshd or kubelet)
	// is using a port and we give that same port out to a Service.  That would
	// be bad because iptables would silently claim the traffic but the process
	// would never know.
	// NOTE: We should not need to have a real listen()ing socket - bind()
	// should be enough, but I can't figure out a way to e2e test without
	// it.  Tools like 'ss' and 'netstat' do not show sockets that are
	// bind()ed but not listen()ed, and at least the default debian netcat
	// has no way to avoid about 10 seconds of retries.
	var socket closeable
	switch hp.protocol {
	case "tcp":
		listener, err := net.Listen("tcp", fmt.Sprintf(":%d", hp.port))
		if err != nil {
			return nil, err
		}
		socket = listener
	case "udp":
		addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", hp.port))
		if err != nil {
			return nil, err
		}
		conn, err := net.ListenUDP("udp", addr)
		if err != nil {
			return nil, err
		}
		socket = conn
	default:
		return nil, fmt.Errorf("unknown protocol %q", hp.protocol)
	}
	glog.V(3).Infof("Opened local port %s", hp.String())
	return socket, nil
}

// openHostports opens all given hostports using the given hostportOpener
// If encounter any error, clean up and return the error
// If all ports are opened successfully, return the hostport and socket mapping
// TODO: move openHostports and closeHostports into a common struct
func openHostports(portOpener hostportOpener, podPortMapping *PodPortMapping) (map[hostport]closeable, error) {
	var retErr error
	ports := make(map[hostport]closeable)
	for _, pm := range podPortMapping.PortMappings {
		if pm.HostPort <= 0 {
			continue
		}
		hp := portMappingToHostport(pm)
		socket, err := portOpener(&hp)
		if err != nil {
			retErr = fmt.Errorf("cannot open hostport %d for pod %s: %v", pm.HostPort, getPodFullName(podPortMapping), err)
			break
		}
		ports[hp] = socket
	}

	// If encounter any error, close all hostports that just got opened.
	if retErr != nil {
		for hp, socket := range ports {
			if err := socket.Close(); err != nil {
				glog.Errorf("Cannot clean up hostport %d for pod %s: %v", hp.port, getPodFullName(podPortMapping), err)
			}
		}
		return nil, retErr
	}
	return ports, nil
}

// portMappingToHostport creates hostport structure based on input portmapping
func portMappingToHostport(portMapping *PortMapping) hostport {
	return hostport{
		port:     portMapping.HostPort,
		protocol: strings.ToLower(string(portMapping.Protocol)),
	}
}

// ensureKubeHostportChains ensures the KUBE-HOSTPORTS chain is setup correctly
func ensureKubeHostportChains(iptables utiliptables.Interface, natInterfaceName string) error {
	glog.V(4).Info("Ensuring kubelet hostport chains")
	// Ensure kubeHostportChain
	if _, err := iptables.EnsureChain(utiliptables.TableNAT, kubeHostportsChain); err != nil {
		return fmt.Errorf("Failed to ensure that %s chain %s exists: %v", utiliptables.TableNAT, kubeHostportsChain, err)
	}
	tableChainsNeedJumpServices := []struct {
		table utiliptables.Table
		chain utiliptables.Chain
	}{
		{utiliptables.TableNAT, utiliptables.ChainOutput},
		{utiliptables.TableNAT, utiliptables.ChainPrerouting},
	}
	args := []string{"-m", "comment", "--comment", "kube hostport portals",
		"-m", "addrtype", "--dst-type", "LOCAL",
		"-j", string(kubeHostportsChain)}
	for _, tc := range tableChainsNeedJumpServices {
		// KUBE-HOSTPORTS chain needs to be appended to the system chains.
		// This ensures KUBE-SERVICES chain gets processed first.
		// Since rules in KUBE-HOSTPORTS chain matches broader cases, allow the more specific rules to be processed first.
		if _, err := iptables.EnsureRule(utiliptables.Append, tc.table, tc.chain, args...); err != nil {
			return fmt.Errorf("Failed to ensure that %s chain %s jumps to %s: %v", tc.table, tc.chain, kubeHostportsChain, err)
		}
	}
	// Need to SNAT traffic from localhost
	args = []string{"-m", "comment", "--comment", "SNAT for localhost access to hostports", "-o", natInterfaceName, "-s", "127.0.0.0/8", "-j", "MASQUERADE"}
	if _, err := iptables.EnsureRule(utiliptables.Append, utiliptables.TableNAT, utiliptables.ChainPostrouting, args...); err != nil {
		return fmt.Errorf("Failed to ensure that %s chain %s jumps to MASQUERADE: %v", utiliptables.TableNAT, utiliptables.ChainPostrouting, err)
	}
	return nil
}
<|endoftext|>


<commit_before>// generated by hasher -type=Hash -file=hash.go; DO NOT EDIT
package svg

// github.com/tdewolff/hasher
//go:generate hasher -type=Hash -file=hash.go
type Hash uint32

// String returns the hash' name.
func (i Hash) String() string {
	start := uint32(i >> 8)
	n := uint32(i & 0xff)
	if start+n > uint32(len(_Hash_text)) {
		return ""
	}
	return _Hash_text[start : start+n]
}

// Hash returns the hash whose name is s. It returns zero if there is no
// such hash. It is case sensitive.
func ToHash(s []byte) Hash {
	if len(s) == 0 || len(s) > _Hash_maxLen {
		return 0
	}
	h := _Hash_fnv(s)
	if i := _Hash_table[h&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) && _Hash_match(_Hash_string(i), s) {
		return i
	}
	if i := _Hash_table[(h>>16)&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) && _Hash_match(_Hash_string(i), s) {
		return i
	}
	return 0
}

// _Hash_fnv computes the FNV hash with an arbitrary starting value h.
func _Hash_fnv(s []byte) uint32 {
	h := uint32(_Hash_hash0)
	for i := range s {
		h ^= uint32(s[i])
		h *= 16777619
	}
	return h
}

func _Hash_match(s string, t []byte) bool {
	for i, c := range t {
		if s[i] != c {
			return false
		}
	}
	return true
}

func _Hash_string(i Hash) string {
	return _Hash_text[i>>8 : i>>8+i&0xff]
}

const _Hash_hash0 = 0x9acb0442
const _Hash_maxLen = 7
const _Hash_text = "pathstylesvgversion"

const (
	G       Hash = 0xb01
	Path    Hash = 0x4
	Style   Hash = 0x405
	Svg     Hash = 0x903
	Version Hash = 0xc07
)

var _Hash_table = [1 << 3]Hash{
	0x0: 0x903, // svg
	0x1: 0x4,   // path
	0x4: 0xc07, // version
	0x6: 0x405, // style
	0x7: 0xb01, // g
}
<commit_msg>D attribute added<commit_after>// generated by hasher -type=Hash -file=hash.go; DO NOT EDIT
package svg

// github.com/tdewolff/hasher
//go:generate hasher -type=Hash -file=hash.go
type Hash uint32

// String returns the hash' name.
func (i Hash) String() string {
	start := uint32(i >> 8)
	n := uint32(i & 0xff)
	if start+n > uint32(len(_Hash_text)) {
		return ""
	}
	return _Hash_text[start : start+n]
}

// Hash returns the hash whose name is s. It returns zero if there is no
// such hash. It is case sensitive.
func ToHash(s []byte) Hash {
	if len(s) == 0 || len(s) > _Hash_maxLen {
		return 0
	}
	h := _Hash_fnv(s)
	if i := _Hash_table[h&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) && _Hash_match(_Hash_string(i), s) {
		return i
	}
	if i := _Hash_table[(h>>16)&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) && _Hash_match(_Hash_string(i), s) {
		return i
	}
	return 0
}

// _Hash_fnv computes the FNV hash with an arbitrary starting value h.
func _Hash_fnv(s []byte) uint32 {
	h := uint32(_Hash_hash0)
	for i := range s {
		h ^= uint32(s[i])
		h *= 16777619
	}
	return h
}

func _Hash_match(s string, t []byte) bool {
	for i, c := range t {
		if s[i] != c {
			return false
		}
	}
	return true
}

func _Hash_string(i Hash) string {
	return _Hash_text[i>>8 : i>>8+i&0xff]
}

const _Hash_hash0 = 0x9acb0442
const _Hash_maxLen = 7
const _Hash_text = "dpathstylesvgversion"

const (
	D       Hash = 0x1
	G       Hash = 0xc01
	Path    Hash = 0x104
	Style   Hash = 0x505
	Svg     Hash = 0xa03
	Version Hash = 0xd07
)

var _Hash_table = [1 << 3]Hash{
	0x0: 0xa03, // svg
	0x1: 0x104, // path
	0x2: 0x1,   // d
	0x4: 0xd07, // version
	0x6: 0x505, // style
	0x7: 0xc01, // g
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/aleasoluciones/gosnmpquerier"
)

const (
	CONTENTION = 4
)

func rootHandler(querier gosnmpquerier.SyncQuerier, w http.ResponseWriter, r *http.Request) {

	cmd, _ := gosnmpquerier.ConvertCommand(r.FormValue("cmd"))
	query := gosnmpquerier.Query{
		Cmd:         cmd,
		Community:   r.FormValue("community"),
		Oids:        []string{r.FormValue("oid")},
		Timeout:     time.Duration(10) * time.Second,
		Retries:     1,
		Destination: r.FormValue("destination"),
	}
	processed := querier.ExecuteQuery(query)
	jsonProcessed, err := gosnmpquerier.ToJson(&processed)
	if err != nil {
		fmt.Fprint(w, err)
	}
	fmt.Fprint(w, jsonProcessed)

}

func main() {

	querier := gosnmpquerier.NewSyncQuerier(CONTENTION, 3, 3*time.Second)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		rootHandler(querier, w, r)
	})
	log.Println("Server running in 'http://0.0.0.0:8080' ...")
	log.Fatal(http.ListenAndServe(":8080", nil))
}
<commit_msg>License<commit_after>// Copyright 2014 The http2snmp Authors. All rights reserved.  Use of this
// source code is governed by a MIT-style license that can be found in the
// LICENSE file.

package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/aleasoluciones/gosnmpquerier"
)

const (
	CONTENTION = 4
)

func rootHandler(querier gosnmpquerier.SyncQuerier, w http.ResponseWriter, r *http.Request) {

	cmd, _ := gosnmpquerier.ConvertCommand(r.FormValue("cmd"))
	query := gosnmpquerier.Query{
		Cmd:         cmd,
		Community:   r.FormValue("community"),
		Oids:        []string{r.FormValue("oid")},
		Timeout:     time.Duration(10) * time.Second,
		Retries:     1,
		Destination: r.FormValue("destination"),
	}
	processed := querier.ExecuteQuery(query)
	jsonProcessed, err := gosnmpquerier.ToJson(&processed)
	if err != nil {
		fmt.Fprint(w, err)
	}
	fmt.Fprint(w, jsonProcessed)

}

func main() {

	querier := gosnmpquerier.NewSyncQuerier(CONTENTION, 3, 3*time.Second)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		rootHandler(querier, w, r)
	})
	log.Println("Server running in 'http://0.0.0.0:8080' ...")
	log.Fatal(http.ListenAndServe(":8080", nil))
}
<|endoftext|>


<commit_before>package rubycommand

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/bitrise-io/go-utils/command"
	"github.com/bitrise-io/go-utils/pathutil"
)

const (
	systemRubyPth  = "/usr/bin/ruby"
	brewRubyPth    = "/usr/local/bin/ruby"
	brewRubyPthAlt = "/usr/local/opt/ruby/bin/ruby"
)

// InstallType ...
type InstallType int8

const (
	// Unkown ...
	Unkown InstallType = iota
	// SystemRuby ...
	SystemRuby
	// BrewRuby ...
	BrewRuby
	// RVMRuby ...
	RVMRuby
	// RbenvRuby ...
	RbenvRuby
)

func cmdExist(slice ...string) bool {
	if len(slice) == 0 {
		return false
	}

	cmd, err := command.NewWithParams(slice...)
	if err != nil {
		return false
	}

	return (cmd.Run() == nil)
}

func installType() InstallType {
	whichRuby, err := command.New("which", "ruby").RunAndReturnTrimmedCombinedOutput()
	if err != nil {
		return Unkown
	}

	installType := Unkown
	if whichRuby == systemRubyPth {
		installType = SystemRuby
	} else if whichRuby == brewRubyPth {
		installType = BrewRuby
	} else if whichRuby == brewRubyPthAlt {
		installType = BrewRuby
	} else if cmdExist("rvm", "-v") {
		installType = RVMRuby
	} else if cmdExist("rbenv", "-v") {
		installType = RbenvRuby
	}

	return installType
}

func sudoNeeded(installType InstallType, slice ...string) bool {
	if installType != SystemRuby {
		return false
	}

	if len(slice) < 2 {
		return false
	}

	name := slice[0]
	if name == "bundle" {
		command := slice[1]
		/*
			bundle command can contain version:
			`bundle _2.0.1_ install`
		*/
		const bundleVersionMarker = "_"
		if strings.HasPrefix(slice[1], bundleVersionMarker) && strings.HasSuffix(slice[1], bundleVersionMarker) {
			if len(slice) < 3 {
				return false
			}
			command = slice[2]
		}

		return (command == "install" || command == "update")
	} else if name == "gem" {
		command := slice[1]
		return (command == "install" || command == "uninstall")
	}

	return false
}

// NewWithParams ...
func NewWithParams(params ...string) (*command.Model, error) {
	rubyInstallType := installType()
	if rubyInstallType == Unkown {
		return nil, errors.New("unknown ruby installation type")
	}

	if sudoNeeded(rubyInstallType, params...) {
		params = append([]string{"sudo"}, params...)
	}

	return command.NewWithParams(params...)
}

// NewFromSlice ...
func NewFromSlice(slice []string) (*command.Model, error) {
	return NewWithParams(slice...)
}

// New ...
func New(name string, args ...string) (*command.Model, error) {
	slice := append([]string{name}, args...)
	return NewWithParams(slice...)
}

// GemUpdate ...
func GemUpdate(gem string) ([]*command.Model, error) {
	cmds := []*command.Model{}

	cmd, err := New("gem", "update", gem, "--no-document")
	if err != nil {
		return []*command.Model{}, err
	}

	cmds = append(cmds, cmd)

	rubyInstallType := installType()
	if rubyInstallType == RbenvRuby {
		cmd, err := New("rbenv", "rehash")
		if err != nil {
			return []*command.Model{}, err
		}

		cmds = append(cmds, cmd)
	}

	return cmds, nil
}

// GemInstall ...
func GemInstall(gem, version string) ([]*command.Model, error) {
	cmds := []*command.Model{}

	slice := []string{"gem", "install", gem, "--no-document"}
	if version != "" {
		slice = append(slice, "-v", version)
	}

	cmd, err := NewFromSlice(slice)
	if err != nil {
		return []*command.Model{}, err
	}

	cmds = append(cmds, cmd)

	rubyInstallType := installType()
	if rubyInstallType == RbenvRuby {
		cmd, err := New("rbenv", "rehash")
		if err != nil {
			return []*command.Model{}, err
		}

		cmds = append(cmds, cmd)
	}

	return cmds, nil
}

func findGemInList(gemList, gem, version string) (bool, error) {
	// minitest (5.10.1, 5.9.1, 5.9.0, 5.8.3, 4.7.5)
	pattern := fmt.Sprintf(`^%s \(.*%s.*\)`, gem, version)
	re := regexp.MustCompile(pattern)

	reader := bytes.NewReader([]byte(gemList))
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		line := scanner.Text()
		match := re.FindString(line)
		if match != "" {
			return true, nil
		}
	}
	if err := scanner.Err(); err != nil {
		return false, err
	}
	return false, nil
}

// IsGemInstalled ...
func IsGemInstalled(gem, version string) (bool, error) {
	cmd, err := New("gem", "list")
	if err != nil {
		return false, err
	}

	out, err := cmd.RunAndReturnTrimmedCombinedOutput()
	if err != nil {
		return false, fmt.Errorf("%s: error: %s", out, err)
	}

	return findGemInList(out, gem, version)
}

func isSpecifiedRbenvRubyInstalled(message string) (bool, string, error) {
	//
	// Not installed
	reg, err := regexp.Compile("rbenv: version \x60.*' is not installed") // \x60 == ` (The go linter suggested to use the hex code instead)
	if err != nil {
		return false, "", fmt.Errorf("failed to parse regex ( %s ) on the error message, error: %s", "rbenv: version \x60.*' is not installed", err) // \x60 == ` (The go linter suggested to use the hex code instead)
	}

	var version string
	if reg.MatchString(message) {
		message := reg.FindString(message)
		version = strings.Split(strings.Split(message, "`")[1], "'")[0]
		return false, version, nil
	}

	//
	// Installed
	reg, err = regexp.Compile(".* \\(set by")
	if err != nil {
		return false, "", fmt.Errorf("failed to parse regex ( %s ) on the error message, error: %s", ".* \\(set by", err)
	}

	if reg.MatchString(message) {
		s := reg.FindString(message)
		version = strings.Split(s, " (set by")[0]
		return true, version, nil
	}
	return false, version, nil
}

// IsSpecifiedRbenvRubyInstalled checks if the selected ruby version is installed via rbenv.
// Ruby version is set by
// 1. The RBENV_VERSION environment variable
// 2. The first .ruby-version file found by searching the directory of the script you are executing and each of its
// parent directories until reaching the root of your filesystem.
// 3.The first .ruby-version file found by searching the current working directory and each of its parent directories
// until reaching the root of your filesystem.
// 4. The global ~/.rbenv/version file. You can modify this file using the rbenv global command.
// src: https://github.com/rbenv/rbenv#choosing-the-ruby-version
func IsSpecifiedRbenvRubyInstalled(workdir string) (bool, string, error) {
	absWorkdir, err := pathutil.AbsPath(workdir)
	if err != nil {
		return false, "", fmt.Errorf("failed to get absolute path for ( %s ), error: %s", workdir, err)
	}

	cmd := command.New("rbenv", "version").SetDir(absWorkdir)
	out, err := cmd.RunAndReturnTrimmedCombinedOutput()
	if err != nil {
		return false, "", fmt.Errorf("failed to check installed ruby version, %s error: %s", out, err)
	}
	return isSpecifiedRbenvRubyInstalled(out)
}
<commit_msg>add RubyInstallType()  (#102)<commit_after>package rubycommand

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/bitrise-io/go-utils/command"
	"github.com/bitrise-io/go-utils/pathutil"
)

const (
	systemRubyPth  = "/usr/bin/ruby"
	brewRubyPth    = "/usr/local/bin/ruby"
	brewRubyPthAlt = "/usr/local/opt/ruby/bin/ruby"
)

// InstallType ...
type InstallType int8

const (
	// Unkown ...
	Unkown InstallType = iota
	// SystemRuby ...
	SystemRuby
	// BrewRuby ...
	BrewRuby
	// RVMRuby ...
	RVMRuby
	// RbenvRuby ...
	RbenvRuby
)

func cmdExist(slice ...string) bool {
	if len(slice) == 0 {
		return false
	}

	cmd, err := command.NewWithParams(slice...)
	if err != nil {
		return false
	}

	return (cmd.Run() == nil)
}

// RubyInstallType returns which version manager was used for the ruby install
func RubyInstallType() InstallType {
	whichRuby, err := command.New("which", "ruby").RunAndReturnTrimmedCombinedOutput()
	if err != nil {
		return Unkown
	}

	installType := Unkown
	if whichRuby == systemRubyPth {
		installType = SystemRuby
	} else if whichRuby == brewRubyPth {
		installType = BrewRuby
	} else if whichRuby == brewRubyPthAlt {
		installType = BrewRuby
	} else if cmdExist("rvm", "-v") {
		installType = RVMRuby
	} else if cmdExist("rbenv", "-v") {
		installType = RbenvRuby
	}

	return installType
}

func sudoNeeded(installType InstallType, slice ...string) bool {
	if installType != SystemRuby {
		return false
	}

	if len(slice) < 2 {
		return false
	}

	name := slice[0]
	if name == "bundle" {
		command := slice[1]
		/*
			bundle command can contain version:
			`bundle _2.0.1_ install`
		*/
		const bundleVersionMarker = "_"
		if strings.HasPrefix(slice[1], bundleVersionMarker) && strings.HasSuffix(slice[1], bundleVersionMarker) {
			if len(slice) < 3 {
				return false
			}
			command = slice[2]
		}

		return (command == "install" || command == "update")
	} else if name == "gem" {
		command := slice[1]
		return (command == "install" || command == "uninstall")
	}

	return false
}

// NewWithParams ...
func NewWithParams(params ...string) (*command.Model, error) {
	rubyInstallType := RubyInstallType()
	if rubyInstallType == Unkown {
		return nil, errors.New("unknown ruby installation type")
	}

	if sudoNeeded(rubyInstallType, params...) {
		params = append([]string{"sudo"}, params...)
	}

	return command.NewWithParams(params...)
}

// NewFromSlice ...
func NewFromSlice(slice []string) (*command.Model, error) {
	return NewWithParams(slice...)
}

// New ...
func New(name string, args ...string) (*command.Model, error) {
	slice := append([]string{name}, args...)
	return NewWithParams(slice...)
}

// GemUpdate ...
func GemUpdate(gem string) ([]*command.Model, error) {
	cmds := []*command.Model{}

	cmd, err := New("gem", "update", gem, "--no-document")
	if err != nil {
		return []*command.Model{}, err
	}

	cmds = append(cmds, cmd)

	rubyInstallType := RubyInstallType()
	if rubyInstallType == RbenvRuby {
		cmd, err := New("rbenv", "rehash")
		if err != nil {
			return []*command.Model{}, err
		}

		cmds = append(cmds, cmd)
	}

	return cmds, nil
}

// GemInstall ...
func GemInstall(gem, version string) ([]*command.Model, error) {
	cmds := []*command.Model{}

	slice := []string{"gem", "install", gem, "--no-document"}
	if version != "" {
		slice = append(slice, "-v", version)
	}

	cmd, err := NewFromSlice(slice)
	if err != nil {
		return []*command.Model{}, err
	}

	cmds = append(cmds, cmd)

	rubyInstallType := RubyInstallType()
	if rubyInstallType == RbenvRuby {
		cmd, err := New("rbenv", "rehash")
		if err != nil {
			return []*command.Model{}, err
		}

		cmds = append(cmds, cmd)
	}

	return cmds, nil
}

func findGemInList(gemList, gem, version string) (bool, error) {
	// minitest (5.10.1, 5.9.1, 5.9.0, 5.8.3, 4.7.5)
	pattern := fmt.Sprintf(`^%s \(.*%s.*\)`, gem, version)
	re := regexp.MustCompile(pattern)

	reader := bytes.NewReader([]byte(gemList))
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		line := scanner.Text()
		match := re.FindString(line)
		if match != "" {
			return true, nil
		}
	}
	if err := scanner.Err(); err != nil {
		return false, err
	}
	return false, nil
}

// IsGemInstalled ...
func IsGemInstalled(gem, version string) (bool, error) {
	cmd, err := New("gem", "list")
	if err != nil {
		return false, err
	}

	out, err := cmd.RunAndReturnTrimmedCombinedOutput()
	if err != nil {
		return false, fmt.Errorf("%s: error: %s", out, err)
	}

	return findGemInList(out, gem, version)
}

func isSpecifiedRbenvRubyInstalled(message string) (bool, string, error) {
	//
	// Not installed
	reg, err := regexp.Compile("rbenv: version \x60.*' is not installed") // \x60 == ` (The go linter suggested to use the hex code instead)
	if err != nil {
		return false, "", fmt.Errorf("failed to parse regex ( %s ) on the error message, error: %s", "rbenv: version \x60.*' is not installed", err) // \x60 == ` (The go linter suggested to use the hex code instead)
	}

	var version string
	if reg.MatchString(message) {
		message := reg.FindString(message)
		version = strings.Split(strings.Split(message, "`")[1], "'")[0]
		return false, version, nil
	}

	//
	// Installed
	reg, err = regexp.Compile(".* \\(set by")
	if err != nil {
		return false, "", fmt.Errorf("failed to parse regex ( %s ) on the error message, error: %s", ".* \\(set by", err)
	}

	if reg.MatchString(message) {
		s := reg.FindString(message)
		version = strings.Split(s, " (set by")[0]
		return true, version, nil
	}
	return false, version, nil
}

// IsSpecifiedRbenvRubyInstalled checks if the selected ruby version is installed via rbenv.
// Ruby version is set by
// 1. The RBENV_VERSION environment variable
// 2. The first .ruby-version file found by searching the directory of the script you are executing and each of its
// parent directories until reaching the root of your filesystem.
// 3.The first .ruby-version file found by searching the current working directory and each of its parent directories
// until reaching the root of your filesystem.
// 4. The global ~/.rbenv/version file. You can modify this file using the rbenv global command.
// src: https://github.com/rbenv/rbenv#choosing-the-ruby-version
func IsSpecifiedRbenvRubyInstalled(workdir string) (bool, string, error) {
	absWorkdir, err := pathutil.AbsPath(workdir)
	if err != nil {
		return false, "", fmt.Errorf("failed to get absolute path for ( %s ), error: %s", workdir, err)
	}

	cmd := command.New("rbenv", "version").SetDir(absWorkdir)
	out, err := cmd.RunAndReturnTrimmedCombinedOutput()
	if err != nil {
		return false, "", fmt.Errorf("failed to check installed ruby version, %s error: %s", out, err)
	}
	return isSpecifiedRbenvRubyInstalled(out)
}
<|endoftext|>


<commit_before>package comments

import (
	"log"

	"github.com/google/go-github/github"
)

var (
	pendingFeedbackLabel = "pending-feedback"

	HandlerPendingFeedbackLabel = func(client *github.Client, event github.IssueCommentEvent) error {
		// if the comment is from the issue author & issue has the "pending-feedback", remove the label
		if hasLabel(event.Issue.Labels, pendingFeedbackLabel) && event.Sender.ID == event.Issue.User.ID {
			owner, name, number := *event.Repo.Owner.Login, *event.Repo.Name, *event.Issue.Number
			_, err := client.Issues.RemoveLabelForIssue(owner, name, number, pendingFeedbackLabel)
			if err != nil {
				log.Printf("[pending_feedback_label]: error removing label (%s/%s#%d): %v", owner, name, number, err)
				return err
			}
			return nil
		}
		return nil
	}
)

func hasLabel(labels []github.Label, desiredLabel string) bool {
	for _, label := range labels {
		if *label.Name == desiredLabel {
			return true
		}
	}
	return false
}
<commit_msg>AUTO_REPLY_DEBUG flag<commit_after>package comments

import (
	"log"

	"github.com/google/go-github/github"
)

var (
	pendingFeedbackLabel = "pending-feedback"

	HandlerPendingFeedbackLabel = func(client *github.Client, event github.IssueCommentEvent) error {
		// if the comment is from the issue author & issue has the "pending-feedback", remove the label

		if os.Getenv("AUTO_REPLY_DEBUG") == "true" {
			log.Println("received event:", event)
		}

		if hasLabel(event.Issue.Labels, pendingFeedbackLabel) && event.Sender.ID == event.Issue.User.ID {
			owner, name, number := *event.Repo.Owner.Login, *event.Repo.Name, *event.Issue.Number
			_, err := client.Issues.RemoveLabelForIssue(owner, name, number, pendingFeedbackLabel)
			if err != nil {
				log.Printf("[pending_feedback_label]: error removing label (%s/%s#%d): %v", owner, name, number, err)
				return err
			}
			return nil
		}
		return nil
	}
)

func hasLabel(labels []github.Label, desiredLabel string) bool {
	for _, label := range labels {
		if *label.Name == desiredLabel {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"path"
	"flag"
	"http"
	"log"
	"strings"
	"fmt"
	"time"
	"recipe"
)

func NotFoundHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Cache-Control", "max-age=10, must-revalidate")
	w.WriteHeader(http.StatusNotFound)
	page := newPage("Not Found")
	page.NotFound = true
	page.Write(w, req)
	return
}

func setCacheControl(w http.ResponseWriter, req *http.Request) {
	if req.Header["X-Draft"] != nil {
		w.Header().Set("Cache-Control", "max-age=1, must-revalidate")
	} else {
		now := time.UTC()
		d := time.Time{2011, 4, 11, 3, 0, 0, time.Monday, 0, "UTC"}
		ONE_WEEK := int64(604800)
		ONE_DAY := int64(86400)
		ttl := ONE_WEEK - (now.Seconds()-d.Seconds())%ONE_DAY
		w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", ttl))
	}
	w.Header().Set("Vary", "Accept-Encoding")
}

func PageHandler(w http.ResponseWriter, req *http.Request) {
	if req.URL.Path == "/" {
		setCacheControl(w, req)
		page := newPage("")
		page.Write(w, req)
		return
	}
	StaticHandler(w, req)
}

func RecipesHandler(w http.ResponseWriter, req *http.Request) {
	var r = recipe.Recipes[path.Base(req.URL.Path)]
	if r != nil {
		var p = strings.Replace(req.URL.Path, "recipes", "recipe", -1)
		w.Header().Set("Location", p)
		w.WriteHeader(http.StatusMovedPermanently)
		return
	}

	if req.URL.Path != "/recipes/" {
		NotFoundHandler(w, req)
		return
	}
	setCacheControl(w, req)
	page := newPage("Recipes")
	page.Recipes = recipe.Recipe_list
	page.Write(w, req)

}

func RecipeHandler(w http.ResponseWriter, req *http.Request) {
	var r = recipe.Recipes[path.Base(req.URL.Path)]
	if r == nil {
		NotFoundHandler(w, req)
		return
	}
	setCacheControl(w, req)
	page := newPage(r.Name + " Recipe")
	page.Recipe = r
	page.Write(w, req)
}

func CanonicalHostHandler(w http.ResponseWriter, req *http.Request) {
	var canonical = "www.eikeon.com"
	if req.Host != canonical {
		http.Redirect(w, req, "http://"+canonical+req.URL.Path, http.StatusMovedPermanently)
	} else {
		NotFoundHandler(w, req)
	}
	// TODO: set CacheControl
}

func StaticHandler(w http.ResponseWriter, req *http.Request) {
	var filename = path.Join(*StaticRoot, req.URL.Path)
	f, err := os.Open(filename)
	if err != nil {
		log.Print(err)
		NotFoundHandler(w, req)
		return
	}
	err = f.Close()

	if strings.Contains(req.URL.Path, "^") {
		w.Header().Set("Cache-Control", "max-age=3153600")
	} else {
		w.Header().Set("Cache-Control", "max-age=1, must-revalidate")
	}
	http.ServeFile(w, req, filename)
}

var Address *string
var StaticRoot *string

func main() {
	Address = flag.String("address", ":9999", "http service address")
	StaticRoot = flag.String("root", "static", "...")
	flag.Parse()

	http.Handle("eikeon.com/", http.HandlerFunc(CanonicalHostHandler))
	http.Handle("/", http.HandlerFunc(PageHandler))
	http.Handle("/recipes/", http.HandlerFunc(RecipesHandler))
	http.Handle("/recipe/", http.HandlerFunc(RecipeHandler))

	err := http.ListenAndServe(*Address, nil)
	if err != nil {
		log.Print("ListenAndServe:", err)
	}
}
<commit_msg>added redirect to canonical url<commit_after>package main

import (
	"os"
	"path"
	"flag"
	"http"
	"log"
	"strings"
	"fmt"
	"time"
	"recipe"
)

func NotFoundHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Cache-Control", "max-age=10, must-revalidate")
	w.WriteHeader(http.StatusNotFound)
	page := newPage("Not Found")
	page.NotFound = true
	page.Write(w, req)
	return
}

func setCacheControl(w http.ResponseWriter, req *http.Request) {
	if req.Header["X-Draft"] != nil {
		w.Header().Set("Cache-Control", "max-age=1, must-revalidate")
	} else {
		now := time.UTC()
		d := time.Time{2011, 4, 11, 3, 0, 0, time.Monday, 0, "UTC"}
		ONE_WEEK := int64(604800)
		ONE_DAY := int64(86400)
		ttl := ONE_WEEK - (now.Seconds()-d.Seconds())%ONE_DAY
		w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", ttl))
	}
	w.Header().Set("Vary", "Accept-Encoding")
}

func PageHandler(w http.ResponseWriter, req *http.Request) {
	if req.URL.Path == "/" {
		setCacheControl(w, req)
		page := newPage("")
		page.Write(w, req)
		return
	}
	StaticHandler(w, req)
}

func RecipesHandler(w http.ResponseWriter, req *http.Request) {
	var r = recipe.Recipes[path.Base(req.URL.Path)]
	if r != nil {
		var p = strings.Replace(req.URL.Path, "recipes", "recipe", -1)
		w.Header().Set("Location", p)
		w.WriteHeader(http.StatusMovedPermanently)
		return
	}

	if req.URL.Path != "/recipes/" {
		NotFoundHandler(w, req)
		return
	}
	setCacheControl(w, req)
	page := newPage("Recipes")
	page.Recipes = recipe.Recipe_list
	page.Write(w, req)

}

func RecipeHandler(w http.ResponseWriter, req *http.Request) {
	var r = recipe.Recipes[path.Base(req.URL.Path)]
	if r == nil {
		NotFoundHandler(w, req)
		return
	}
	if req.URL.Path[len(req.URL.Path)-1] != '/' {
		http.Redirect(w, req, req.URL.Path+"/", http.StatusMovedPermanently)
		return
	}
	setCacheControl(w, req)
	page := newPage(r.Name + " Recipe")
	page.Recipe = r
	page.Write(w, req)
}

func CanonicalHostHandler(w http.ResponseWriter, req *http.Request) {
	var canonical = "www.eikeon.com"
	if req.Host != canonical {
		http.Redirect(w, req, "http://"+canonical+req.URL.Path, http.StatusMovedPermanently)
	} else {
		NotFoundHandler(w, req)
	}
	// TODO: set CacheControl
}

func StaticHandler(w http.ResponseWriter, req *http.Request) {
	var filename = path.Join(*StaticRoot, req.URL.Path)
	f, err := os.Open(filename)
	if err != nil {
		log.Print(err)
		NotFoundHandler(w, req)
		return
	}
	err = f.Close()

	if strings.Contains(req.URL.Path, "^") {
		w.Header().Set("Cache-Control", "max-age=3153600")
	} else {
		w.Header().Set("Cache-Control", "max-age=1, must-revalidate")
	}
	http.ServeFile(w, req, filename)
}

var Address *string
var StaticRoot *string

func main() {
	Address = flag.String("address", ":9999", "http service address")
	StaticRoot = flag.String("root", "static", "...")
	flag.Parse()

	http.Handle("eikeon.com/", http.HandlerFunc(CanonicalHostHandler))
	http.Handle("/", http.HandlerFunc(PageHandler))
	http.Handle("/recipes/", http.HandlerFunc(RecipesHandler))
	http.Handle("/recipe/", http.HandlerFunc(RecipeHandler))

	err := http.ListenAndServe(*Address, nil)
	if err != nil {
		log.Print("ListenAndServe:", err)
	}
}
<|endoftext|>


<commit_before>package skiplist

import (
	"fmt"
	"math"
	"math/rand"
	"strings"
)

const (
	p            = 0.3
	defaultDepth = 2
)

type Comparable interface {
	LessThan(Comparable) bool
	Equal(Comparable) bool
}

type SkipList struct {
	length             uint
	terminus           *Node
	levelProbabilities []float32
	curCapacity        uint
	curDepth           uint
	nodes              []Node
	localRand          *rand.Rand
}

type Node struct {
	Key        Comparable
	Value      interface{}
	heightRand float32
	prev       *Node
	nexts      []*Node
	skiplist   *SkipList
}

func New(rng *rand.Rand) *SkipList {
	depth := defaultDepth

	terminus := &Node{
		heightRand: 0,
		nexts:      make([]*Node, depth),
	}
	terminus.prev = terminus
	for idx := 0; idx < len(terminus.nexts); idx++ {
		terminus.nexts[idx] = terminus
	}
	s := &SkipList{
		length:    0,
		terminus:  terminus,
		curDepth:  uint(depth),
		localRand: rng,
	}
	s.levelProbabilities = []float32{p}
	terminus.skiplist = s
	s.determineCapacity()
	s.nodes = make([]Node, s.curCapacity)

	return s
}

func (s *SkipList) determineCapacity() {
	base := float64(1.0) / p
	capacity := math.Pow(base, float64(s.curDepth))
	s.curCapacity = uint(math.Floor(capacity))
}

func (s *SkipList) chooseNumLevels() (float32, int) {
	r := s.localRand.Float32()
	max := len(s.levelProbabilities)
	for idx := 0; idx < max; idx++ {
		if r > s.levelProbabilities[idx] {
			return r, idx + 1
		}
	}
	return r, max + 1
}

func (s *SkipList) ensureCapacity() {
	if s.length < s.curCapacity {
		return
	}

	threshold := p * s.levelProbabilities[s.curDepth-2]
	s.curDepth++
	s.levelProbabilities = append(s.levelProbabilities, threshold)

	s.determineCapacity()

	// cur and next are just used to walk through the list at lvl. prev
	// records the last node that made it up to the new level.
	cur := s.terminus
	lvl := len(cur.nexts) - 1
	prev := cur
	for {
		next := cur.nexts[lvl]
		if cur.heightRand <= threshold {
			cur.nexts = append(cur.nexts, s.terminus)
			prev.nexts[lvl+1] = cur
			prev = cur
		}
		if next == s.terminus {
			break
		} else {
			cur = next
		}
	}
}

func (s *SkipList) getNode() *Node {
	if len(s.nodes) == 0 {
		s.nodes = make([]Node, s.curCapacity)
	}
	n := &s.nodes[0]
	s.nodes = s.nodes[1:]
	return n
}

func (s *SkipList) getEqOrLessThan(cur *Node, k Comparable, captureDescent bool) (*Node, []*Node) {
	if s.length == 0 {
		return s.terminus, nil
	}
	if cur != s.terminus {
		if k.Equal(cur.Key) {
			return cur, nil
		}
		if k.LessThan(cur.Key) {
			return s.getEqOrLessThan(s.terminus, k, captureDescent)
		}
	}
	// 1. Travel, not-descending, as far as possible
	lvl := len(cur.nexts) - 1
	for {
		n := cur.nexts[lvl]
		if n == s.terminus {
			break
		}
		if n.Key.LessThan(k) {
			cur = n
			lvl = len(cur.nexts) - 1
		} else if n.Key.Equal(k) {
			return n, nil
		} else {
			break
		}
	}
	// 2. Now descend as needed
	var descent []*Node
	if captureDescent {
		descent = make([]*Node, lvl+1)
		descent[lvl] = cur
	}
	for lvl--; lvl >= 0; lvl-- {
		for {
			n := cur.nexts[lvl]
			if n == s.terminus {
				break
			}
			if n.Key.LessThan(k) {
				cur = n
			} else if n.Key.Equal(k) {
				return n, descent
			} else {
				break
			}
		}
		if captureDescent {
			descent[lvl] = cur
		}
	}
	return cur, descent
}

func (s *SkipList) insert(cur *Node, k Comparable, v interface{}, n *Node) *Node {
	// do this first even though we may not need to - if we do it after
	// the getEqOrLessThan call, we may break descent.
	s.ensureCapacity()
	cur, descent := s.getEqOrLessThan(cur, k, true)
	if cur != s.terminus && cur.Key.Equal(k) {
		cur.Value = v
		return cur
	}
	// We didn't find k, so cur will be the node immediately prior to
	// where k should go.
	heightRand, height := s.chooseNumLevels()
	if n == nil {
		n = s.getNode()
	}
	n.Key = k
	n.Value = v
	n.heightRand = heightRand
	n.nexts = make([]*Node, height)
	n.prev = cur
	n.skiplist = s
	s.length++

	if len(cur.nexts) >= len(n.nexts) {
		for idx := 0; idx < len(n.nexts); idx++ {
			n.nexts[idx] = cur.nexts[idx]
			cur.nexts[idx] = n
		}
	} else {
		// Descent may capture only part of the path: it may be shorter
		// than levels (in the case where the original cur is !=
		// s.terminus) and we reached the correct location without
		// travelling up very far. However, because we didn't find k, we
		// know that all the "lower" levels of descent will be populated
		// (where "lower" is "closer to [0]"), so we just need to fill in
		// the "top".
		if len(n.nexts) > len(descent) {
			_, extra := s.getEqOrLessThan(s.terminus, descent[len(descent)-1].Key, true)
			// Aside: because we know we'll find that Key, all the lower
			// indices of extra will be nil.
			descent = append(descent, extra[len(descent):]...)
		}
		for idx := 0; idx < len(n.nexts); idx++ {
			n.nexts[idx] = descent[idx].nexts[idx]
			descent[idx].nexts[idx] = n
		}
	}
	n._next().prev = n
	return n
}

func (s *SkipList) remove(cur *Node, k Comparable) interface{} {
	n, _ := s.getEqOrLessThan(cur, k, false)
	if n == s.terminus || !n.Key.Equal(k) {
		return nil
	}
	s.removeNode(n)
	n.nullify()
	return n.Value
}

func (s *SkipList) removeNode(n *Node) {
	s.length--
	p := n.prev
	n._next().prev = p
	for idx := 0; idx < len(p.nexts) && idx < len(n.nexts); idx++ {
		p.nexts[idx] = n.nexts[idx]
	}
	if len(p.nexts) < len(n.nexts) {
		_, descent := s.getEqOrLessThan(s.terminus, p.Key, true)
		// because we know we're going to find Key, the lower indices
		// of descent will be nil. But we know p == n.prev, so all of
		// those pointers will be to n anyway, which we've already
		// dealt with in the previous loop.
		for idx := len(p.nexts); idx < len(n.nexts); idx++ {
			descent[idx].nexts[idx] = n.nexts[idx]
		}
	}
}

func (s *SkipList) reposition(cur *Node) {
	needsMove := false
	if cur != s.terminus {
		if cur.prev != s.terminus && !cur.prev.Key.LessThan(cur.Key) {
			needsMove = true
		} else if n := cur._next(); n != s.terminus && !cur.Key.LessThan(n.Key) {
			needsMove = true
		}
	}
	if needsMove {
		s.removeNode(cur)
		s.insert(cur.prev, cur.Key, cur.Value, cur)
	}
}

func (s *SkipList) First() *Node {
	return s.terminus.Next()
}

func (s *SkipList) Last() *Node {
	return s.terminus.Prev()
}

func (s *SkipList) Insert(k Comparable, v interface{}) *Node {
	return s.insert(s.terminus, k, v, nil)
}

func (s *SkipList) Get(k Comparable) *Node {
	return s.terminus.Get(k)
}

func (s *SkipList) Remove(k Comparable) interface{} {
	return s.remove(s.terminus, k)
}

func (s *SkipList) Len() uint {
	return s.length
}

// NB: this destroys t. Do not use t after this.
func (s *SkipList) Merge(t *SkipList) {
	cur := s.terminus
	for n := t.First(); n != nil; {
		m := n.Next() // need to save this out before we destroy it in the insert
		cur = s.insert(cur, n.Key, n.Value, n)
		n = m
	}
}

func (n *Node) Get(k Comparable) *Node {
	m, _ := n.skiplist.getEqOrLessThan(n, k, false)
	if m != n.skiplist.terminus && m.Key.Equal(k) {
		return m
	} else {
		return nil
	}
}

func (n *Node) Insert(k Comparable, v interface{}) *Node {
	return n.skiplist.insert(n, k, v, nil)
}

func (n *Node) Remove() interface{} {
	return n.skiplist.remove(n, n.Key)
}

func (n *Node) _next() *Node {
	return n.nexts[0]
}

func (n *Node) Next() *Node {
	if m := n.nexts[0]; m != n.skiplist.terminus {
		return m
	} else {
		return nil
	}
}

func (n *Node) Prev() *Node {
	if m := n.prev; m != n.skiplist.terminus {
		return m
	} else {
		return nil
	}
}

func (n *Node) Reposition() {
	n.skiplist.reposition(n)
}

func (n *Node) nullify() {
	// this is called when n is removed from the skiplist. It's really
	// just to ensure that if someone has a reference to n lying
	// around, they can't use it.
	n.prev = nil
	n.nexts = nil
	n.skiplist = nil
}

func (s *SkipList) String() string {
	strs := make([]string, 1, s.length+1)
	strs[0] = fmt.Sprint(s.terminus)
	for cur := s.terminus._next(); cur != s.terminus; cur = cur._next() {
		strs = append(strs, fmt.Sprint(cur))
	}
	return fmt.Sprintf("Skiplist of length %v (counted: %v), levelProbabilities %v, and nodes:\n\t[%v]",
		s.length, len(strs)-1, s.levelProbabilities, strings.Join(strs, ",\n\t "))
}

func (n *Node) String() string {
	strs := make([]string, len(n.nexts))
	for idx := 0; idx < len(strs); idx++ {
		strs[idx] = fmt.Sprint(n.nexts[idx].Key)
	}
	return fmt.Sprintf("%v -> %v (nexts: [%v])", n.Key, n.Value, strings.Join(strs, ", "))
}
<commit_msg>Minor.<commit_after>package skiplist

import (
	"fmt"
	"math"
	"math/rand"
	"strings"
)

const (
	p            = 0.3
	defaultDepth = 2
)

type Comparable interface {
	LessThan(Comparable) bool
	Equal(Comparable) bool
}

type SkipList struct {
	length             uint
	terminus           *Node
	levelProbabilities []float32
	curCapacity        uint
	curDepth           uint
	nodes              []Node
	localRand          *rand.Rand
}

type Node struct {
	Key        Comparable
	Value      interface{}
	heightRand float32
	prev       *Node
	nexts      []*Node
	skiplist   *SkipList
}

func New(rng *rand.Rand) *SkipList {
	depth := defaultDepth

	terminus := &Node{
		heightRand: 0,
		nexts:      make([]*Node, depth),
	}
	terminus.prev = terminus
	for idx := 0; idx < len(terminus.nexts); idx++ {
		terminus.nexts[idx] = terminus
	}
	s := &SkipList{
		length:    0,
		terminus:  terminus,
		curDepth:  uint(depth),
		localRand: rng,
	}
	s.levelProbabilities = []float32{p}
	terminus.skiplist = s
	s.determineCapacity()

	return s
}

func (s *SkipList) determineCapacity() {
	base := float64(1.0) / p
	capacity := math.Pow(base, float64(s.curDepth))
	s.curCapacity = uint(math.Floor(capacity))
}

func (s *SkipList) chooseNumLevels() (float32, int) {
	r := s.localRand.Float32()
	max := len(s.levelProbabilities)
	for idx := 0; idx < max; idx++ {
		if r > s.levelProbabilities[idx] {
			return r, idx + 1
		}
	}
	return r, max + 1
}

func (s *SkipList) ensureCapacity() {
	if s.length < s.curCapacity {
		return
	}

	threshold := p * s.levelProbabilities[s.curDepth-2]
	s.curDepth++
	s.levelProbabilities = append(s.levelProbabilities, threshold)

	s.determineCapacity()

	// cur and next are just used to walk through the list at lvl. prev
	// records the last node that made it up to the new level.
	cur := s.terminus
	lvl := len(cur.nexts) - 1
	prev := cur
	for {
		next := cur.nexts[lvl]
		if cur.heightRand <= threshold {
			cur.nexts = append(cur.nexts, s.terminus)
			prev.nexts[lvl+1] = cur
			prev = cur
		}
		if next == s.terminus {
			break
		} else {
			cur = next
		}
	}
}

func (s *SkipList) getNode() *Node {
	if len(s.nodes) == 0 {
		s.nodes = make([]Node, s.curCapacity)
	}
	n := &s.nodes[0]
	s.nodes = s.nodes[1:]
	return n
}

func (s *SkipList) getEqOrLessThan(cur *Node, k Comparable, captureDescent bool) (*Node, []*Node) {
	if s.length == 0 {
		return s.terminus, nil
	}
	if cur != s.terminus {
		if k.Equal(cur.Key) {
			return cur, nil
		}
		if k.LessThan(cur.Key) {
			return s.getEqOrLessThan(s.terminus, k, captureDescent)
		}
	}
	// 1. Travel, not-descending, as far as possible
	lvl := len(cur.nexts) - 1
	for {
		n := cur.nexts[lvl]
		if n == s.terminus {
			break
		}
		if n.Key.LessThan(k) {
			cur = n
			lvl = len(cur.nexts) - 1
		} else if n.Key.Equal(k) {
			return n, nil
		} else {
			break
		}
	}
	// 2. Now descend as needed
	var descent []*Node
	if captureDescent {
		descent = make([]*Node, lvl+1)
		descent[lvl] = cur
	}
	for lvl--; lvl >= 0; lvl-- {
		for {
			n := cur.nexts[lvl]
			if n == s.terminus {
				break
			}
			if n.Key.LessThan(k) {
				cur = n
			} else if n.Key.Equal(k) {
				return n, descent
			} else {
				break
			}
		}
		if captureDescent {
			descent[lvl] = cur
		}
	}
	return cur, descent
}

func (s *SkipList) insert(cur *Node, k Comparable, v interface{}, n *Node) *Node {
	// do this first even though we may not need to - if we do it after
	// the getEqOrLessThan call, we may break descent.
	s.ensureCapacity()
	cur, descent := s.getEqOrLessThan(cur, k, true)
	if cur != s.terminus && cur.Key.Equal(k) {
		cur.Value = v
		return cur
	}
	// We didn't find k, so cur will be the node immediately prior to
	// where k should go.
	heightRand, height := s.chooseNumLevels()
	if n == nil {
		n = s.getNode()
	}
	n.Key = k
	n.Value = v
	n.heightRand = heightRand
	n.nexts = make([]*Node, height)
	n.prev = cur
	n.skiplist = s
	s.length++

	if len(cur.nexts) >= len(n.nexts) {
		for idx := 0; idx < len(n.nexts); idx++ {
			n.nexts[idx] = cur.nexts[idx]
			cur.nexts[idx] = n
		}
	} else {
		// Descent may capture only part of the path: it may be shorter
		// than levels (in the case where the original cur is !=
		// s.terminus) and we reached the correct location without
		// travelling up very far. However, because we didn't find k, we
		// know that all the "lower" levels of descent will be populated
		// (where "lower" is "closer to [0]"), so we just need to fill in
		// the "top".
		if len(n.nexts) > len(descent) {
			_, extra := s.getEqOrLessThan(s.terminus, descent[len(descent)-1].Key, true)
			// Aside: because we know we'll find that Key, all the lower
			// indices of extra will be nil.
			descent = append(descent, extra[len(descent):]...)
		}
		for idx := 0; idx < len(n.nexts); idx++ {
			n.nexts[idx] = descent[idx].nexts[idx]
			descent[idx].nexts[idx] = n
		}
	}
	n._next().prev = n
	return n
}

func (s *SkipList) remove(cur *Node, k Comparable) interface{} {
	n, _ := s.getEqOrLessThan(cur, k, false)
	if n == s.terminus || !n.Key.Equal(k) {
		return nil
	}
	s.removeNode(n)
	n.nullify()
	return n.Value
}

func (s *SkipList) removeNode(n *Node) {
	s.length--
	p := n.prev
	n._next().prev = p
	for idx := 0; idx < len(p.nexts) && idx < len(n.nexts); idx++ {
		p.nexts[idx] = n.nexts[idx]
	}
	if len(p.nexts) < len(n.nexts) {
		_, descent := s.getEqOrLessThan(s.terminus, p.Key, true)
		// because we know we're going to find Key, the lower indices
		// of descent will be nil. But we know p == n.prev, so all of
		// those pointers will be to n anyway, which we've already
		// dealt with in the previous loop.
		for idx := len(p.nexts); idx < len(n.nexts); idx++ {
			descent[idx].nexts[idx] = n.nexts[idx]
		}
	}
}

func (s *SkipList) reposition(cur *Node) {
	needsMove := false
	if cur != s.terminus {
		if cur.prev != s.terminus && !cur.prev.Key.LessThan(cur.Key) {
			needsMove = true
		} else if n := cur._next(); n != s.terminus && !cur.Key.LessThan(n.Key) {
			needsMove = true
		}
	}
	if needsMove {
		s.removeNode(cur)
		s.insert(cur.prev, cur.Key, cur.Value, cur)
	}
}

func (s *SkipList) First() *Node {
	return s.terminus.Next()
}

func (s *SkipList) Last() *Node {
	return s.terminus.Prev()
}

func (s *SkipList) Insert(k Comparable, v interface{}) *Node {
	return s.insert(s.terminus, k, v, nil)
}

func (s *SkipList) Get(k Comparable) *Node {
	return s.terminus.Get(k)
}

func (s *SkipList) Remove(k Comparable) interface{} {
	return s.remove(s.terminus, k)
}

func (s *SkipList) Len() uint {
	return s.length
}

// NB: this destroys t. Do not use t after this.
func (s *SkipList) Merge(t *SkipList) {
	cur := s.terminus
	for n := t.First(); n != nil; {
		m := n.Next() // need to save this out before we destroy it in the insert
		cur = s.insert(cur, n.Key, n.Value, n)
		n = m
	}
}

func (n *Node) Get(k Comparable) *Node {
	m, _ := n.skiplist.getEqOrLessThan(n, k, false)
	if m != n.skiplist.terminus && m.Key.Equal(k) {
		return m
	} else {
		return nil
	}
}

func (n *Node) Insert(k Comparable, v interface{}) *Node {
	return n.skiplist.insert(n, k, v, nil)
}

func (n *Node) Remove() interface{} {
	return n.skiplist.remove(n, n.Key)
}

func (n *Node) _next() *Node {
	return n.nexts[0]
}

func (n *Node) Next() *Node {
	if m := n.nexts[0]; m != n.skiplist.terminus {
		return m
	} else {
		return nil
	}
}

func (n *Node) Prev() *Node {
	if m := n.prev; m != n.skiplist.terminus {
		return m
	} else {
		return nil
	}
}

func (n *Node) Reposition() {
	n.skiplist.reposition(n)
}

func (n *Node) nullify() {
	// this is called when n is removed from the skiplist. It's really
	// just to ensure that if someone has a reference to n lying
	// around, they can't use it.
	n.prev = nil
	n.nexts = nil
	n.skiplist = nil
}

func (s *SkipList) String() string {
	strs := make([]string, 1, s.length+1)
	strs[0] = fmt.Sprint(s.terminus)
	for cur := s.terminus._next(); cur != s.terminus; cur = cur._next() {
		strs = append(strs, fmt.Sprint(cur))
	}
	return fmt.Sprintf("Skiplist of length %v (counted: %v), levelProbabilities %v, and nodes:\n\t[%v]",
		s.length, len(strs)-1, s.levelProbabilities, strings.Join(strs, ",\n\t "))
}

func (n *Node) String() string {
	strs := make([]string, len(n.nexts))
	for idx := 0; idx < len(strs); idx++ {
		strs[idx] = fmt.Sprint(n.nexts[idx].Key)
	}
	return fmt.Sprintf("%v -> %v (nexts: [%v])", n.Key, n.Value, strings.Join(strs, ", "))
}
<|endoftext|>


<commit_before>// Copyright 2015/2016 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package sys

import (
	"fmt"
)

const ptrSize = 8

type Call struct {
	ID       int
	NR       int // kernel syscall number
	CallID   int
	Name     string
	CallName string
	Args     []Type
	Ret      Type
}

type Type interface {
	Name() string
	Optional() bool
	Default() uintptr
	Size() uintptr
	Align() uintptr
}

func IsPad(t Type) bool {
	if ct, ok := t.(ConstType); ok && ct.IsPad {
		return true
	}
	return false
}

type TypeCommon struct {
	TypeName   string
	IsOptional bool
}

func (t TypeCommon) Name() string {
	return t.TypeName
}

func (t TypeCommon) Optional() bool {
	return t.IsOptional
}

func (t TypeCommon) Default() uintptr {
	return 0
}

const (
	InvalidFD = ^uintptr(0)
)

type ResourceDesc struct {
	Name   string
	Type   Type
	Kind   []string
	Values []uintptr
}

type ResourceType struct {
	TypeCommon
	Desc *ResourceDesc
}

func (t ResourceType) Default() uintptr {
	return t.Desc.Values[0]
}

func (t ResourceType) SpecialValues() []uintptr {
	return t.Desc.Values
}

func (t ResourceType) Size() uintptr {
	return t.Desc.Type.Size()
}

func (t ResourceType) Align() uintptr {
	return t.Desc.Type.Align()
}

type FileoffType struct {
	TypeCommon
	TypeSize uintptr
	File     string
}

func (t FileoffType) Size() uintptr {
	return t.TypeSize
}

func (t FileoffType) Align() uintptr {
	return t.Size()
}

type BufferKind int

const (
	BufferBlob BufferKind = iota
	BufferString
	BufferSockaddr
	BufferFilesystem
	BufferAlgType
	BufferAlgName
)

type BufferType struct {
	TypeCommon
	Kind BufferKind
}

func (t BufferType) Size() uintptr {
	switch t.Kind {
	case BufferAlgType:
		return 14
	case BufferAlgName:
		return 64
	default:
		panic(fmt.Sprintf("buffer size is not statically known: %v", t.Name()))
	}
}

func (t BufferType) Align() uintptr {
	return 1
}

type VmaType struct {
	TypeCommon
}

func (t VmaType) Size() uintptr {
	return ptrSize
}

func (t VmaType) Align() uintptr {
	return t.Size()
}

type LenType struct {
	TypeCommon
	TypeSize uintptr
	ByteSize bool // want size in bytes instead of array size
	Buf      string
}

func (t LenType) Size() uintptr {
	return t.TypeSize
}

func (t LenType) Align() uintptr {
	return t.Size()
}

type FlagsType struct {
	TypeCommon
	TypeSize uintptr
	Vals     []uintptr
}

func (t FlagsType) Size() uintptr {
	return t.TypeSize
}

func (t FlagsType) Align() uintptr {
	return t.Size()
}

type ConstType struct {
	TypeCommon
	TypeSize uintptr
	Val      uintptr
	IsPad    bool
}

func (t ConstType) Size() uintptr {
	return t.TypeSize
}

func (t ConstType) Align() uintptr {
	return t.Size()
}

type StrConstType struct {
	TypeCommon
	TypeSize uintptr
	Val      string
}

func (t StrConstType) Size() uintptr {
	return ptrSize
}

func (t StrConstType) Align() uintptr {
	return t.Size()
}

type IntKind int

const (
	IntPlain IntKind = iota
	IntSignalno
	IntInaddr
	IntInport
	IntRange
)

type IntType struct {
	TypeCommon
	TypeSize   uintptr
	Kind       IntKind
	RangeBegin int64
	RangeEnd   int64
}

func (t IntType) Size() uintptr {
	return t.TypeSize
}

func (t IntType) Align() uintptr {
	return t.Size()
}

type FilenameType struct {
	TypeCommon
}

func (t FilenameType) Size() uintptr {
	panic("filename size is not statically known")
}

func (t FilenameType) Align() uintptr {
	return 1
}

type ArrayType struct {
	TypeCommon
	Type Type
	Len  uintptr // 0 if variable-length, unused for now
}

func (t ArrayType) Size() uintptr {
	if t.Len == 0 {
		return 0 // for trailing embed arrays
	}
	return t.Len * t.Type.Size()
}

func (t ArrayType) Align() uintptr {
	return t.Type.Align()
}

type PtrType struct {
	TypeCommon
	Type Type
	Dir  Dir
}

func (t PtrType) Size() uintptr {
	return ptrSize
}

func (t PtrType) Align() uintptr {
	return t.Size()
}

type StructType struct {
	TypeCommon
	Fields []Type
	padded bool
	packed bool
	align  uintptr
}

func (t *StructType) Size() uintptr {
	if !t.padded {
		panic("struct is not padded yet")
	}
	var size uintptr
	for _, f := range t.Fields {
		size += f.Size()
	}
	return size
}

func (t *StructType) Align() uintptr {
	if t.align != 0 {
		return t.align // overrided by user attribute
	}
	var align uintptr
	for _, f := range t.Fields {
		if a1 := f.Align(); align < a1 {
			align = a1
		}
	}
	return align
}

type UnionType struct {
	TypeCommon
	Options []Type
	varlen  bool
}

func (t *UnionType) Size() uintptr {
	if t.varlen {
		panic("union size is not statically known")
	}
	size := t.Options[0].Size()
	for _, opt := range t.Options {
		if size < opt.Size() {
			size = opt.Size()
		}
	}
	return size
}

func (t *UnionType) Align() uintptr {
	var align uintptr
	for _, opt := range t.Options {
		if a1 := opt.Align(); align < a1 {
			align = a1
		}
	}
	return align
}

type Dir int

const (
	DirIn Dir = iota
	DirOut
	DirInOut
)

var ctors = make(map[string][]*Call)

// ResourceConstructors returns a list of calls that can create a resource of the given kind.
func ResourceConstructors(name string) []*Call {
	return ctors[name]
}

func initResources() {
	for name, res := range Resources {
		ctors[name] = resourceCtors(res.Kind, false)
	}
}

func resourceCtors(kind []string, precise bool) []*Call {
	// Find calls that produce the necessary resources.
	var metas []*Call
	// Recurse into arguments to see if there is an out/inout arg of necessary type.
	seen := make(map[Type]bool)
	var checkArg func(typ Type, dir Dir) bool
	checkArg = func(typ Type, dir Dir) bool {
		if resarg, ok := typ.(ResourceType); ok && dir != DirIn && isCompatibleResource(kind, resarg.Desc.Kind, precise) {
			return true
		}
		switch typ1 := typ.(type) {
		case ArrayType:
			if checkArg(typ1.Type, dir) {
				return true
			}
		case *StructType:
			if seen[typ1] {
				return false // prune recursion via pointers to structs/unions
			}
			seen[typ1] = true
			for _, fld := range typ1.Fields {
				if checkArg(fld, dir) {
					return true
				}
			}
		case *UnionType:
			if seen[typ1] {
				return false // prune recursion via pointers to structs/unions
			}
			seen[typ1] = true
			for _, opt := range typ1.Options {
				if checkArg(opt, dir) {
					return true
				}
			}
		case PtrType:
			if checkArg(typ1.Type, typ1.Dir) {
				return true
			}
		}
		return false
	}
	for _, meta := range Calls {
		ok := false
		for _, arg := range meta.Args {
			if checkArg(arg, DirIn) {
				ok = true
				break
			}
		}
		if !ok && meta.Ret != nil && checkArg(meta.Ret, DirOut) {
			ok = true
		}
		if ok {
			metas = append(metas, meta)
		}
	}
	return metas
}

// IsCompatibleResource returns true if resource of kind src can be passed as an argument of kind dst.
func IsCompatibleResource(dst, src string) bool {
	dstRes := Resources[dst]
	if dstRes == nil {
		panic(fmt.Sprintf("unknown resource '%v'", dst))
	}
	srcRes := Resources[src]
	if srcRes == nil {
		panic(fmt.Sprintf("unknown resource '%v'", src))
	}
	return isCompatibleResource(dstRes.Kind, srcRes.Kind, false)
}

// isCompatibleResource returns true if resource of kind src can be passed as an argument of kind dst.
// If precise is true, then it does not allow passing a less specialized resource (e.g. fd)
// as a more specialized resource (e.g. socket). Otherwise it does.
func isCompatibleResource(dst, src []string, precise bool) bool {
	if len(dst) > len(src) {
		// dst is more specialized, e.g dst=socket, src=fd.
		if precise {
			return false
		}
		dst = dst[:len(src)]
	}
	if len(src) > len(dst) {
		// src is more specialized, e.g dst=fd, src=socket.
		src = src[:len(dst)]
	}
	for i, k := range dst {
		if k != src[i] {
			return false
		}
	}
	return true
}

func (c *Call) InputResources() []ResourceType {
	var resources []ResourceType
	seen := make(map[Type]bool)
	var checkArg func(typ Type, dir Dir)
	checkArg = func(typ Type, dir Dir) {
		switch typ1 := typ.(type) {
		case ResourceType:
			if dir != DirOut && !typ1.IsOptional {
				resources = append(resources, typ1)
			}
		case ArrayType:
			checkArg(typ1.Type, dir)
		case PtrType:
			checkArg(typ1.Type, typ1.Dir)
		case *StructType:
			if seen[typ1] {
				return // prune recursion via pointers to structs/unions
			}
			seen[typ1] = true
			for _, fld := range typ1.Fields {
				checkArg(fld, dir)
			}
		case *UnionType:
			if seen[typ1] {
				return // prune recursion via pointers to structs/unions
			}
			seen[typ1] = true
			for _, opt := range typ1.Options {
				checkArg(opt, dir)
			}
		}
	}
	for _, arg := range c.Args {
		checkArg(arg, DirIn)
	}
	return resources
}

func TransitivelyEnabledCalls(enabled map[*Call]bool) map[*Call]bool {
	supported := make(map[*Call]bool)
	for c := range enabled {
		supported[c] = true
	}
	for {
		n := len(supported)
		for c := range enabled {
			if !supported[c] {
				continue
			}
			canCreate := true
			for _, res := range c.InputResources() {
				noctors := true
				for _, ctor := range resourceCtors(res.Desc.Kind, true) {
					if supported[ctor] {
						noctors = false
						break
					}
				}
				if noctors {
					canCreate = false
					break
				}
			}
			if !canCreate {
				delete(supported, c)
			}
		}
		if n == len(supported) {
			break
		}
	}
	return supported
}

var (
	Calls     []*Call
	CallCount int
	CallMap   = make(map[string]*Call)
	CallID    = make(map[string]int)
)

func init() {
	initCalls()
	initResources()
	initAlign()

	for i, c := range Calls {
		c.ID = i
		if CallMap[c.Name] != nil {
			println(c.Name)
			panic("duplicate syscall")
		}
		id, ok := CallID[c.CallName]
		if !ok {
			id = len(CallID)
			CallID[c.CallName] = id
		}
		c.CallID = id
		CallMap[c.Name] = c
	}
	CallCount = len(CallID)
}
<commit_msg>sys: delete Size() and Align() methods for struct and union<commit_after>// Copyright 2015/2016 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package sys

import (
	"fmt"
)

const ptrSize = 8

type Call struct {
	ID       int
	NR       int // kernel syscall number
	CallID   int
	Name     string
	CallName string
	Args     []Type
	Ret      Type
}

type Type interface {
	Name() string
	Optional() bool
	Default() uintptr
	Size() uintptr
	Align() uintptr
}

func IsPad(t Type) bool {
	if ct, ok := t.(ConstType); ok && ct.IsPad {
		return true
	}
	return false
}

type TypeCommon struct {
	TypeName   string
	IsOptional bool
}

func (t TypeCommon) Name() string {
	return t.TypeName
}

func (t TypeCommon) Optional() bool {
	return t.IsOptional
}

func (t TypeCommon) Default() uintptr {
	return 0
}

const (
	InvalidFD = ^uintptr(0)
)

type ResourceDesc struct {
	Name   string
	Type   Type
	Kind   []string
	Values []uintptr
}

type ResourceType struct {
	TypeCommon
	Desc *ResourceDesc
}

func (t ResourceType) Default() uintptr {
	return t.Desc.Values[0]
}

func (t ResourceType) SpecialValues() []uintptr {
	return t.Desc.Values
}

func (t ResourceType) Size() uintptr {
	return t.Desc.Type.Size()
}

func (t ResourceType) Align() uintptr {
	return t.Desc.Type.Align()
}

type FileoffType struct {
	TypeCommon
	TypeSize uintptr
	File     string
}

func (t FileoffType) Size() uintptr {
	return t.TypeSize
}

func (t FileoffType) Align() uintptr {
	return t.Size()
}

type BufferKind int

const (
	BufferBlob BufferKind = iota
	BufferString
	BufferSockaddr
	BufferFilesystem
	BufferAlgType
	BufferAlgName
)

type BufferType struct {
	TypeCommon
	Kind BufferKind
}

func (t BufferType) Size() uintptr {
	switch t.Kind {
	case BufferAlgType:
		return 14
	case BufferAlgName:
		return 64
	default:
		panic(fmt.Sprintf("buffer size is not statically known: %v", t.Name()))
	}
}

func (t BufferType) Align() uintptr {
	return 1
}

type VmaType struct {
	TypeCommon
}

func (t VmaType) Size() uintptr {
	return ptrSize
}

func (t VmaType) Align() uintptr {
	return t.Size()
}

type LenType struct {
	TypeCommon
	TypeSize uintptr
	ByteSize bool // want size in bytes instead of array size
	Buf      string
}

func (t LenType) Size() uintptr {
	return t.TypeSize
}

func (t LenType) Align() uintptr {
	return t.Size()
}

type FlagsType struct {
	TypeCommon
	TypeSize uintptr
	Vals     []uintptr
}

func (t FlagsType) Size() uintptr {
	return t.TypeSize
}

func (t FlagsType) Align() uintptr {
	return t.Size()
}

type ConstType struct {
	TypeCommon
	TypeSize uintptr
	Val      uintptr
	IsPad    bool
}

func (t ConstType) Size() uintptr {
	return t.TypeSize
}

func (t ConstType) Align() uintptr {
	return t.Size()
}

type StrConstType struct {
	TypeCommon
	TypeSize uintptr
	Val      string
}

func (t StrConstType) Size() uintptr {
	return ptrSize
}

func (t StrConstType) Align() uintptr {
	return t.Size()
}

type IntKind int

const (
	IntPlain IntKind = iota
	IntSignalno
	IntInaddr
	IntInport
	IntRange
)

type IntType struct {
	TypeCommon
	TypeSize   uintptr
	Kind       IntKind
	RangeBegin int64
	RangeEnd   int64
}

func (t IntType) Size() uintptr {
	return t.TypeSize
}

func (t IntType) Align() uintptr {
	return t.Size()
}

type FilenameType struct {
	TypeCommon
}

func (t FilenameType) Size() uintptr {
	panic("filename size is not statically known")
}

func (t FilenameType) Align() uintptr {
	return 1
}

type ArrayType struct {
	TypeCommon
	Type Type
	Len  uintptr // 0 if variable-length, unused for now
}

func (t ArrayType) Size() uintptr {
	if t.Len == 0 {
		return 0 // for trailing embed arrays
	}
	return t.Len * t.Type.Size()
}

func (t ArrayType) Align() uintptr {
	return t.Type.Align()
}

type PtrType struct {
	TypeCommon
	Type Type
	Dir  Dir
}

func (t PtrType) Size() uintptr {
	return ptrSize
}

func (t PtrType) Align() uintptr {
	return t.Size()
}

type StructType struct {
	TypeCommon
	Fields []Type
	padded bool
	packed bool
	align  uintptr
}

func (t *StructType) Size() uintptr {
	panic("not called")
}

func (t *StructType) Align() uintptr {
	panic("not called")
}

type UnionType struct {
	TypeCommon
	Options []Type
	varlen  bool
}

func (t *UnionType) Size() uintptr {
	panic("not called")
}

func (t *UnionType) Align() uintptr {
	panic("not called")
}

type Dir int

const (
	DirIn Dir = iota
	DirOut
	DirInOut
)

var ctors = make(map[string][]*Call)

// ResourceConstructors returns a list of calls that can create a resource of the given kind.
func ResourceConstructors(name string) []*Call {
	return ctors[name]
}

func initResources() {
	for name, res := range Resources {
		ctors[name] = resourceCtors(res.Kind, false)
	}
}

func resourceCtors(kind []string, precise bool) []*Call {
	// Find calls that produce the necessary resources.
	var metas []*Call
	// Recurse into arguments to see if there is an out/inout arg of necessary type.
	seen := make(map[Type]bool)
	var checkArg func(typ Type, dir Dir) bool
	checkArg = func(typ Type, dir Dir) bool {
		if resarg, ok := typ.(ResourceType); ok && dir != DirIn && isCompatibleResource(kind, resarg.Desc.Kind, precise) {
			return true
		}
		switch typ1 := typ.(type) {
		case ArrayType:
			if checkArg(typ1.Type, dir) {
				return true
			}
		case *StructType:
			if seen[typ1] {
				return false // prune recursion via pointers to structs/unions
			}
			seen[typ1] = true
			for _, fld := range typ1.Fields {
				if checkArg(fld, dir) {
					return true
				}
			}
		case *UnionType:
			if seen[typ1] {
				return false // prune recursion via pointers to structs/unions
			}
			seen[typ1] = true
			for _, opt := range typ1.Options {
				if checkArg(opt, dir) {
					return true
				}
			}
		case PtrType:
			if checkArg(typ1.Type, typ1.Dir) {
				return true
			}
		}
		return false
	}
	for _, meta := range Calls {
		ok := false
		for _, arg := range meta.Args {
			if checkArg(arg, DirIn) {
				ok = true
				break
			}
		}
		if !ok && meta.Ret != nil && checkArg(meta.Ret, DirOut) {
			ok = true
		}
		if ok {
			metas = append(metas, meta)
		}
	}
	return metas
}

// IsCompatibleResource returns true if resource of kind src can be passed as an argument of kind dst.
func IsCompatibleResource(dst, src string) bool {
	dstRes := Resources[dst]
	if dstRes == nil {
		panic(fmt.Sprintf("unknown resource '%v'", dst))
	}
	srcRes := Resources[src]
	if srcRes == nil {
		panic(fmt.Sprintf("unknown resource '%v'", src))
	}
	return isCompatibleResource(dstRes.Kind, srcRes.Kind, false)
}

// isCompatibleResource returns true if resource of kind src can be passed as an argument of kind dst.
// If precise is true, then it does not allow passing a less specialized resource (e.g. fd)
// as a more specialized resource (e.g. socket). Otherwise it does.
func isCompatibleResource(dst, src []string, precise bool) bool {
	if len(dst) > len(src) {
		// dst is more specialized, e.g dst=socket, src=fd.
		if precise {
			return false
		}
		dst = dst[:len(src)]
	}
	if len(src) > len(dst) {
		// src is more specialized, e.g dst=fd, src=socket.
		src = src[:len(dst)]
	}
	for i, k := range dst {
		if k != src[i] {
			return false
		}
	}
	return true
}

func (c *Call) InputResources() []ResourceType {
	var resources []ResourceType
	seen := make(map[Type]bool)
	var checkArg func(typ Type, dir Dir)
	checkArg = func(typ Type, dir Dir) {
		switch typ1 := typ.(type) {
		case ResourceType:
			if dir != DirOut && !typ1.IsOptional {
				resources = append(resources, typ1)
			}
		case ArrayType:
			checkArg(typ1.Type, dir)
		case PtrType:
			checkArg(typ1.Type, typ1.Dir)
		case *StructType:
			if seen[typ1] {
				return // prune recursion via pointers to structs/unions
			}
			seen[typ1] = true
			for _, fld := range typ1.Fields {
				checkArg(fld, dir)
			}
		case *UnionType:
			if seen[typ1] {
				return // prune recursion via pointers to structs/unions
			}
			seen[typ1] = true
			for _, opt := range typ1.Options {
				checkArg(opt, dir)
			}
		}
	}
	for _, arg := range c.Args {
		checkArg(arg, DirIn)
	}
	return resources
}

func TransitivelyEnabledCalls(enabled map[*Call]bool) map[*Call]bool {
	supported := make(map[*Call]bool)
	for c := range enabled {
		supported[c] = true
	}
	for {
		n := len(supported)
		for c := range enabled {
			if !supported[c] {
				continue
			}
			canCreate := true
			for _, res := range c.InputResources() {
				noctors := true
				for _, ctor := range resourceCtors(res.Desc.Kind, true) {
					if supported[ctor] {
						noctors = false
						break
					}
				}
				if noctors {
					canCreate = false
					break
				}
			}
			if !canCreate {
				delete(supported, c)
			}
		}
		if n == len(supported) {
			break
		}
	}
	return supported
}

var (
	Calls     []*Call
	CallCount int
	CallMap   = make(map[string]*Call)
	CallID    = make(map[string]int)
)

func init() {
	initCalls()
	initResources()
	initAlign()

	for i, c := range Calls {
		c.ID = i
		if CallMap[c.Name] != nil {
			println(c.Name)
			panic("duplicate syscall")
		}
		id, ok := CallID[c.CallName]
		if !ok {
			id = len(CallID)
			CallID[c.CallName] = id
		}
		c.CallID = id
		CallMap[c.Name] = c
	}
	CallCount = len(CallID)
}
<|endoftext|>